summaryrefslogtreecommitdiffstats
path: root/drivers/char
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1997-12-06 23:51:34 +0000
committerRalf Baechle <ralf@linux-mips.org>1997-12-06 23:51:34 +0000
commit230e5ab6a084ed50470f101934782dbf54b0d06b (patch)
tree5dd821c8d33f450470588e7a543f74bf74306e9e /drivers/char
parentc9b1c8a64c6444d189856f1e26bdcb8b4cd0113a (diff)
Merge with Linux 2.1.67.
Diffstat (limited to 'drivers/char')
-rw-r--r--drivers/char/ChangeLog44
-rw-r--r--drivers/char/Config.in27
-rw-r--r--drivers/char/Makefile52
-rw-r--r--drivers/char/README.cycladesZ8
-rw-r--r--drivers/char/acquirewdt.c4
-rw-r--r--drivers/char/apm_bios.c14
-rw-r--r--drivers/char/atixlmouse.c10
-rw-r--r--drivers/char/bt848.h319
-rw-r--r--drivers/char/bttv.c2005
-rw-r--r--drivers/char/bttv.h162
-rw-r--r--drivers/char/busmouse.c8
-rw-r--r--drivers/char/cyclades.c1409
-rw-r--r--drivers/char/epca.c2
-rw-r--r--drivers/char/esp.c148
-rw-r--r--drivers/char/ftape/Config.in38
-rw-r--r--drivers/char/ftape/Makefile110
-rw-r--r--drivers/char/ftape/README.PCI4
-rw-r--r--drivers/char/ftape/RELEASE-NOTES399
-rw-r--r--drivers/char/ftape/calibr.c183
-rw-r--r--drivers/char/ftape/compressor/Makefile48
-rw-r--r--drivers/char/ftape/compressor/lzrw3.c750
-rw-r--r--drivers/char/ftape/compressor/lzrw3.h253
-rw-r--r--drivers/char/ftape/compressor/zftape-compress.c1317
-rw-r--r--drivers/char/ftape/compressor/zftape-compress.h83
-rw-r--r--drivers/char/ftape/fdc-io.c1300
-rw-r--r--drivers/char/ftape/fdc-io.h182
-rw-r--r--drivers/char/ftape/fdc-isr.c813
-rw-r--r--drivers/char/ftape/ftape-bsm.c428
-rw-r--r--drivers/char/ftape/ftape-ctl.c883
-rw-r--r--drivers/char/ftape/ftape-ctl.h94
-rw-r--r--drivers/char/ftape/ftape-eof.c554
-rw-r--r--drivers/char/ftape/ftape-io.c1050
-rw-r--r--drivers/char/ftape/ftape-io.h77
-rw-r--r--drivers/char/ftape/ftape-read.c677
-rw-r--r--drivers/char/ftape/ftape-rw.c953
-rw-r--r--drivers/char/ftape/ftape-rw.h173
-rw-r--r--drivers/char/ftape/ftape-write.c723
-rw-r--r--drivers/char/ftape/kernel-interface.c358
-rw-r--r--drivers/char/ftape/lowlevel/.cvsignore1
-rw-r--r--drivers/char/ftape/lowlevel/Makefile60
-rw-r--r--drivers/char/ftape/lowlevel/fc-10.c (renamed from drivers/char/ftape/fc-10.c)137
-rw-r--r--drivers/char/ftape/lowlevel/fc-10.h (renamed from drivers/char/ftape/fc-10.h)11
-rw-r--r--drivers/char/ftape/lowlevel/fdc-io.c1439
-rw-r--r--drivers/char/ftape/lowlevel/fdc-io.h257
-rw-r--r--drivers/char/ftape/lowlevel/fdc-isr.c1185
-rw-r--r--drivers/char/ftape/lowlevel/fdc-isr.h (renamed from drivers/char/ftape/fdc-isr.h)27
-rw-r--r--drivers/char/ftape/lowlevel/ftape-bsm.c490
-rw-r--r--drivers/char/ftape/lowlevel/ftape-bsm.h (renamed from drivers/char/ftape/ftape-bsm.h)49
-rw-r--r--drivers/char/ftape/lowlevel/ftape-buffer.c147
-rw-r--r--drivers/char/ftape/lowlevel/ftape-buffer.h32
-rw-r--r--drivers/char/ftape/lowlevel/ftape-calibr.c289
-rw-r--r--drivers/char/ftape/lowlevel/ftape-calibr.h (renamed from drivers/char/ftape/calibr.h)28
-rw-r--r--drivers/char/ftape/lowlevel/ftape-ctl.c901
-rw-r--r--drivers/char/ftape/lowlevel/ftape-ctl.h172
-rw-r--r--drivers/char/ftape/lowlevel/ftape-ecc.c (renamed from drivers/char/ftape/ecc.c)623
-rw-r--r--drivers/char/ftape/lowlevel/ftape-ecc.h (renamed from drivers/char/ftape/ecc.h)69
-rw-r--r--drivers/char/ftape/lowlevel/ftape-format.c342
-rw-r--r--drivers/char/ftape/lowlevel/ftape-format.h37
-rw-r--r--drivers/char/ftape/lowlevel/ftape-init.c188
-rw-r--r--drivers/char/ftape/lowlevel/ftape-init.h (renamed from drivers/char/ftape/kernel-interface.h)38
-rw-r--r--drivers/char/ftape/lowlevel/ftape-io.c1018
-rw-r--r--drivers/char/ftape/lowlevel/ftape-io.h94
-rw-r--r--drivers/char/ftape/lowlevel/ftape-proc.c431
-rw-r--r--drivers/char/ftape/lowlevel/ftape-proc.h37
-rw-r--r--drivers/char/ftape/lowlevel/ftape-read.c614
-rw-r--r--drivers/char/ftape/lowlevel/ftape-read.h (renamed from drivers/char/ftape/ftape-read.h)34
-rw-r--r--drivers/char/ftape/lowlevel/ftape-rw.c1091
-rw-r--r--drivers/char/ftape/lowlevel/ftape-rw.h121
-rw-r--r--drivers/char/ftape/lowlevel/ftape-setup.c105
-rw-r--r--drivers/char/ftape/lowlevel/ftape-tracing.c118
-rw-r--r--drivers/char/ftape/lowlevel/ftape-tracing.h180
-rw-r--r--drivers/char/ftape/lowlevel/ftape-write.c337
-rw-r--r--drivers/char/ftape/lowlevel/ftape-write.h (renamed from drivers/char/ftape/ftape-write.h)35
-rw-r--r--drivers/char/ftape/lowlevel/ftape_syms.c103
-rw-r--r--drivers/char/ftape/lowlevel/ftape_syms.h39
-rw-r--r--drivers/char/ftape/qic117.h280
-rw-r--r--drivers/char/ftape/tracing.c103
-rw-r--r--drivers/char/ftape/tracing.h99
-rw-r--r--drivers/char/ftape/vendors.h127
-rw-r--r--drivers/char/ftape/zftape/.cvsignore1
-rw-r--r--drivers/char/ftape/zftape/Makefile54
-rw-r--r--drivers/char/ftape/zftape/zftape-buffers.c160
-rw-r--r--drivers/char/ftape/zftape/zftape-buffers.h56
-rw-r--r--drivers/char/ftape/zftape/zftape-ctl.c1502
-rw-r--r--drivers/char/ftape/zftape/zftape-ctl.h60
-rw-r--r--drivers/char/ftape/zftape/zftape-eof.c207
-rw-r--r--drivers/char/ftape/zftape/zftape-eof.h (renamed from drivers/char/ftape/ftape-eof.h)45
-rw-r--r--drivers/char/ftape/zftape/zftape-init.c512
-rw-r--r--drivers/char/ftape/zftape/zftape-init.h85
-rw-r--r--drivers/char/ftape/zftape/zftape-read.c390
-rw-r--r--drivers/char/ftape/zftape/zftape-read.h53
-rw-r--r--drivers/char/ftape/zftape/zftape-rw.c377
-rw-r--r--drivers/char/ftape/zftape/zftape-rw.h102
-rw-r--r--drivers/char/ftape/zftape/zftape-vtbl.c758
-rw-r--r--drivers/char/ftape/zftape/zftape-vtbl.h229
-rw-r--r--drivers/char/ftape/zftape/zftape-write.c496
-rw-r--r--drivers/char/ftape/zftape/zftape-write.h38
-rw-r--r--drivers/char/ftape/zftape/zftape_syms.c60
-rw-r--r--drivers/char/ftape/zftape/zftape_syms.h39
-rw-r--r--drivers/char/istallion.c16
-rw-r--r--drivers/char/joystick.c1051
-rw-r--r--drivers/char/lp.c15
-rw-r--r--drivers/char/lp_m68k.c4
-rw-r--r--drivers/char/mem.c132
-rw-r--r--drivers/char/misc.c6
-rw-r--r--drivers/char/msbusmouse.c9
-rw-r--r--drivers/char/n_tty.c27
-rw-r--r--drivers/char/nvram.c15
-rw-r--r--drivers/char/pc110pad.c5
-rw-r--r--drivers/char/pcwd.c5
-rw-r--r--drivers/char/pcxx.c2
-rw-r--r--drivers/char/pms.c1069
-rw-r--r--drivers/char/psaux.c30
-rw-r--r--drivers/char/radio.c234
-rw-r--r--drivers/char/random.c60
-rw-r--r--drivers/char/riscom8.c2
-rw-r--r--drivers/char/rocket.c63
-rw-r--r--drivers/char/rtc.c35
-rw-r--r--drivers/char/rtrack.c213
-rw-r--r--drivers/char/rtrack.h25
-rw-r--r--drivers/char/serial.c230
-rw-r--r--drivers/char/softdog.c2
-rw-r--r--drivers/char/stallion.c2
-rw-r--r--drivers/char/tpqic02.c15
-rw-r--r--drivers/char/tty_io.c235
-rw-r--r--drivers/char/tty_ioctl.c21
-rw-r--r--drivers/char/tuner.h59
-rw-r--r--drivers/char/vc_screen.c22
-rw-r--r--drivers/char/videodev.c264
-rw-r--r--drivers/char/vt.c2
-rw-r--r--drivers/char/wdt.c6
131 files changed, 25158 insertions, 11092 deletions
diff --git a/drivers/char/ChangeLog b/drivers/char/ChangeLog
index 3083c0c54..0e61fb819 100644
--- a/drivers/char/ChangeLog
+++ b/drivers/char/ChangeLog
@@ -1,3 +1,47 @@
+Mon Nov 24 10:37:49 1997 Theodore Ts'o <tytso@rsts-11.mit.edu>
+
+ * serial.c, esp.c, rocket.c: Change drivers to take advantage of
+ tty_get_baud_rate().
+
+ * tty_io.c (tty_get_baud_rate): New function which computes the
+ correct baud rate for the tty. More factoring out of
+ common code out of the serial driver to the high-level tty
+ functions....
+
+Sat Nov 22 07:53:36 1997 Theodore Ts'o <tytso@rsts-11.mit.edu>
+
+ * serial.c, esp.c, rocket.c: Add tty->driver.break() routine, and
+ allow high-level tty code to handle the break and soft
+ carrier ioctls.
+
+ * tty_ioctl.c (n_tty_ioctl): Support TIOCGSOFTCAR and
+ TIOCSSOFTCAR, so that device drivers don't have to support
+ it.
+
+ * serial.c (autoconfig): Change 16750 test to hopefully eliminate
+ false results by people with strange 16550A's being
+ detected as 16750's. Hopefully 16750's will still be
+ detected as 16750, and other wierd UART's won't get poorly
+ autodetected. If this doesn't work, I'll have to disable
+ the auto identification for the 16750....
+
+ * tty_io.c (tty_hangup): Now do actually do the tty hangup
+ processing during the timer processing, and disable
+ interrupts while doing the hangup processing. This avoids
+ several nasty race conditions which happened when the
+ hangup processing was done asynchronously.
+ (tty_ioctl): Do break handling in the tty driver if
+ driver's break function is supported.
+ (tty_flip_buffer_push): New exported function which should
+ be used by drivers to push characters in the flip buffer
+ to the tty handler. This may either be done using a task
+ queue function for better CPU efficiency, or directly for
+ low latency operation.
+
+ * serial.c (rs_set_termios): Fix bug rs_set_termios when
+ transitioning away from B0, submitted by Stanislav
+ Voronyi.
+
Thu Jun 19 20:05:58 1997 Theodore Ts'o <tytso@rsts-11.mit.edu>
* serial.c (begin_break, end_break, rs_ioctl): Applied patch
diff --git a/drivers/char/Config.in b/drivers/char/Config.in
index eee723670..9b13a4d2d 100644
--- a/drivers/char/Config.in
+++ b/drivers/char/Config.in
@@ -76,11 +76,6 @@ if [ "$CONFIG_QIC02_TAPE" != "n" ]; then
fi
fi
-tristate 'Ftape (QIC-80/Travan) support' CONFIG_FTAPE
-if [ "$CONFIG_FTAPE" != "n" ]; then
- comment 'Set IObase/IRQ/DMA for ftape in ./drivers/char/ftape/Makefile'
-fi
-
bool 'Advanced Power Management BIOS support' CONFIG_APM
if [ "$CONFIG_APM" = "y" ]; then
bool ' Ignore USER SUSPEND' CONFIG_APM_IGNORE_USER_SUSPEND
@@ -104,9 +99,29 @@ if [ "$CONFIG_WATCHDOG" != "n" ]; then
tristate ' Acquire SBC Watchdog Timer' CONFIG_ACQUIRE_WDT
fi
bool 'Enhanced Real Time Clock Support' CONFIG_RTC
-if [ "$CONFIG_PPC" = "y" ]; then
+if [ "$CONFIG_ALPHA_BOOK1" = "y" ]; then
bool 'Tadpole ANA H8 Support' CONFIG_H8
fi
+tristate 'Video For Linux' CONFIG_VIDEO_DEV
+dep_tristate 'BT848 Video For Linux' CONFIG_VIDEO_BT848 $CONFIG_VIDEO_DEV
+#dep_tristate 'Quickcam BW Video For Linux' CONFIG_VIDEO_BWQCAM $CONFIG_VIDEO_DEV
+dep_tristate 'Mediavision Pro Movie Studio Video For Linux' CONFIG_VIDEO_PMS $CONFIG_VIDEO_DEV
tristate '/dev/nvram support' CONFIG_NVRAM
tristate 'PC joystick support' CONFIG_JOYSTICK
+bool 'Radio Device Support' CONFIG_MISC_RADIO
+if [ "$CONFIG_MISC_RADIO" != "n" ]; then
+ bool ' AIMSlab RadioTrack (aka RadioReveal) support' CONFIG_RADIO_RTRACK
+ if [ "$CONFIG_RADIO_RTRACK" != "n" ]; then
+ hex ' RadioTrack i/o port (0x20f or 0x30f)' CONFIG_RADIO_RTRACK_PORT 0x20f
+ fi
+fi
+
+mainmenu_option next_comment
+comment 'Ftape, the floppy tape device driver'
+tristate 'Ftape (QIC-80/Travan) support' CONFIG_FTAPE
+if [ "$CONFIG_FTAPE" != "n" ]; then
+ source drivers/char/ftape/Config.in
+fi
+endmenu
+
endmenu
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index e0cb4dd44..58ca9aa09 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -279,11 +279,54 @@ endif
ifeq ($(CONFIG_NVRAM),y)
M = y
-L_OBJS += nvram.o
+ ifeq ($(CONFIG_PMAC)$(CONFIG_CHRP),)
+ L_OBJS += nvram.o
+ endif
else
ifeq ($(CONFIG_NVRAM),m)
MM = m
+ ifeq ($(CONFIG_PMAC)$(CONFIG_CHRP),)
M_OBJS += nvram.o
+ endif
+ endif
+endif
+
+ifeq ($(CONFIG_VIDEO_DEV),y)
+LX_OBJS += videodev.o
+else
+ ifeq ($(CONFIG_VIDEO_DEV),m)
+ MX_OBJS += videodev.o
+ endif
+endif
+
+ifeq ($(CONFIG_VIDEO_BT848),y)
+L_OBJS += bttv.o
+else
+ ifeq ($(CONFIG_VIDEO_BT848),m)
+ M_OBJS += bttv.o
+ endif
+endif
+
+ifeq ($(CONFIG_VIDEO_BWQCAM),y)
+L_OBJS += bw-qcam.o
+else
+ ifeq ($(CONFIG_VIDEO_BWQCAM),m)
+ M_OBJS += bw-qcam.o
+ endif
+endif
+
+ifeq ($(CONFIG_VIDEO_PMS),y)
+L_OBJS += pms.o
+else
+ ifeq ($(CONFIG_VIDEO_PMS),m)
+ M_OBJS += pms.o
+ endif
+endif
+
+ifeq ($(CONFIG_MISC_RADIO),y)
+L_OBJS += radio.o
+ ifeq ($(CONFIG_RADIO_RTRACK),y)
+ L_OBJS += rtrack.o
endif
endif
@@ -296,8 +339,11 @@ else
endif
ifeq ($(CONFIG_FTAPE),y)
-SUB_DIRS += ftape
-L_OBJS += ftape/ftape.o
+L_OBJS += ftape/ftape.o
+SUB_DIRS += ftape
+ifneq ($(CONFIG_ZFTAPE),n)
+MOD_SUB_DIRS += ftape
+endif
else
ifeq ($(CONFIG_FTAPE),m)
MOD_SUB_DIRS += ftape
diff --git a/drivers/char/README.cycladesZ b/drivers/char/README.cycladesZ
new file mode 100644
index 000000000..024a69443
--- /dev/null
+++ b/drivers/char/README.cycladesZ
@@ -0,0 +1,8 @@
+
+The Cyclades-Z must have firmware loaded onto the card before it will
+operate. This operation should be performed during system startup,
+
+The firmware, loader program and the latest device driver code are
+available from Cyclades at
+ ftp://ftp.cyclades.com/pub/cyclades/cyclades-z/linux/
+
diff --git a/drivers/char/acquirewdt.c b/drivers/char/acquirewdt.c
index aeb7b72fc..8d2600f41 100644
--- a/drivers/char/acquirewdt.c
+++ b/drivers/char/acquirewdt.c
@@ -62,7 +62,7 @@ static void acq_ping(void)
inb_p(WDT_START);
}
-static long acq_write(struct inode *inode, struct file *file, const char *buf, unsigned long count)
+static ssize_t acq_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
{
if(count)
{
@@ -72,7 +72,7 @@ static long acq_write(struct inode *inode, struct file *file, const char *buf, u
return 0;
}
-static long acq_read(struct inode *inode, struct file *file, char *buf, unsigned long count)
+static ssize_t acq_read(struct file *file, char *buf, size_t count, loff_t *ppos)
{
return -EINVAL;
}
diff --git a/drivers/char/apm_bios.c b/drivers/char/apm_bios.c
index 0a722b5e6..c19db0340 100644
--- a/drivers/char/apm_bios.c
+++ b/drivers/char/apm_bios.c
@@ -63,8 +63,8 @@
#include <asm/system.h>
#include <asm/uaccess.h>
-#include <asm/poll.h>
+#include <linux/poll.h>
#include <linux/types.h>
#include <linux/stddef.h>
#include <linux/timer.h>
@@ -307,7 +307,7 @@ static void do_apm_timer(unsigned long);
static int do_open(struct inode *, struct file *);
static int do_release(struct inode *, struct file *);
-static long do_read(struct inode *, struct file *, char *, unsigned long);
+static ssize_t do_read(struct file *, char *, size_t , loff_t *);
static unsigned int do_poll(struct file *, poll_table *);
static int do_ioctl(struct inode *, struct file *, u_int, u_long);
@@ -812,8 +812,7 @@ static int check_apm_bios_struct(struct apm_bios_struct *as, const char *func)
return 0;
}
-static long do_read(struct inode *inode, struct file *fp,
- char *buf, unsigned long count)
+static ssize_t do_read(struct file *fp, char *buf, size_t count, loff_t *ppos)
{
struct apm_bios_struct * as;
int i;
@@ -831,8 +830,7 @@ static long do_read(struct inode *inode, struct file *fp,
add_wait_queue(&process_list, &wait);
repeat:
current->state = TASK_INTERRUPTIBLE;
- if (queue_empty(as)
- && !(current->signal & ~current->blocked)) {
+ if (queue_empty(as) && !signal_pending(current)) {
schedule();
goto repeat;
}
@@ -859,7 +857,7 @@ repeat:
}
if (i < count)
return count - i;
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
return -ERESTARTSYS;
return 0;
}
@@ -958,7 +956,7 @@ static int do_open(struct inode * inode, struct file * filp)
as = (struct apm_bios_struct *)kmalloc(sizeof(*as), GFP_KERNEL);
if (as == NULL) {
- printk(KERN_ER "apm_bios: cannot allocate struct of size %d bytes",
+ printk(KERN_ERR "apm_bios: cannot allocate struct of size %d bytes",
sizeof(*as));
return -ENOMEM;
}
diff --git a/drivers/char/atixlmouse.c b/drivers/char/atixlmouse.c
index 7ffee43bc..51451a0a2 100644
--- a/drivers/char/atixlmouse.c
+++ b/drivers/char/atixlmouse.c
@@ -136,16 +136,16 @@ static int open_mouse(struct inode * inode, struct file * file)
}
-static long write_mouse(struct inode * inode, struct file * file,
- const char * buffer, unsigned long count)
+static ssize_t write_mouse(struct file * file, const char * buffer,
+ size_t count, loff_t *ppos)
{
return -EINVAL;
}
-static long read_mouse(struct inode * inode, struct file * file,
- char * buffer, unsigned long count)
+static ssize_t read_mouse(struct file * file, char * buffer,
+ size_t count, loff_t *ppos)
{
- int i;
+ ssize_t i;
if (count < 3)
return -EINVAL;
diff --git a/drivers/char/bt848.h b/drivers/char/bt848.h
new file mode 100644
index 000000000..2171a1574
--- /dev/null
+++ b/drivers/char/bt848.h
@@ -0,0 +1,319 @@
+/*
+ bt848.h - Bt848 register offsets
+
+ Copyright (C) 1996,97 Ralph Metzler (rjkm@thp.uni-koeln.de)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _BT848_H_
+#define _BT848_H_
+
+#ifndef PCI_VENDOR_ID_BROOKTREE
+#define PCI_VENDOR_ID_BROOKTREE 0x109e
+#endif
+#ifndef PCI_DEVICE_ID_BT848
+#define PCI_DEVICE_ID_BT848 0x350
+#endif
+
+#define RISCMEM_LEN 131040
+
+/* Brooktree 848 registers */
+
+#define BT848_DSTATUS 0x000
+#define BT848_DSTATUS_PRES (1<<7)
+#define BT848_DSTATUS_HLOC (1<<6)
+#define BT848_DSTATUS_FIELD (1<<5)
+#define BT848_DSTATUS_NUML (1<<4)
+#define BT848_DSTATUS_CSEL (1<<3)
+#define BT848_DSTATUS_LOF (1<<1)
+#define BT848_DSTATUS_COF (1<<0)
+
+#define BT848_IFORM 0x004
+#define BT848_IFORM_HACTIVE (1<<7)
+#define BT848_IFORM_MUXSEL (3<<5)
+#define BT848_IFORM_MUX0 (2<<5)
+#define BT848_IFORM_MUX1 (3<<5)
+#define BT848_IFORM_MUX2 (1<<5)
+#define BT848_IFORM_XTSEL (3<<3)
+#define BT848_IFORM_XT0 (1<<3)
+#define BT848_IFORM_XT1 (2<<3)
+#define BT848_IFORM_XTAUTO (3<<3)
+#define BT848_IFORM_XTBOTH (3<<3)
+#define BT848_IFORM_NTSC 1
+#define BT848_IFORM_PAL_BDGHI 3
+#define BT848_IFORM_PAL_M 4
+#define BT848_IFORM_PAL_N 5
+#define BT848_IFORM_SECAM 6
+#define BT848_IFORM_AUTO 0
+#define BT848_IFORM_NORM 7
+
+#define BT848_TDEC 0x008
+#define BT848_TDEC_DEC_FIELD (1<<7)
+#define BT848_TDEC_FLDALIGN (1<<6)
+#define BT848_TDEC_DEC_RAT (0x1f)
+
+#define BT848_E_CROP 0x00C
+#define BT848_O_CROP 0x08C
+
+#define BT848_E_VDELAY_LO 0x010
+#define BT848_O_VDELAY_LO 0x090
+
+#define BT848_E_VACTIVE_LO 0x014
+#define BT848_O_VACTIVE_LO 0x094
+
+#define BT848_E_HDELAY_LO 0x018
+#define BT848_O_HDELAY_LO 0x098
+
+#define BT848_E_HACTIVE_LO 0x01C
+#define BT848_O_HACTIVE_LO 0x09C
+
+#define BT848_E_HSCALE_HI 0x020
+#define BT848_O_HSCALE_HI 0x0A0
+
+#define BT848_E_HSCALE_LO 0x024
+#define BT848_O_HSCALE_LO 0x0A4
+
+#define BT848_BRIGHT 0x028
+
+#define BT848_E_CONTROL 0x02C
+#define BT848_O_CONTROL 0x0AC
+#define BT848_CONTROL_LNOTCH (1<<7)
+#define BT848_CONTROL_COMP (1<<6)
+#define BT848_CONTROL_LDEC (1<<5)
+#define BT848_CONTROL_CBSENSE (1<<4)
+#define BT848_CONTROL_CON_MSB (1<<2)
+#define BT848_CONTROL_SAT_U_MSB (1<<1)
+#define BT848_CONTROL_SAT_V_MSB (1<<0)
+
+#define BT848_CONTRAST_LO 0x030
+#define BT848_SAT_U_LO 0x034
+#define BT848_SAT_V_LO 0x038
+#define BT848_HUE 0x03C
+
+#define BT848_E_SCLOOP 0x040
+#define BT848_O_SCLOOP 0x0C0
+#define BT848_SCLOOP_CAGC (1<<6)
+#define BT848_SCLOOP_CKILL (1<<5)
+#define BT848_SCLOOP_HFILT_AUTO (0<<3)
+#define BT848_SCLOOP_HFILT_CIF (1<<3)
+#define BT848_SCLOOP_HFILT_QCIF (2<<3)
+#define BT848_SCLOOP_HFILT_ICON (3<<3)
+
+#define BT848_OFORM 0x048
+#define BT848_OFORM_RANGE (1<<7)
+#define BT848_OFORM_CORE0 (0<<5)
+#define BT848_OFORM_CORE8 (1<<5)
+#define BT848_OFORM_CORE16 (2<<5)
+#define BT848_OFORM_CORE32 (3<<5)
+
+#define BT848_E_VSCALE_HI 0x04C
+#define BT848_O_VSCALE_HI 0x0CC
+#define BT848_VSCALE_YCOMB (1<<7)
+#define BT848_VSCALE_COMB (1<<6)
+#define BT848_VSCALE_INT (1<<5)
+#define BT848_VSCALE_HI 15
+
+#define BT848_E_VSCALE_LO 0x050
+#define BT848_O_VSCALE_LO 0x0D0
+#define BT848_TEST 0x054
+#define BT848_ADELAY 0x060
+#define BT848_BDELAY 0x064
+
+#define BT848_ADC 0x068
+#define BT848_ADC_RESERVED (2<<6)
+#define BT848_ADC_SYNC_T (1<<5)
+#define BT848_ADC_AGC_EN (1<<4)
+#define BT848_ADC_CLK_SLEEP (1<<3)
+#define BT848_ADC_Y_SLEEP (1<<2)
+#define BT848_ADC_C_SLEEP (1<<1)
+#define BT848_ADC_CRUSH (1<<0)
+
+#define BT848_E_VTC 0x06C
+#define BT848_O_VTC 0x0EC
+#define BT848_VTC_HSFMT (1<<7)
+#define BT848_VTC_VFILT_2TAP 0
+#define BT848_VTC_VFILT_3TAP 1
+#define BT848_VTC_VFILT_4TAP 2
+#define BT848_VTC_VFILT_5TAP 3
+
+#define BT848_SRESET 0x07C
+
+#define BT848_COLOR_FMT 0x0D4
+#define BT848_COLOR_FMT_O_RGB32 (0<<4)
+#define BT848_COLOR_FMT_O_RGB24 (1<<4)
+#define BT848_COLOR_FMT_O_RGB16 (2<<4)
+#define BT848_COLOR_FMT_O_RGB15 (3<<4)
+#define BT848_COLOR_FMT_O_YUY2 (4<<4)
+#define BT848_COLOR_FMT_O_BtYUV (5<<4)
+#define BT848_COLOR_FMT_O_Y8 (6<<4)
+#define BT848_COLOR_FMT_O_RGB8 (7<<4)
+#define BT848_COLOR_FMT_O_YCrCb422 (8<<4)
+#define BT848_COLOR_FMT_O_YCrCb411 (9<<4)
+#define BT848_COLOR_FMT_O_RAW (14<<4)
+#define BT848_COLOR_FMT_E_RGB32 0
+#define BT848_COLOR_FMT_E_RGB24 1
+#define BT848_COLOR_FMT_E_RGB16 2
+#define BT848_COLOR_FMT_E_RGB15 3
+#define BT848_COLOR_FMT_E_YUY2 4
+#define BT848_COLOR_FMT_E_BtYUV 5
+#define BT848_COLOR_FMT_E_Y8 6
+#define BT848_COLOR_FMT_E_RGB8 7
+#define BT848_COLOR_FMT_E_YCrCb422 8
+#define BT848_COLOR_FMT_E_YCrCb411 9
+#define BT848_COLOR_FMT_E_RAW 14
+
+#define BT848_COLOR_FMT_RGB32 0x00
+#define BT848_COLOR_FMT_RGB24 0x11
+#define BT848_COLOR_FMT_RGB16 0x22
+#define BT848_COLOR_FMT_RGB15 0x33
+#define BT848_COLOR_FMT_YUY2 0x44
+#define BT848_COLOR_FMT_BtYUV 0x55
+#define BT848_COLOR_FMT_Y8 0x66
+#define BT848_COLOR_FMT_RGB8 0x77
+#define BT848_COLOR_FMT_YCrCb422 0x88
+#define BT848_COLOR_FMT_YCrCb411 0x99
+#define BT848_COLOR_FMT_RAW 0xee
+
+#define BT848_COLOR_CTL 0x0D8
+#define BT848_COLOR_CTL_EXT_FRMRATE (1<<7)
+#define BT848_COLOR_CTL_COLOR_BARS (1<<6)
+#define BT848_COLOR_CTL_RGB_DED (1<<5)
+#define BT848_COLOR_CTL_GAMMA (1<<4)
+#define BT848_COLOR_CTL_WSWAP_ODD (1<<3)
+#define BT848_COLOR_CTL_WSWAP_EVEN (1<<2)
+#define BT848_COLOR_CTL_BSWAP_ODD (1<<1)
+#define BT848_COLOR_CTL_BSWAP_EVEN (1<<0)
+
+#define BT848_CAP_CTL 0x0DC
+#define BT848_CAP_CTL_DITH_FRAME (1<<4)
+#define BT848_CAP_CTL_CAPTURE_VBI_ODD (1<<3)
+#define BT848_CAP_CTL_CAPTURE_VBI_EVEN (1<<2)
+#define BT848_CAP_CTL_CAPTURE_ODD (1<<1)
+#define BT848_CAP_CTL_CAPTURE_EVEN (1<<0)
+
+#define BT848_VBI_PACK_SIZE 0x0E0
+
+#define BT848_VBI_PACK_DEL 0x0E4
+#define BT848_VBI_PACK_DEL_VBI_HDELAY 0xfc
+#define BT848_VBI_PACK_DEL_EXT_FRAME 2
+#define BT848_VBI_PACK_DEL_VBI_PKT_HI 1
+
+#define BT848_INT_STAT 0x100
+#define BT848_INT_MASK 0x104
+
+#define BT848_INT_ETBF (1<<23)
+
+#define BT848_INT_RISCS (0xf<<28)
+#define BT848_INT_RISC_EN (1<<27)
+#define BT848_INT_RACK (1<<25)
+#define BT848_INT_FIELD (1<<24)
+#define BT848_INT_SCERR (1<<19)
+#define BT848_INT_OCERR (1<<18)
+#define BT848_INT_PABORT (1<<17)
+#define BT848_INT_RIPERR (1<<16)
+#define BT848_INT_PPERR (1<<15)
+#define BT848_INT_FDSR (1<<14)
+#define BT848_INT_FTRGT (1<<13)
+#define BT848_INT_FBUS (1<<12)
+#define BT848_INT_RISCI (1<<11)
+#define BT848_INT_GPINT (1<<9)
+#define BT848_INT_I2CDONE (1<<8)
+#define BT848_INT_VPRES (1<<5)
+#define BT848_INT_HLOCK (1<<4)
+#define BT848_INT_OFLOW (1<<3)
+#define BT848_INT_HSYNC (1<<2)
+#define BT848_INT_VSYNC (1<<1)
+#define BT848_INT_FMTCHG (1<<0)
+
+
+#define BT848_GPIO_DMA_CTL 0x10C
+#define BT848_GPIO_DMA_CTL_GPINTC (1<<15)
+#define BT848_GPIO_DMA_CTL_GPINTI (1<<14)
+#define BT848_GPIO_DMA_CTL_GPWEC (1<<13)
+#define BT848_GPIO_DMA_CTL_GPIOMODE (3<<11)
+#define BT848_GPIO_DMA_CTL_GPCLKMODE (1<<10)
+#define BT848_GPIO_DMA_CTL_PLTP23_4 (0<<6)
+#define BT848_GPIO_DMA_CTL_PLTP23_8 (1<<6)
+#define BT848_GPIO_DMA_CTL_PLTP23_16 (2<<6)
+#define BT848_GPIO_DMA_CTL_PLTP23_32 (3<<6)
+#define BT848_GPIO_DMA_CTL_PLTP1_4 (0<<4)
+#define BT848_GPIO_DMA_CTL_PLTP1_8 (1<<4)
+#define BT848_GPIO_DMA_CTL_PLTP1_16 (2<<4)
+#define BT848_GPIO_DMA_CTL_PLTP1_32 (3<<4)
+#define BT848_GPIO_DMA_CTL_PKTP_4 (0<<2)
+#define BT848_GPIO_DMA_CTL_PKTP_8 (1<<2)
+#define BT848_GPIO_DMA_CTL_PKTP_16 (2<<2)
+#define BT848_GPIO_DMA_CTL_PKTP_32 (3<<2)
+#define BT848_GPIO_DMA_CTL_RISC_ENABLE (1<<1)
+#define BT848_GPIO_DMA_CTL_FIFO_ENABLE (1<<0)
+
+#define BT848_I2C 0x110
+#define BT848_I2C_DIV (0xf<<4)
+#define BT848_I2C_SYNC (1<<3)
+#define BT848_I2C_W3B (1<<2)
+#define BT848_I2C_SCL (1<<1)
+#define BT848_I2C_SDA (1<<0)
+
+
+#define BT848_RISC_STRT_ADD 0x114
+#define BT848_GPIO_OUT_EN 0x118
+#define BT848_GPIO_REG_INP 0x11C
+#define BT848_RISC_COUNT 0x120
+#define BT848_GPIO_DATA 0x200
+
+
+/* Bt848 RISC commands */
+
+/* only for the SYNC RISC command */
+#define BT848_FIFO_STATUS_FM1 0x06
+#define BT848_FIFO_STATUS_FM3 0x0e
+#define BT848_FIFO_STATUS_SOL 0x02
+#define BT848_FIFO_STATUS_EOL4 0x01
+#define BT848_FIFO_STATUS_EOL3 0x0d
+#define BT848_FIFO_STATUS_EOL2 0x09
+#define BT848_FIFO_STATUS_EOL1 0x05
+#define BT848_FIFO_STATUS_VRE 0x04
+#define BT848_FIFO_STATUS_VRO 0x0c
+#define BT848_FIFO_STATUS_PXV 0x00
+
+#define BT848_RISC_RESYNC (1<<15)
+
+/* WRITE and SKIP */
+/* disable which bytes of each DWORD */
+#define BT848_RISC_BYTE0 (1<<12)
+#define BT848_RISC_BYTE1 (1<<13)
+#define BT848_RISC_BYTE2 (1<<14)
+#define BT848_RISC_BYTE3 (1<<15)
+#define BT848_RISC_BYTE_ALL (0x0f<<12)
+#define BT848_RISC_BYTE_NONE 0
+/* cause RISCI */
+#define BT848_RISC_IRQ (1<<24)
+/* RISC command is last one in this line */
+#define BT848_RISC_EOL (1<<26)
+/* RISC command is first one in this line */
+#define BT848_RISC_SOL (1<<27)
+
+#define BT848_RISC_WRITE (0x01<<28)
+#define BT848_RISC_SKIP (0x02<<28)
+#define BT848_RISC_WRITEC (0x05<<28)
+#define BT848_RISC_JUMP (0x07<<28)
+#define BT848_RISC_SYNC (0x08<<28)
+
+#define BT848_RISC_WRITE123 (0x09<<28)
+#define BT848_RISC_SKIP123 (0x0a<<28)
+#define BT848_RISC_WRITE1S23 (0x0b<<28)
+
+#endif
diff --git a/drivers/char/bttv.c b/drivers/char/bttv.c
new file mode 100644
index 000000000..56d38d7a2
--- /dev/null
+++ b/drivers/char/bttv.c
@@ -0,0 +1,2005 @@
+/*
+ bttv - Bt848 frame grabber driver
+
+ Copyright (C) 1996,97 Ralph Metzler (rjkm@thp.uni-koeln.de)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ Modified to put the RISC code writer in the kernel and to fit a
+ common (and I hope safe) kernel interface. When we have an X extension
+ all will now be really sweet.
+*/
+
+#include <linux/module.h>
+#include <linux/bios32.h>
+#include <linux/config.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/pci.h>
+#include <linux/signal.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <linux/sched.h>
+#include <asm/segment.h>
+#include <linux/types.h>
+#include <linux/videodev.h>
+
+#include <linux/version.h>
+#include <asm/uaccess.h>
+
+#include "bttv.h"
+#include "tuner.h"
+
+#define DEBUG(x) /* Debug driver */
+#define IDEBUG(x) /* Debug interrupt handler */
+
+static unsigned int remap=0;
+static unsigned int vidmem=0;
+static unsigned int tuner=0; /* Default tuner */
+
+static int find_vga(void);
+static void bt848_set_risc_jmps(struct bttv *btv);
+
+/* Anybody who uses more than four? */
+#define BTTV_MAX 4
+
+static int bttv_num;
+static struct bttv bttvs[BTTV_MAX];
+
+#define I2C_TIMING (0x7<<4)
+#define I2C_COMMAND (I2C_TIMING | BT848_I2C_SCL | BT848_I2C_SDA)
+
+#define AUDIO_MUTE_DELAY 10000
+#define FREQ_CHANGE_DELAY 20000
+#define EEPROM_WRITE_DELAY 20000
+
+
+/*******************************/
+/* Memory management functions */
+/*******************************/
+
+/* convert virtual user memory address to physical address */
+/* (virt_to_phys only works for kmalloced kernel memory) */
+
+static inline ulong uvirt_to_phys(ulong adr)
+{
+ pgd_t *pgd;
+ pmd_t *pmd;
+ pte_t *ptep, pte;
+
+/* printk("adr: 0x%08x\n",adr);*/
+ pgd = pgd_offset(current->mm, adr);
+/* printk("pgd: 0x%08x\n",pgd);*/
+ if (pgd_none(*pgd))
+ return 0;
+ pmd = pmd_offset(pgd, adr);
+/* printk("pmd: 0x%08x\n",pmd); */
+ if (pmd_none(*pmd))
+ return 0;
+ ptep = pte_offset(pmd, adr&(~PGDIR_MASK));
+ pte = *ptep;
+ if(pte_present(pte))
+ return (pte_page(pte)|(adr&(PAGE_SIZE-1)));
+ return 0;
+}
+
+/* convert virtual kernel memory address to physical address */
+/* (virt_to_phys only works for kmalloced kernel memory) */
+
+static inline ulong kvirt_to_phys(ulong adr)
+{
+ return uvirt_to_phys(VMALLOC_VMADDR(adr));
+}
+
+static inline ulong kvirt_to_bus(ulong adr)
+{
+ return virt_to_bus(phys_to_virt(kvirt_to_phys(adr)));
+}
+
+
+/*****************/
+/* I2C functions */
+/*****************/
+
+static int I2CRead(struct bttv *btv, int addr)
+{
+ u32 i;
+ u32 stat;
+
+ /* clear status bit ; BT848_INT_RACK is ro */
+ btwrite(BT848_INT_I2CDONE, BT848_INT_STAT);
+
+ btwrite(((addr & 0xff) << 24) | I2C_COMMAND, BT848_I2C);
+
+ for (i=0x7fffffff; i; i--)
+ {
+ stat=btread(BT848_INT_STAT);
+ if (stat & BT848_INT_I2CDONE)
+ break;
+ }
+
+ if (!i)
+ return -1;
+ if (!(stat & BT848_INT_RACK))
+ return -2;
+
+ i=(btread(BT848_I2C)>>8)&0xff;
+ return i;
+}
+
+/* set both to write both bytes, reset it to write only b1 */
+
+static int I2CWrite(struct bttv *btv, unchar addr, unchar b1,
+ unchar b2, int both)
+{
+ u32 i;
+ u32 data;
+ u32 stat;
+
+ /* clear status bit; BT848_INT_RACK is ro */
+ btwrite(BT848_INT_I2CDONE, BT848_INT_STAT);
+
+ data=((addr & 0xff) << 24) | ((b1 & 0xff) << 16) | I2C_COMMAND;
+ if (both)
+ {
+ data|=((b2 & 0xff) << 8);
+ data|=BT848_I2C_W3B;
+ }
+
+ btwrite(data, BT848_I2C);
+
+ for (i=0x7fffffff; i; i--)
+ {
+ stat=btread(BT848_INT_STAT);
+ if (stat & BT848_INT_I2CDONE)
+ break;
+ }
+
+ if (!i)
+ return -1;
+ if (!(stat & BT848_INT_RACK))
+ return -2;
+
+ return 0;
+}
+
+static void readee(struct bttv *btv, unchar *eedata)
+{
+ int i, k;
+
+ if (I2CWrite(btv, 0xa0, 0, -1, 0)<0)
+ {
+ printk(KERN_WARNING "bttv: readee error\n");
+ return;
+ }
+
+ for (i=0; i<256; i++)
+ {
+ k=I2CRead(btv, 0xa1);
+ if (k<0)
+ {
+ printk(KERN_WARNING "bttv: readee error\n");
+ break;
+ }
+ eedata[i]=k;
+ }
+}
+
+static void writeee(struct bttv *btv, unchar *eedata)
+{
+ int i;
+
+ for (i=0; i<256; i++)
+ {
+ if (I2CWrite(btv, 0xa0, i, eedata[i], 1)<0)
+ {
+ printk(KERN_WARNING "bttv: writeee error (%d)\n", i);
+ break;
+ }
+ udelay(EEPROM_WRITE_DELAY);
+ }
+}
+
+/*
+ * Tuner, internal, external and mute
+ */
+
+static unchar audiomuxs[][4] =
+{
+ { 0x00, 0x00, 0x00, 0x00}, /* unknown */
+ { 0x02, 0x00, 0x00, 0x0a}, /* MIRO */
+ { 0x00, 0x02, 0x03, 0x04}, /* Hauppauge */
+ { 0x04, 0x02, 0x03, 0x01}, /* STB */
+ { 0x01, 0x02, 0x03, 0x04}, /* Intel??? */
+ { 0x01, 0x02, 0x03, 0x04}, /* Diamond DTV2000??? */
+};
+
+static void audio(struct bttv *btv, int mode)
+{
+ btwrite(0x0f, BT848_GPIO_OUT_EN);
+ btwrite(0x00, BT848_GPIO_REG_INP);
+
+ switch (mode)
+ {
+ case AUDIO_UNMUTE:
+ btv->audio&=~AUDIO_MUTE;
+ mode=btv->audio;
+ break;
+ case AUDIO_OFF:
+ mode=AUDIO_OFF;
+ break;
+ case AUDIO_ON:
+ mode=btv->audio;
+ break;
+ default:
+ btv->audio&=AUDIO_MUTE;
+ btv->audio|=mode;
+ break;
+ }
+ if ((btv->audio&AUDIO_MUTE) || !(btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC))
+ mode=AUDIO_OFF;
+ btaor(audiomuxs[btv->type][mode] , ~0x0f, BT848_GPIO_DATA);
+}
+
+
+extern inline void bt848_dma(struct bttv *btv, uint state)
+{
+ if (state)
+ btor(3, BT848_GPIO_DMA_CTL);
+ else
+ btand(~3, BT848_GPIO_DMA_CTL);
+}
+
+
+static void bt848_cap(struct bttv *btv, uint state)
+{
+ if (state)
+ {
+ btv->cap|=3;
+ bt848_set_risc_jmps(btv);
+ }
+ else
+ {
+ btv->cap&=~3;
+ bt848_set_risc_jmps(btv);
+ }
+}
+
+static void bt848_muxsel(struct bttv *btv, uint input)
+{
+ input&=3;
+
+ /* This seems to get rid of some synchronization problems */
+ btand(~(3<<5), BT848_IFORM);
+ udelay(10000);
+
+ if (input==3)
+ {
+ btor(BT848_CONTROL_COMP, BT848_E_CONTROL);
+ btor(BT848_CONTROL_COMP, BT848_O_CONTROL);
+ }
+ else
+ {
+ btand(~BT848_CONTROL_COMP, BT848_E_CONTROL);
+ btand(~BT848_CONTROL_COMP, BT848_O_CONTROL);
+ }
+ if (input==2)
+ input=3;
+ btaor(((input+2)&3)<<5, ~(3<<5), BT848_IFORM);
+ audio(btv, input ? AUDIO_EXTERN : AUDIO_TUNER);
+}
+
+
+#define VBIBUF_SIZE 65536
+
+static void make_vbitab(struct bttv *btv)
+{
+ int i;
+ dword *po=(dword *) btv->vbi_odd;
+ dword *pe=(dword *) btv->vbi_even;
+
+ DEBUG(printk(KERN_DEBUG "vbiodd: 0x%08x\n",(int)btv->vbi_odd));
+ DEBUG(printk(KERN_DEBUG "vbievn: 0x%08x\n",(int)btv->vbi_even));
+ DEBUG(printk(KERN_DEBUG "po: 0x%08x\n",(int)po));
+ DEBUG(printk(KERN_DEBUG "pe: 0x%08x\n",(int)pe));
+
+ *(po++)=BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1; *(po++)=0;
+ for (i=0; i<16; i++)
+ {
+ *(po++)=BT848_RISC_WRITE|2044|BT848_RISC_EOL|BT848_RISC_SOL|(13<<20);
+ *(po++)=kvirt_to_bus((ulong)btv->vbibuf+i*2048);
+ }
+ *(po++)=BT848_RISC_JUMP;
+ *(po++)=virt_to_bus(btv->risc_jmp+4);
+
+ *(pe++)=BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1; *(pe++)=0;
+ for (i=16; i<32; i++)
+ {
+ *(pe++)=BT848_RISC_WRITE|2044|BT848_RISC_EOL|BT848_RISC_SOL;
+ *(pe++)=kvirt_to_bus((ulong)btv->vbibuf+i*2048);
+ }
+ *(pe++)=BT848_RISC_JUMP|BT848_RISC_IRQ|(0x01<<16);
+ *(pe++)=virt_to_bus(btv->risc_jmp+10);
+}
+
+/*
+ * Set the registers for the size we have specified. Don't bother
+ * trying to understand this without the BT848 manual in front of
+ * you [AC].
+ *
+ * PS: The manual is free for download in .pdf format from
+ * www.brooktree.com - nicely done those folks.
+ */
+
+static void bt848_set_size(struct bttv *btv)
+{
+ u16 vscale, hscale;
+ u32 xsf, sr;
+ u16 hdelay, vdelay;
+ u16 hactive, vactive;
+ u16 inter;
+ u8 crop;
+
+ /*
+ * No window , no try...
+ */
+
+ if (!btv->win.width)
+ return;
+ if (!btv->win.height)
+ return;
+
+ inter=(btv->win.interlace&1)^1;
+
+ switch (btv->win.bpp)
+ {
+ /*
+ * RGB8 seems to be a 9x5x5 GRB color cube starting at color 16
+ * Why the h... can't they even mention this in the datasheet???
+ */
+ case 1:
+ btwrite(BT848_COLOR_FMT_RGB8, BT848_COLOR_FMT);
+ btand(~0x10, BT848_CAP_CTL); // Dithering looks much better in this mode
+ break;
+ case 2:
+ btwrite(BT848_COLOR_FMT_RGB16, BT848_COLOR_FMT);
+ btor(0x10, BT848_CAP_CTL);
+ break;
+ case 3:
+ btwrite(BT848_COLOR_FMT_RGB24, BT848_COLOR_FMT);
+ btor(0x10, BT848_CAP_CTL);
+ break;
+ case 4:
+ btwrite(BT848_COLOR_FMT_RGB32, BT848_COLOR_FMT);
+ btor(0x10, BT848_CAP_CTL);
+ break;
+ }
+
+ /*
+ * Set things up according to the final picture width.
+ */
+
+ hactive=btv->win.width;
+ if (hactive < 193)
+ {
+ btwrite (2, BT848_E_VTC);
+ btwrite (2, BT848_O_VTC);
+ }
+ else if (hactive < 385)
+ {
+ btwrite (1, BT848_E_VTC);
+ btwrite (1, BT848_O_VTC);
+ }
+ else
+ {
+ btwrite (0, BT848_E_VTC);
+ btwrite (0, BT848_O_VTC);
+ }
+
+ /*
+ * Ok are we doing Never The Same Color or PAL ?
+ */
+
+ if (btv->win.norm==1)
+ {
+ btv->win.cropwidth=640;
+ btv->win.cropheight=480;
+ btwrite(0x68, BT848_ADELAY);
+ btwrite(0x5d, BT848_BDELAY);
+ btaor(BT848_IFORM_NTSC, ~7, BT848_IFORM);
+ btaor(BT848_IFORM_XT0, ~0x18, BT848_IFORM);
+ xsf = (btv->win.width*365625UL)/300000UL;
+ hscale = ((910UL*4096UL)/xsf-4096);
+ vdelay=btv->win.cropy+0x16;
+ hdelay=((hactive*135)/754+btv->win.cropx)&0x3fe;
+ }
+ else
+ {
+ btv->win.cropwidth=768;
+ btv->win.cropheight=576;
+ if (btv->win.norm==0)
+ {
+ btwrite(0x7f, BT848_ADELAY);
+ btwrite(0x72, BT848_BDELAY);
+ btaor(BT848_IFORM_PAL_BDGHI, ~BT848_IFORM_NORM, BT848_IFORM);
+ }
+ else
+ {
+ btwrite(0x7f, BT848_ADELAY);
+ btwrite(0x00, BT848_BDELAY);
+ btaor(BT848_IFORM_SECAM, ~BT848_IFORM_NORM, BT848_IFORM);
+ }
+ btaor(BT848_IFORM_XT1, ~0x18, BT848_IFORM);
+ xsf = (btv->win.width*36875UL)/30000UL;
+ hscale = ((1135UL*4096UL)/xsf-4096);
+ vdelay=btv->win.cropy+0x20;
+ hdelay=((hactive*186)/922+btv->win.cropx)&0x3fe;
+ }
+ sr=((btv->win.cropheight>>inter)*512)/btv->win.height-512;
+ vscale=(0x10000UL-sr)&0x1fff;
+ vactive=btv->win.cropheight;
+
+#if 0
+ printk("bttv: hscale=0x%04x, ",hscale);
+ printk("bttv: vscale=0x%04x\n",vscale);
+
+ printk("bttv: hdelay =0x%04x\n",hdelay);
+ printk("bttv: hactive=0x%04x\n",hactive);
+ printk("bttv: vdelay =0x%04x\n",vdelay);
+ printk("bttv: vactive=0x%04x\n",vactive);
+#endif
+
+ /*
+ * Interlace is set elsewhere according to the final image
+ * size we desire
+ */
+
+ if (btv->win.interlace)
+ {
+ btor(BT848_VSCALE_INT, BT848_E_VSCALE_HI);
+ btor(BT848_VSCALE_INT, BT848_O_VSCALE_HI);
+ }
+ else
+ {
+ btand(~BT848_VSCALE_INT, BT848_E_VSCALE_HI);
+ btand(~BT848_VSCALE_INT, BT848_O_VSCALE_HI);
+ }
+
+ /*
+ * Load her up
+ */
+
+ btwrite(hscale>>8, BT848_E_HSCALE_HI);
+ btwrite(hscale>>8, BT848_O_HSCALE_HI);
+ btwrite(hscale&0xff, BT848_E_HSCALE_LO);
+ btwrite(hscale&0xff, BT848_O_HSCALE_LO);
+
+ btwrite((vscale>>8)|(btread(BT848_E_VSCALE_HI)&0xe0), BT848_E_VSCALE_HI);
+ btwrite((vscale>>8)|(btread(BT848_O_VSCALE_HI)&0xe0), BT848_O_VSCALE_HI);
+ btwrite(vscale&0xff, BT848_E_VSCALE_LO);
+ btwrite(vscale&0xff, BT848_O_VSCALE_LO);
+
+ btwrite(hactive&0xff, BT848_E_HACTIVE_LO);
+ btwrite(hactive&0xff, BT848_O_HACTIVE_LO);
+ btwrite(hdelay&0xff, BT848_E_HDELAY_LO);
+ btwrite(hdelay&0xff, BT848_O_HDELAY_LO);
+
+ btwrite(vactive&0xff, BT848_E_VACTIVE_LO);
+ btwrite(vactive&0xff, BT848_O_VACTIVE_LO);
+ btwrite(vdelay&0xff, BT848_E_VDELAY_LO);
+ btwrite(vdelay&0xff, BT848_O_VDELAY_LO);
+
+ crop=((hactive>>8)&0x03)|((hdelay>>6)&0x0c)|
+ ((vactive>>4)&0x30)|((vdelay>>2)&0xc0);
+ btwrite(crop, BT848_E_CROP);
+ btwrite(crop, BT848_O_CROP);
+}
+
+
+/*
+ * The floats in the tuner struct are computed at compile time
+ * by gcc and cast back to integers. Thus we don't violate the
+ * "no float in kernel" rule.
+ */
+
+static struct tunertype tuners[] = {
+ {"Temic PAL", TEMIC, PAL,
+ 16*140.25,16*463.25,0x02,0x04,0x01,0x8e,0xc2},
+ {"Philips PAL_I", Philips, PAL_I,
+ 16*140.25,16*463.25,0x00,0x00,0x00,0x00,0x00},
+ {"Philips NTSC", Philips, NTSC,
+ 16*157.25,16*451.25,0xA0,0x90,0x30,0x8e,0xc0},
+ {"Philips SECAM", Philips, SECAM,
+ 16*168.25,16*447.25,0xA3,0x93,0x33,0x8e,0xc0},
+ {"NoTuner", NoTuner, NOTUNER,
+ 0 ,0 ,0x00,0x00,0x00,0x00,0x00},
+ {"Philips PAL", Philips, PAL,
+ 16*168.25,16*447.25,0xA0,0x90,0x30,0x8e,0xc0},
+ {"Temic NTSC", TEMIC, NTSC,
+ 16*157.25,16*463.25,0x02,0x04,0x01,0x8e,0xc2},
+ {"TEMIC PAL_I", TEMIC, PAL_I,
+ 0 ,0 ,0x00,0x00,0x00,0x00,0xc2},
+};
+
+/*
+ * Set TSA5522 synthesizer frequency in 1/16 Mhz steps
+ */
+
+static void set_freq(struct bttv *btv, ushort freq)
+{
+ u8 config;
+ u16 div;
+ struct tunertype *tun=&tuners[btv->tuner];
+ int oldAudio = btv->audio;
+
+ audio(btv, AUDIO_MUTE);
+ udelay(AUDIO_MUTE_DELAY);
+ if (freq < tun->thresh1)
+ config = tun->VHF_L;
+ else if (freq < tun->thresh2)
+ config = tun->VHF_H;
+ else
+ config = tun->UHF;
+
+ div=freq+623; /* div=((freq+16*38.9));*/
+
+ div&=0x7fff;
+ if (I2CWrite(btv, btv->tuneradr, (div>>8)&0x7f, div&0xff, 1)<0)
+ return;
+ I2CWrite(btv, btv->tuneradr, tun->config, config, 1);
+ if (!(oldAudio & AUDIO_MUTE))
+ {
+ udelay(FREQ_CHANGE_DELAY);
+ audio(btv, AUDIO_UNMUTE);
+ }
+}
+
+static long bttv_write(struct video_device *v, const char *buf, unsigned long count, int nonblock)
+{
+ return -EINVAL;
+}
+
+static long bttv_read(struct video_device *v, char *buf, unsigned long count, int nonblock)
+{
+ struct bttv *btv= (struct bttv *)v;
+ int q,todo;
+
+ todo=count;
+ while (todo && todo>(q=VBIBUF_SIZE-btv->vbip))
+ {
+ if(copy_to_user((void *) buf, (void *) btv->vbibuf+btv->vbip, q))
+ return -EFAULT;
+ todo-=q;
+ buf+=q;
+
+/* btv->vbip=0; */
+ cli();
+ if (todo && q==VBIBUF_SIZE-btv->vbip)
+ {
+ if(nonblock)
+ {
+ sti();
+ if(count==todo)
+ return -EWOULDBLOCK;
+ return count-todo;
+ }
+ interruptible_sleep_on(&btv->vbiq);
+ sti();
+ if(current->signal & ~current->blocked)
+ {
+ if(todo==count)
+ return -EINTR;
+ else
+ return count-todo;
+ }
+ }
+ }
+ if (todo)
+ {
+ if(copy_to_user((void *) buf, (void *) btv->vbibuf+btv->vbip, todo))
+ return -EFAULT;
+ btv->vbip+=todo;
+ }
+ return count;
+}
+
+/*
+ * Open a bttv card. Right now the flags stuff is just playing
+ */
+
+static int bttv_open(struct video_device *dev, int flags)
+{
+ struct bttv *btv = (struct bttv *)dev;
+ int users, i;
+
+ switch (flags)
+ {
+ case 0:
+ if (btv->user)
+ return -EBUSY;
+ btv->user++;
+ audio(btv, AUDIO_UNMUTE);
+ for (i=users=0; i<bttv_num; i++)
+ users+=bttvs[i].user;
+ if (users==1)
+ find_vga();
+ break;
+ case 1:
+ break;
+ case 2:
+ btv->vbip=VBIBUF_SIZE;
+ btv->cap|=0x0c;
+ bt848_set_risc_jmps(btv);
+ break;
+ }
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static void bttv_close(struct video_device *dev)
+{
+ struct bttv *btv=(struct bttv *)dev;
+
+ btv->user--;
+ audio(btv, AUDIO_MUTE);
+ btv->cap&=~3;
+#if 0 /* FIXME */
+ if(minor&0x20)
+ {
+ btv->cap&=~0x0c;
+ }
+#endif
+ bt848_set_risc_jmps(btv);
+
+ MOD_DEC_USE_COUNT;
+}
+
+/***********************************/
+/* ioctls and supporting functions */
+/***********************************/
+
+extern inline void bt848_bright(struct bttv *btv, uint bright)
+{
+ btwrite(bright&0xff, BT848_BRIGHT);
+}
+
+extern inline void bt848_hue(struct bttv *btv, uint hue)
+{
+ btwrite(hue&0xff, BT848_HUE);
+}
+
+extern inline void bt848_contrast(struct bttv *btv, uint cont)
+{
+ unsigned int conthi;
+
+ conthi=(cont>>6)&4;
+ btwrite(cont&0xff, BT848_CONTRAST_LO);
+ btaor(conthi, ~4, BT848_E_CONTROL);
+ btaor(conthi, ~4, BT848_O_CONTROL);
+}
+
+extern inline void bt848_sat_u(struct bttv *btv, ulong data)
+{
+ u32 datahi;
+
+ datahi=(data>>7)&2;
+ btwrite(data&0xff, BT848_SAT_U_LO);
+ btaor(datahi, ~2, BT848_E_CONTROL);
+ btaor(datahi, ~2, BT848_O_CONTROL);
+}
+
+static inline void bt848_sat_v(struct bttv *btv, ulong data)
+{
+ u32 datahi;
+
+ datahi=(data>>8)&1;
+ btwrite(data&0xff, BT848_SAT_V_LO);
+ btaor(datahi, ~1, BT848_E_CONTROL);
+ btaor(datahi, ~1, BT848_O_CONTROL);
+}
+
+/*
+ * Cliprect -> risc table.
+ *
+ * FIXME: This is generating wrong code when we have some kinds of
+ * rectangle lists. I don't currently understand why.
+ */
+
+static void write_risc_data(struct bttv *btv, struct video_clip *vp, int count)
+{
+ int i;
+ u32 yy, y, x, dx, ox;
+ u32 *rmem, *rmem2;
+ struct video_clip first, *cur, *cur2, *nx, first2, *prev, *nx2;
+ u32 *rp, rpo=0, rpe=0, p, bpsl;
+ u32 *rpp;
+ u32 mask;
+ int interlace;
+ int depth;
+
+ rmem=(u32 *)btv->risc_odd;
+ rmem2=(u32 *)btv->risc_even;
+ depth=btv->win.bpp;
+
+ /* create y-sorted list */
+
+ first.next=NULL;
+ for (i=0; i<count; i++)
+ {
+ cur=&first;
+ while ((nx=cur->next) && (vp[i].y > cur->next->y))
+ cur=nx;
+ cur->next=&(vp[i]);
+ vp[i].next=nx;
+ }
+ first2.next=NULL;
+
+ rmem[rpo++]=BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1; rmem[rpo++]=0;
+
+ rmem2[rpe++]=BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1; rmem2[rpe++]=0;
+
+
+ /*
+ * 32bit depth frame buffers need extra flags setting
+ */
+
+ if (depth==4)
+ mask=BT848_RISC_BYTE3;
+ else
+ mask=0;
+
+ bpsl=btv->win.width*btv->win.bpp;
+ p=btv->win.vidadr+btv->win.x*btv->win.bpp+
+ btv->win.y*btv->win.bpl;
+
+ interlace=btv->win.interlace;
+
+ /*
+ * Loop through all lines
+ */
+
+ for (yy=0; yy<(btv->win.height<<(1^interlace)); yy++)
+ {
+ y=yy>>(1^interlace);
+
+ /*
+ * Even or odd frame generation. We have to
+ * write the RISC instructions to the right stream.
+ */
+
+ if(!(y&1))
+ {
+ rp=&rpo;
+ rpp=rmem;
+ }
+ else
+ {
+ rp=&rpe;
+ rpp=rmem2;
+ }
+
+
+ /*
+ * first2 is the header of a list of "active" rectangles. We add
+ * rectangles as we hit their top and remove them as they fall off
+ * the bottom
+ */
+
+ /* remove rects with y2 > y */
+ if ((cur=first2.next))
+ {
+ prev=&first2;
+ do
+ {
+ if (cur->y+cur->height < y)
+ prev->next=cur->next;
+ else
+ prev=cur;
+ }
+ while ((cur=cur->next));
+ }
+
+ /* add rect to second (x-sorted) list if rect.y == y */
+ if ((cur=first.next))
+ {
+ while ((cur) && (cur->y == y))
+ {
+ first.next=cur->next;
+ cur2=&first2;
+ while ((nx2=cur2->next) && (cur->x > cur2->next->x))
+ cur2=nx2;
+ cur2->next=cur;
+ cur->next=nx2;
+ cur=first.next;
+ }
+ }
+
+
+ /*
+ * Begin writing the RISC script
+ */
+
+ dx=x=0;
+
+ /*
+ * Starting at x position 0 on a new scan line
+ * write to location p, don't yet write the number
+ * of pixels for the instruction
+ */
+
+ rpp[(*rp)++]=BT848_RISC_WRITE|BT848_RISC_SOL;
+ rpp[(*rp)++]=p;
+
+ /*
+ * For each rectangle we have in the "active" list - sorted left to
+ * right..
+ */
+
+ for (cur2=first2.next; cur2; cur2=cur2->next)
+ {
+ /*
+ * If we are to the left of the first drawing area
+ */
+
+ if (x+dx < cur2->x)
+ {
+ /* Bytes pending ? */
+ if (dx)
+ {
+ /* For a delta away from the start we need to write a SKIP */
+ if (x)
+ rpp[(*rp)++]=BT848_RISC_SKIP|(dx*depth);
+ else
+ /* Rewrite the start of line WRITE to a SKIP */
+ rpp[(*rp)-2]|=BT848_RISC_BYTE_ALL|(dx*depth);
+ /* Move X to the next point (drawing start) */
+ x=x+dx;
+ }
+ /* Ok how far are we from the start of the next rectangle ? */
+ dx=cur2->x-x;
+ /* dx is now the size of data to write */
+
+ /* If this isnt the left edge generate a "write continue" */
+ if (x)
+ rpp[(*rp)++]=BT848_RISC_WRITEC|(dx*depth)|mask;
+ else
+ /* Fill in the byte count on the initial WRITE */
+ rpp[(*rp)-2]|=(dx*depth)|mask;
+ /* Move to the start of the rectangle */
+ x=cur2->x;
+ /* x is our left dx is byte size of hole */
+ dx=cur2->width+1;
+ }
+ else
+ /* Already in a clip zone.. set dx */
+ if (x+dx < cur2->x+cur2->width)
+ dx=cur2->x+cur2->width-x+1;
+ }
+ /* now treat the rest of the line */
+ ox=x;
+ if (dx)
+ {
+ /* Complete the SKIP to eat to the end of the gap */
+ if (x)
+ rpp[(*rp)++]=BT848_RISC_SKIP|(dx*depth);
+ else
+ /* Rewrite to SKIP start to this point */
+ rpp[(*rp)-2]|=BT848_RISC_BYTE_ALL|(dx*depth);
+ x=x+dx;
+ }
+
+ /*
+ * Not at the right hand edge ?
+ */
+
+ if ((dx=btv->win.width-x)!=0)
+ {
+ /* Write to edge of display */
+ if (x)
+ rpp[(*rp)++]=BT848_RISC_WRITEC|(dx*depth)|BT848_RISC_EOL|mask;
+ else
+ /* Entire frame is a write - patch first order */
+ rpp[(*rp)-2]|=(dx*depth)|BT848_RISC_EOL|mask;
+ }
+ else
+ {
+ /* End of line if needed */
+ if (ox)
+ rpp[(*rp)-1]|=BT848_RISC_EOL|mask;
+ else
+ {
+ /* Skip the line : write a SKIP + start/end of line marks */
+ (*rp)--;
+ rpp[(*rp)-1]=BT848_RISC_SKIP|(btv->win.width*depth)|
+ BT848_RISC_EOL|BT848_RISC_SOL;
+ }
+ }
+ /*
+ * Move the video render pointer on a line
+ */
+ if (interlace||(y&1))
+ p+=btv->win.bpl;
+ }
+
+ /*
+ * Attach the interframe jumps
+ */
+
+ rmem[rpo++]=BT848_RISC_JUMP;
+ rmem[rpo++]=btv->bus_vbi_even;
+
+ rmem2[rpe++]=BT848_RISC_JUMP;
+ rmem2[rpe++]=btv->bus_vbi_odd;
+}
+
+/*
+ * Helper for adding clips.
+ */
+
+static void new_risc_clip(struct video_window *vw, struct video_clip *vcp, int x, int y, int w, int h)
+{
+ vcp[vw->clipcount].x=x;
+ vcp[vw->clipcount].y=y;
+ vcp[vw->clipcount].width=w;
+ vcp[vw->clipcount].height=h;
+ vw->clipcount++;
+}
+
+/*
+ * ioctl routine
+ */
+
+static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
+{
+ unsigned char eedata[256];
+/* unsigned long data;*/
+/* static ushort creg;*/
+ struct bttv *btv=(struct bttv *)dev;
+ static int lastchan=0;
+
+ switch (cmd)
+ {
+ case VIDIOCGCAP:
+ {
+ struct video_capability b;
+ strcpy(b.name,btv->video_dev.name);
+ b.type = VID_TYPE_CAPTURE|
+ VID_TYPE_TUNER|
+ VID_TYPE_TELETEXT|
+ VID_TYPE_OVERLAY|
+ VID_TYPE_CLIPPING|
+ VID_TYPE_FRAMERAM|
+ VID_TYPE_SCALES;
+ b.channels = 4; /* tv , input, svhs */
+ b.audios = 4; /* tv, input, svhs */
+ b.maxwidth = 768;
+ b.maxheight = 576;
+ b.minwidth = 32;
+ b.minheight = 32;
+ if(copy_to_user(arg,&b,sizeof(b)))
+ return -EFAULT;
+ return 0;
+ }
+ case VIDIOCGCHAN:
+ {
+ struct video_channel v;
+ if(copy_from_user(&v, arg,sizeof(v)))
+ return -EFAULT;
+ v.flags=VIDEO_VC_AUDIO;
+ v.tuners=0;
+ v.type=VIDEO_TYPE_CAMERA;
+ switch(v.channel)
+ {
+ case 0:
+ strcpy(v.name,"Television");
+ v.flags|=VIDEO_VC_TUNER;
+ v.type=VIDEO_TYPE_TV;
+ v.tuners=1;
+ break;
+ case 1:
+ strcpy(v.name,"Composite1");
+ break;
+ case 2:
+ strcpy(v.name,"Composite2");
+ break;
+ case 3:
+ strcpy(v.name,"SVHS");
+ break;
+ default:
+ return -EINVAL;
+ }
+ if(copy_to_user(arg,&v,sizeof(v)))
+ return -EFAULT;
+ return 0;
+ }
+ /*
+ * Each channel has 1 tuner
+ */
+ case VIDIOCSCHAN:
+ {
+ int v;
+ if(copy_from_user(&v, arg, sizeof(v)))
+ return -EFAULT;
+ bt848_muxsel(btv, v);
+ lastchan=v;
+ return 0;
+ }
+ case VIDIOCGTUNER:
+ {
+ struct video_tuner v;
+ if(copy_from_user(&v,arg,sizeof(v))!=0)
+ return -EFAULT;
+ if(v.tuner||lastchan) /* Only tuner 0 */
+ return -EINVAL;
+ strcpy(v.name, "Television");
+ v.rangelow=0;
+ v.rangehigh=0xFFFFFFFF;
+ v.flags=VIDEO_TUNER_PAL|VIDEO_TUNER_NTSC;
+ v.mode = btv->win.norm;
+ if(copy_to_user(arg,&v,sizeof(v)))
+ return -EFAULT;
+ return 0;
+ }
+ /* We have but tuner 0 */
+ case VIDIOCSTUNER:
+ {
+ struct video_tuner v;
+ if(copy_from_user(&v, arg, sizeof(v)))
+ return -EFAULT;
+ /* Only channel 0 has a tuner */
+ if(v.tuner!=0 || lastchan)
+ return -EINVAL;
+ if(v.mode!=VIDEO_MODE_PAL||v.mode!=VIDEO_MODE_NTSC)
+ return -EOPNOTSUPP;
+ btv->win.norm = v.mode;
+ bt848_set_size(btv);
+ return 0;
+ }
+ case VIDIOCGPICT:
+ {
+ struct video_picture p=btv->picture;
+ if(btv->win.bpp==8)
+ p.palette=VIDEO_PALETTE_HI240;
+ if(btv->win.bpp==16)
+ p.palette=VIDEO_PALETTE_RGB565;
+ if(btv->win.bpp==24)
+ p.palette=VIDEO_PALETTE_RGB24;
+ if(btv->win.bpp==32)
+ p.palette=VIDEO_PALETTE_RGB32;
+ if(copy_to_user(arg, &p, sizeof(p)))
+ return -EFAULT;
+ return 0;
+ }
+ case VIDIOCSPICT:
+ {
+ struct video_picture p;
+ if(copy_from_user(&p, arg,sizeof(p)))
+ return -EFAULT;
+ /* We want -128 to 127 we get 0-65535 */
+ bt848_bright(btv, (p.brightness>>8)-128);
+ /* 0-511 for the colour */
+ bt848_sat_u(btv, p.colour>>7);
+ bt848_sat_v(btv, ((p.colour>>7)*201L)/237);
+ /* -128 to 127 */
+ bt848_hue(btv, (p.hue>>8)-128);
+ /* 0-511 */
+ bt848_contrast(btv, p.contrast>>7);
+ btv->picture=p;
+ return 0;
+ }
+ case VIDIOCSWIN:
+ {
+ struct video_window vw;
+ struct video_clip *vcp;
+ int on;
+
+ if(copy_from_user(&vw,arg,sizeof(vw)))
+ return -EFAULT;
+
+ if(vw.flags)
+ return -EINVAL;
+
+ btv->win.x=vw.x;
+ btv->win.y=vw.y;
+ btv->win.width=vw.width;
+ btv->win.height=vw.height;
+
+ if(btv->win.height>btv->win.cropheight/2)
+ btv->win.interlace=1;
+ else
+ btv->win.interlace=0;
+
+ on=(btv->cap&3)?1:0;
+
+ bt848_cap(btv,0);
+ bt848_set_size(btv);
+
+ if(vw.clipcount>256)
+ return -EDOM; /* Too many! */
+
+ /*
+ * Do any clips.
+ */
+
+ vcp=vmalloc(sizeof(struct video_clip)*(vw.clipcount+4));
+ if(vcp==NULL)
+ return -ENOMEM;
+ if(vw.clipcount && copy_from_user(vcp,vw.clips,sizeof(struct video_clip)*vw.clipcount))
+ return -EFAULT;
+ /*
+ * Impose display clips
+ */
+ if(btv->win.x<0)
+ new_risc_clip(&vw, vcp, 0, 0, -btv->win.x, btv->win.height-1);
+ if(btv->win.y<0)
+ new_risc_clip(&vw, vcp, 0, 0, btv->win.width-1,-btv->win.y);
+ if(btv->win.x+btv->win.width> btv->win.swidth)
+ new_risc_clip(&vw, vcp, btv->win.swidth-btv->win.x, 0, btv->win.width-1, btv->win.height-1);
+ if(btv->win.y+btv->win.height > btv->win.sheight)
+ new_risc_clip(&vw, vcp, 0, btv->win.sheight-btv->win.y, btv->win.width-1, btv->win.height-1);
+ /*
+ * Question: Do we need to walk the clip list
+ * and saw off any clips outside the window
+ * frame or will write_risc_tab do the right
+ * thing ?
+ */
+ write_risc_data(btv,vcp, vw.clipcount);
+ vfree(vcp);
+ if(on)
+ bt848_cap(btv,1);
+ return 0;
+ }
+ case VIDIOCGWIN:
+ {
+ struct video_window vw;
+ /* Oh for a COBOL move corresponding .. */
+ vw.x=btv->win.x;
+ vw.y=btv->win.y;
+ vw.width=btv->win.width;
+ vw.height=btv->win.height;
+ vw.chromakey=0;
+ vw.flags=0;
+ if(btv->win.interlace)
+ vw.flags|=VIDEO_WINDOW_INTERLACE;
+ if(copy_to_user(arg,&vw,sizeof(vw)))
+ return -EFAULT;
+ return 0;
+ }
+ case VIDIOCCAPTURE:
+ {
+ int v;
+ if(copy_from_user(&v, arg,sizeof(v)))
+ return -EFAULT;
+ if(btv->win.vidadr==0 || btv->win.width==0 || btv->win.height==0)
+ return -EINVAL;
+ if(v==0)
+ {
+ bt848_cap(btv,0);
+ }
+ else
+ {
+ bt848_cap(btv,1);
+ }
+ return 0;
+ }
+ case VIDIOCGFBUF:
+ {
+ struct video_buffer v;
+ v.base=(void *)btv->win.vidadr;
+ v.height=btv->win.sheight;
+ v.width=btv->win.swidth;
+ v.depth=btv->win.bpp*8;
+ v.bytesperline=btv->win.bpl;
+ if(copy_to_user(arg, &v,sizeof(v)))
+ return -EFAULT;
+ return 0;
+
+ }
+ case VIDIOCSFBUF:
+ {
+ struct video_buffer v;
+ if(!suser())
+ return -EPERM;
+ if(copy_from_user(&v, arg,sizeof(v)))
+ return -EFAULT;
+ if(v.depth!=8 && v.depth!=16 && v.depth!=24 && v.depth!=32)
+ return -EINVAL;
+ btv->win.vidadr=(int)v.base;
+ btv->win.sheight=v.height;
+ btv->win.swidth=v.width;
+ btv->win.bpp=v.depth/8;
+ btv->win.bpl=v.bytesperline;
+
+ DEBUG(printk("Display at %p is %d by %d, bytedepth %d, bpl %d\n",
+ v.base, v.width,v.height, btv->win.bpp, btv->win.bpl));
+ bt848_set_size(btv);
+ return 0;
+ }
+ case VIDIOCKEY:
+ {
+ /* Will be handled higher up .. */
+ return 0;
+ }
+ case VIDIOCGFREQ:
+ {
+ unsigned long v=btv->win.freq;
+ if(copy_to_user(arg,&v,sizeof(v)))
+ return -EFAULT;
+ return 0;
+ }
+ case VIDIOCSFREQ:
+ {
+ unsigned long v;
+ if(copy_from_user(&v, arg, sizeof(v)))
+ return -EFAULT;
+ btv->win.freq=v;
+ set_freq(btv, btv->win.freq);
+ return 0;
+ }
+
+ case VIDIOCGAUDIO:
+ {
+ struct video_audio vp;
+ vp=btv->audio_dev;
+ vp.flags&=~(VIDEO_AUDIO_MUTE|VIDEO_AUDIO_MUTABLE);
+ vp.flags|=VIDEO_AUDIO_MUTABLE;
+ return 0;
+ }
+ case VIDIOCSAUDIO:
+ {
+ struct video_audio v;
+ if(copy_from_user(&v,arg, sizeof(v)))
+ return -EFAULT;
+ if(v.flags&VIDEO_AUDIO_MUTE)
+ audio(btv, AUDIO_MUTE);
+ if(v.audio<0||v.audio>2)
+ return -EINVAL;
+ bt848_muxsel(btv,v.audio);
+ if(!(v.flags&VIDEO_AUDIO_MUTE))
+ audio(btv, AUDIO_UNMUTE);
+ btv->audio_dev=v;
+ return 0;
+ }
+
+ case BTTV_WRITEEE:
+ if(copy_from_user((void *) eedata, (void *) arg, 256))
+ return -EFAULT;
+ writeee(btv, eedata);
+ break;
+
+ case BTTV_READEE:
+ readee(btv, eedata);
+ if(copy_to_user((void *) arg, (void *) eedata, 256))
+ return -EFAULT;
+ break;
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+static int bttv_init_done(struct video_device *dev)
+{
+ return 0;
+}
+
+static struct video_device bttv_template=
+{
+ "UNSET",
+ VID_TYPE_TUNER|VID_TYPE_CAPTURE|VID_TYPE_OVERLAY|VID_TYPE_TELETEXT,
+ VID_HARDWARE_BT848,
+ bttv_open,
+ bttv_close,
+ bttv_read,
+ bttv_write,
+ bttv_ioctl,
+ NULL, /* no mmap yet */
+ bttv_init_done,
+ NULL,
+ 0,
+ 0
+};
+
+struct vidbases
+{
+ ushort vendor, device;
+ char *name;
+ uint badr;
+};
+
+static struct vidbases vbs[] = {
+ { PCI_VENDOR_ID_TSENG, 0, "TSENG", PCI_BASE_ADDRESS_0},
+ { PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MIL,
+ "Matrox Millennium", PCI_BASE_ADDRESS_1},
+ { PCI_VENDOR_ID_MATROX, 0x051a, "Matrox Mystique", PCI_BASE_ADDRESS_1},
+ { PCI_VENDOR_ID_S3, 0, "S3", PCI_BASE_ADDRESS_0},
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_210888GX,
+ "ATI MACH64 Winturbo", PCI_BASE_ADDRESS_0},
+ { PCI_VENDOR_ID_CIRRUS, 0, "Cirrus Logic", PCI_BASE_ADDRESS_0},
+ { PCI_VENDOR_ID_N9, PCI_DEVICE_ID_N9_I128,
+ "Number Nine Imagine 128", PCI_BASE_ADDRESS_0},
+ { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TGA,
+ "DEC DC21030", PCI_BASE_ADDRESS_0},
+};
+
+
+/* DEC TGA offsets stolen from XFree-3.2 */
+
+static uint dec_offsets[4] = {
+ 0x200000,
+ 0x804000,
+ 0,
+ 0x1004000
+};
+
+#define NR_CARDS (sizeof(vbs)/sizeof(struct vidbases))
+
+/* Scan for PCI display adapter
+ if more than one card is present the last one is used for now */
+
+static int find_vga(void)
+{
+ unsigned int devfn, class, vendev;
+ ushort vendor, device, badr;
+ int found=0, bus=0, i, tga_type;
+ unsigned int vidadr=0;
+
+
+ for (devfn = 0; devfn < 0xff; devfn++)
+ {
+ if (PCI_FUNC(devfn) != 0)
+ continue;
+ pcibios_read_config_dword(bus, devfn, PCI_VENDOR_ID, &vendev);
+ if (vendev == 0xffffffff || vendev == 0x00000000)
+ continue;
+ pcibios_read_config_word(bus, devfn, PCI_VENDOR_ID, &vendor);
+ pcibios_read_config_word(bus, devfn, PCI_DEVICE_ID, &device);
+ pcibios_read_config_dword(bus, devfn, PCI_CLASS_REVISION, &class);
+ class = class >> 16;
+/* if (class == PCI_CLASS_DISPLAY_VGA) {*/
+ if ((class>>8) == PCI_BASE_CLASS_DISPLAY ||
+ /* Number 9 GXE64Pro needs this */
+ class == PCI_CLASS_NOT_DEFINED_VGA)
+ {
+ badr=0;
+ printk(KERN_INFO "bttv: PCI display adapter: ");
+ for (i=0; i<NR_CARDS; i++)
+ {
+ if (vendor==vbs[i].vendor)
+ {
+ if (vbs[i].device)
+ if (vbs[i].device!=device)
+ continue;
+ printk("%s.\n", vbs[i].name);
+ badr=vbs[i].badr;
+ break;
+ }
+ }
+ if (!badr)
+ {
+ printk(KERN_ERR "bttv: Unknown video memory base address.\n");
+ continue;
+ }
+ pcibios_read_config_dword(bus, devfn, badr, &vidadr);
+ if (vidadr & PCI_BASE_ADDRESS_SPACE_IO)
+ {
+ printk(KERN_ERR "bttv: Memory seems to be I/O memory.\n");
+ printk(KERN_ERR "bttv: Check entry for your card type in bttv.c vidbases struct.\n");
+ continue;
+ }
+ vidadr &= PCI_BASE_ADDRESS_MEM_MASK;
+ if (!vidadr)
+ {
+ printk(KERN_ERR "bttv: Memory @ 0, must be something wrong!");
+ continue;
+ }
+
+ if (vendor==PCI_VENDOR_ID_DEC)
+ if (device==PCI_DEVICE_ID_DEC_TGA)
+ {
+ tga_type = (readl((unsigned long)vidadr) >> 12) & 0x0f;
+ if (tga_type != 0 && tga_type != 1 && tga_type != 3)
+ {
+ printk(KERN_ERR "bttv: TGA type (0x%x) unrecognized!\n", tga_type);
+ found--;
+ }
+ vidadr+=dec_offsets[tga_type];
+ }
+
+ DEBUG(printk(KERN_DEBUG "bttv: memory @ 0x%08x, ", vidadr));
+ DEBUG(printk(KERN_DEBUG "devfn: 0x%04x.\n", devfn));
+ found++;
+ }
+ }
+
+ if (vidmem)
+ {
+ vidadr=vidmem<<20;
+ printk(KERN_INFO "bttv: Video memory override: 0x%08x\n", vidadr);
+ found=1;
+ }
+ for (i=0; i<BTTV_MAX; i++)
+ bttvs[i].win.vidadr=vidadr;
+
+ return found;
+}
+
+#define TRITON_PCON 0x50
+#define TRITON_BUS_CONCURRENCY (1<<0)
+#define TRITON_STREAMING (1<<1)
+#define TRITON_WRITE_BURST (1<<2)
+#define TRITON_PEER_CONCURRENCY (1<<3)
+
+static void handle_chipset(void)
+{
+ int index;
+
+ for (index = 0; index < 8; index++)
+ {
+ unsigned char bus, devfn;
+ unsigned char b, bo;
+
+ /* nothing wrong with this one, just checking buffer control config */
+
+ if (!pcibios_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82441,
+ index, &bus, &devfn))
+ {
+ pcibios_read_config_byte(bus, devfn, 0x53, &b);
+ DEBUG(printk(KERN_INFO "bttv: Host bridge: 82441FX Natoma, "));
+ DEBUG(printk("bufcon=0x%02x\n",b));
+ }
+
+ if (!pcibios_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82437,
+ index, &bus, &devfn))
+ {
+ printk(KERN_INFO "bttv: Host bridge 82437FX Triton PIIX\n");
+ pcibios_read_config_byte(bus, devfn, TRITON_PCON, &b);
+ bo=b;
+ DEBUG(printk(KERN_DEBUG "bttv: 82437FX: PCON: 0x%x\n",b));
+
+ /* 430FX (Triton I) freezes with bus concurrency on -> switch it off */
+ if(!(b & TRITON_BUS_CONCURRENCY))
+ {
+ printk(KERN_WARNING "bttv: 82437FX: disabling bus concurrency\n");
+ b |= TRITON_BUS_CONCURRENCY;
+ }
+
+ /* still freezes on other boards -> switch off even more */
+ if(b & TRITON_PEER_CONCURRENCY)
+ {
+ printk(KERN_WARNING "bttv: 82437FX: disabling peer concurrency\n");
+ b &= ~TRITON_PEER_CONCURRENCY;
+ }
+ if(!(b & TRITON_STREAMING))
+ {
+ printk(KERN_WARNING "bttv: 82437FX: disabling streaming\n");
+ b |= TRITON_STREAMING;
+ }
+
+ if (b!=bo)
+ {
+ pcibios_write_config_byte(bus, devfn, TRITON_PCON, b);
+ printk(KERN_DEBUG "bttv: 82437FX: PCON changed to: 0x%x\n",b);
+ }
+ }
+ }
+}
+
+static void init_tda9850(struct bttv *btv)
+{
+ I2CWrite(btv, I2C_TDA9850, TDA9850_CON3, 0, 1);
+}
+
+/* Figure out card and tuner type */
+
+static void idcard(struct bttv *btv)
+{
+ int i;
+
+ btwrite(0, BT848_GPIO_OUT_EN);
+ DEBUG(printk(KERN_DEBUG "bttv: GPIO: 0x%08x\n", btread(BT848_GPIO_DATA)));
+
+ btv->type=BTTV_MIRO;
+ btv->tuner=tuner;
+
+ if (I2CRead(btv, I2C_HAUPEE)>=0)
+ btv->type=BTTV_HAUPPAUGE;
+ else if (I2CRead(btv, I2C_STBEE)>=0)
+ btv->type=BTTV_STB;
+
+ for (i=0xc0; i<0xd0; i+=2)
+ {
+ if (I2CRead(btv, i)>=0)
+ {
+ btv->tuneradr=i;
+ break;
+ }
+ }
+
+ btv->dbx = I2CRead(btv, I2C_TDA9850) ? 0 : 1;
+
+ if (btv->dbx)
+ init_tda9850(btv);
+
+ /* How do I detect the tuner type for other cards but Miro ??? */
+ printk(KERN_INFO "bttv: model: ");
+ switch (btv->type)
+ {
+ case BTTV_MIRO:
+ btv->tuner=((btread(BT848_GPIO_DATA)>>10)-1)&7;
+ printk("MIRO");
+ strcpy(btv->video_dev.name,"BT848(Miro)");
+ break;
+ case BTTV_HAUPPAUGE:
+ printk("HAUPPAUGE");
+ strcpy(btv->video_dev.name,"BT848(Hauppauge)");
+ break;
+ case BTTV_STB:
+ printk("STB");
+ strcpy(btv->video_dev.name,"BT848(STB)");
+ break;
+ case BTTV_INTEL:
+ printk("Intel");
+ strcpy(btv->video_dev.name,"BT848(Intel)");
+ break;
+ case BTTV_DIAMOND:
+ printk("Diamond");
+ strcpy(btv->video_dev.name,"BT848(Diamond)");
+ break;
+ }
+ printk(" (%s @ 0x%02x)\n", tuners[btv->tuner].name, btv->tuneradr);
+ audio(btv, AUDIO_MUTE);
+}
+
+
+static void bt848_set_risc_jmps(struct bttv *btv)
+{
+ int flags=btv->cap;
+
+ btv->risc_jmp[0]=BT848_RISC_SYNC|BT848_RISC_RESYNC|BT848_FIFO_STATUS_VRE;
+ btv->risc_jmp[1]=0;
+
+ btv->risc_jmp[2]=BT848_RISC_JUMP;
+ if (flags&8)
+ btv->risc_jmp[3]=virt_to_bus(btv->vbi_odd);
+ else
+ btv->risc_jmp[3]=virt_to_bus(btv->risc_jmp+4);
+
+ btv->risc_jmp[4]=BT848_RISC_JUMP;
+ if (flags&2)
+ btv->risc_jmp[5]=virt_to_bus(btv->risc_odd);
+ else
+ btv->risc_jmp[5]=virt_to_bus(btv->risc_jmp+6);
+
+ btv->risc_jmp[6]=BT848_RISC_SYNC|BT848_RISC_RESYNC|BT848_FIFO_STATUS_VRO;
+ btv->risc_jmp[7]=0;
+
+ btv->risc_jmp[8]=BT848_RISC_JUMP;
+ if (flags&4)
+ btv->risc_jmp[9]=virt_to_bus(btv->vbi_even);
+ else
+ btv->risc_jmp[9]=virt_to_bus(btv->risc_jmp+10);
+
+ btv->risc_jmp[10]=BT848_RISC_JUMP;
+ if (flags&1)
+ btv->risc_jmp[11]=virt_to_bus(btv->risc_even);
+ else
+ btv->risc_jmp[11]=virt_to_bus(btv->risc_jmp);
+
+ btaor(flags, ~0x0f, BT848_CAP_CTL);
+ if (flags&0x0f)
+ bt848_dma(btv, 3);
+ else
+ bt848_dma(btv, 0);
+}
+
+
+static int init_bt848(struct bttv *btv)
+{
+ /* reset the bt848 */
+ btwrite(0,BT848_SRESET);
+ btv->user=0;
+
+ DEBUG(printk(KERN_DEBUG "bttv: bt848_mem: 0x%08x\n",(unsigned int) btv->bt848_mem));
+
+ /* default setup for max. PAL size in a 1024xXXX hicolor framebuffer */
+
+ btv->win.norm=0; /* change this to 1 for NTSC, 2 for SECAM */
+ btv->win.interlace=1;
+ btv->win.x=0;
+ btv->win.y=0;
+ btv->win.width=768; /* 640 */
+ btv->win.height=576; /* 480 */
+ btv->win.cropwidth=768; /* 640 */
+ btv->win.cropheight=576; /* 480 */
+ btv->win.cropx=0;
+ btv->win.cropy=0;
+ btv->win.bpp=2;
+ btv->win.bpl=1024*btv->win.bpp;
+ btv->win.swidth=1024;
+ btv->win.sheight=768;
+ btv->cap=0;
+
+ if (!(btv->risc_odd=(dword *) kmalloc(RISCMEM_LEN/2, GFP_KERNEL)))
+ return -1;
+ if (!(btv->risc_even=(dword *) kmalloc(RISCMEM_LEN/2, GFP_KERNEL)))
+ return -1;
+ if (!(btv->risc_jmp =(dword *) kmalloc(1024, GFP_KERNEL)))
+ return -1;
+ btv->vbi_odd=btv->risc_jmp+12;
+ btv->vbi_even=btv->vbi_odd+256;
+ btv->bus_vbi_odd=virt_to_bus(btv->risc_jmp);
+ btv->bus_vbi_even=virt_to_bus(btv->risc_jmp+6);
+
+ btwrite(virt_to_bus(btv->risc_jmp+2), BT848_RISC_STRT_ADD);
+ btv->vbibuf=(unchar *) vmalloc(VBIBUF_SIZE);
+ if (!btv->vbibuf)
+ return -1;
+
+ bt848_muxsel(btv, 1);
+ bt848_set_size(btv);
+
+/* btwrite(0, BT848_TDEC); */
+ btwrite(0x10, BT848_COLOR_CTL);
+ btwrite(0x00, BT848_CAP_CTL);
+
+ btwrite(0x0ff, BT848_VBI_PACK_SIZE);
+ btwrite(1, BT848_VBI_PACK_DEL);
+
+ btwrite(0xfc, BT848_GPIO_DMA_CTL);
+ btwrite(BT848_IFORM_MUX1 | BT848_IFORM_XTAUTO | BT848_IFORM_PAL_BDGHI,
+ BT848_IFORM);
+
+ bt848_bright(btv, 0x10);
+ btwrite(0xd8, BT848_CONTRAST_LO);
+
+ btwrite(0x60, BT848_E_VSCALE_HI);
+ btwrite(0x60, BT848_O_VSCALE_HI);
+ btwrite(/*BT848_ADC_SYNC_T|*/
+ BT848_ADC_RESERVED|BT848_ADC_CRUSH, BT848_ADC);
+
+ btwrite(BT848_CONTROL_LDEC, BT848_E_CONTROL);
+ btwrite(BT848_CONTROL_LDEC, BT848_O_CONTROL);
+ btwrite(0x00, BT848_E_SCLOOP);
+ btwrite(0x00, BT848_O_SCLOOP);
+
+ btwrite(0xffffffUL,BT848_INT_STAT);
+/* BT848_INT_PABORT|BT848_INT_RIPERR|BT848_INT_PPERR|BT848_INT_FDSR|
+ BT848_INT_FTRGT|BT848_INT_FBUS|*/
+ btwrite(BT848_INT_ETBF|
+ BT848_INT_SCERR|
+ BT848_INT_RISCI|BT848_INT_OCERR|BT848_INT_VPRES|
+ BT848_INT_FMTCHG|BT848_INT_HLOCK,
+ BT848_INT_MASK);
+
+/* make_risctab(btv); */
+ make_vbitab(btv);
+ bt848_set_risc_jmps(btv);
+
+ /*
+ * Now add the template and register the device unit.
+ */
+
+ memcpy(&btv->video_dev,&bttv_template,sizeof(bttv_template));
+ idcard(btv);
+ if(video_register_device(&btv->video_dev)<0)
+ return -1;
+ return 0;
+}
+
+
+static void bttv_irq(int irq, void *dev_id, struct pt_regs * regs)
+{
+ u32 stat,astat;
+ u32 dstat;
+ int count;
+ struct bttv *btv;
+
+ btv=(struct bttv *)dev_id;
+ count=0;
+ while (1)
+ {
+ /* get/clear interrupt status bits */
+ stat=btread(BT848_INT_STAT);
+ astat=stat&btread(BT848_INT_MASK);
+ if (!astat)
+ return;
+ btwrite(astat,BT848_INT_STAT);
+ IDEBUG(printk ("bttv: astat %08x\n",astat));
+ IDEBUG(printk ("bttv: stat %08x\n",stat));
+
+ /* get device status bits */
+ dstat=btread(BT848_DSTATUS);
+
+ if (astat&BT848_INT_FMTCHG)
+ {
+ IDEBUG(printk ("bttv: IRQ_FMTCHG\n"));
+/* btv->win.norm&=(dstat&BT848_DSTATUS_NUML) ? (~1) : (~0); */
+ }
+ if (astat&BT848_INT_VPRES)
+ {
+ IDEBUG(printk ("bttv: IRQ_VPRES\n"));
+ }
+ if (astat&BT848_INT_VSYNC)
+ {
+ IDEBUG(printk ("bttv: IRQ_VSYNC\n"));
+ }
+ if (astat&BT848_INT_SCERR) {
+ IDEBUG(printk ("bttv: IRQ_SCERR\n"));
+ bt848_dma(btv, 0);
+ bt848_dma(btv, 1);
+ wake_up_interruptible(&btv->vbiq);
+ wake_up_interruptible(&btv->capq);
+ }
+ if (astat&BT848_INT_RISCI)
+ {
+ IDEBUG(printk ("bttv: IRQ_RISCI\n"));
+ /* printk ("bttv: IRQ_RISCI%d\n",stat>>28); */
+ if (stat&(1<<28))
+ {
+ btv->vbip=0;
+ wake_up_interruptible(&btv->vbiq);
+ }
+ if (stat&(2<<28))
+ {
+ bt848_set_risc_jmps(btv);
+ wake_up_interruptible(&btv->capq);
+ break;
+ }
+ }
+ if (astat&BT848_INT_OCERR)
+ {
+ IDEBUG(printk ("bttv: IRQ_OCERR\n"));
+ }
+ if (astat&BT848_INT_PABORT)
+ {
+ IDEBUG(printk ("bttv: IRQ_PABORT\n"));
+ }
+ if (astat&BT848_INT_RIPERR)
+ {
+ IDEBUG(printk ("bttv: IRQ_RIPERR\n"));
+ }
+ if (astat&BT848_INT_PPERR)
+ {
+ IDEBUG(printk ("bttv: IRQ_PPERR\n"));
+ }
+ if (astat&BT848_INT_FDSR)
+ {
+ IDEBUG(printk ("bttv: IRQ_FDSR\n"));
+ }
+ if (astat&BT848_INT_FTRGT)
+ {
+ IDEBUG(printk ("bttv: IRQ_FTRGT\n"));
+ }
+ if (astat&BT848_INT_FBUS)
+ {
+ IDEBUG(printk ("bttv: IRQ_FBUS\n"));
+ }
+ if (astat&BT848_INT_HLOCK)
+ {
+ if (dstat&BT848_DSTATUS_HLOC)
+ audio(btv, AUDIO_ON);
+ else
+ audio(btv, AUDIO_OFF);
+ }
+
+ if (astat&BT848_INT_I2CDONE)
+ {
+ }
+
+ count++;
+ if (count > 10)
+ printk (KERN_WARNING "bttv: irq loop %d\n", count);
+ if (count > 20)
+ {
+ btwrite(0, BT848_INT_MASK);
+ printk(KERN_ERR "bttv: IRQ lockup, cleared int mask\n");
+ }
+ }
+}
+
+
+/*
+ * Scan for a Bt848 card, request the irq and map the io memory
+ */
+
+static int find_bt848(void)
+{
+ short pci_index;
+ unsigned char command, latency;
+ int result;
+ unsigned char bus, devfn;
+ struct bttv *btv;
+
+ bttv_num=0;
+
+ if (!pcibios_present())
+ {
+ DEBUG(printk(KERN_DEBUG "bttv: PCI-BIOS not present or not accessable!\n"));
+ return 0;
+ }
+
+ for (pci_index = 0;
+ !pcibios_find_device(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT848,
+ pci_index, &bus, &devfn);
+ ++pci_index)
+ {
+ btv=&bttvs[bttv_num];
+ btv->bus=bus;
+ btv->devfn=devfn;
+ btv->bt848_mem=NULL;
+ btv->vbibuf=NULL;
+ btv->risc_jmp=NULL;
+ btv->vbi_odd=NULL;
+ btv->vbi_even=NULL;
+ btv->vbiq=NULL;
+ btv->capq=NULL;
+ btv->vbip=VBIBUF_SIZE;
+
+ pcibios_read_config_byte(btv->bus, btv->devfn,
+ PCI_INTERRUPT_LINE, &btv->irq);
+ pcibios_read_config_dword(btv->bus, btv->devfn, PCI_BASE_ADDRESS_0,
+ &btv->bt848_adr);
+
+ if (remap&&(!bttv_num))
+ {
+ remap<<=20;
+ remap&=PCI_BASE_ADDRESS_MEM_MASK;
+ printk(KERN_INFO "Remapping to : 0x%08x.\n", remap);
+ remap|=btv->bt848_adr&(~PCI_BASE_ADDRESS_MEM_MASK);
+ pcibios_write_config_dword(btv->bus, btv->devfn, PCI_BASE_ADDRESS_0,
+ remap);
+ pcibios_read_config_dword(btv->bus, btv->devfn, PCI_BASE_ADDRESS_0,
+ &btv->bt848_adr);
+ }
+
+ btv->bt848_adr&=PCI_BASE_ADDRESS_MEM_MASK;
+ pcibios_read_config_byte(btv->bus, btv->devfn, PCI_CLASS_REVISION,
+ &btv->revision);
+ printk(KERN_INFO "bttv: Brooktree Bt848 (rev %d) ",btv->revision);
+ printk("bus: %d, devfn: %d, ",
+ btv->bus, btv->devfn);
+ printk("irq: %d, ",btv->irq);
+ printk("memory: 0x%08x.\n", btv->bt848_adr);
+
+ btv->bt848_mem=ioremap(btv->bt848_adr, 0x1000);
+
+ result = request_irq(btv->irq, bttv_irq,
+ SA_SHIRQ | SA_INTERRUPT,"bttv",(void *)btv);
+ if (result==-EINVAL)
+ {
+ printk(KERN_ERR "bttv: Bad irq number or handler\n");
+ return -EINVAL;
+ }
+ if (result==-EBUSY)
+ {
+ printk(KERN_ERR "bttv: IRQ %d busy, change your PnP config in BIOS\n",btv->irq);
+ return result;
+ }
+ if (result < 0)
+ return result;
+
+ /* Enable bus-mastering */
+ pcibios_read_config_byte(btv->bus, btv->devfn, PCI_COMMAND, &command);
+ command|=PCI_COMMAND_MASTER;
+ pcibios_write_config_byte(btv->bus, btv->devfn, PCI_COMMAND, command);
+ pcibios_read_config_byte(btv->bus, btv->devfn, PCI_COMMAND, &command);
+ if (!(command&PCI_COMMAND_MASTER))
+ {
+ printk(KERN_ERR "bttv: PCI bus-mastering could not be enabled\n");
+ return -1;
+ }
+ pcibios_read_config_byte(btv->bus, btv->devfn, PCI_LATENCY_TIMER,
+ &latency);
+ if (!latency)
+ {
+ latency=32;
+ pcibios_write_config_byte(btv->bus, btv->devfn, PCI_LATENCY_TIMER,
+ latency);
+ }
+ DEBUG(printk(KERN_DEBUG "bttv: latency: %02x\n", latency));
+ bttv_num++;
+ }
+ if(bttv_num)
+ printk(KERN_INFO "bttv: %d Bt848 card(s) found.\n", bttv_num);
+ return bttv_num;
+}
+
+static void release_bttv(void)
+{
+ u8 command;
+ int i;
+ struct bttv *btv;
+
+ for (i=0;i<bttv_num; i++)
+ {
+ btv=&bttvs[i];
+ /* turn off all capturing, DMA and IRQs */
+
+ /* first disable interrupts before unmapping the memory! */
+ btwrite(0, BT848_INT_MASK);
+ btwrite(0xffffffffUL,BT848_INT_STAT);
+ btwrite(0x0, BT848_GPIO_OUT_EN);
+
+ bt848_cap(btv, 0);
+
+ /* disable PCI bus-mastering */
+ pcibios_read_config_byte(btv->bus, btv->devfn, PCI_COMMAND, &command);
+ command|=PCI_COMMAND_MASTER;
+ pcibios_write_config_byte(btv->bus, btv->devfn, PCI_COMMAND, command);
+
+ /* unmap and free memory */
+ if (btv->risc_odd)
+ kfree((void *) btv->risc_odd);
+
+ if (btv->risc_even)
+ kfree((void *) btv->risc_even);
+
+ DEBUG(printk(KERN_DEBUG "free: risc_jmp: 0x%08x.\n", btv->risc_jmp));
+ if (btv->risc_jmp)
+ kfree((void *) btv->risc_jmp);
+
+ DEBUG(printk(KERN_DEBUG "bt848_vbibuf: 0x%08x.\n", btv->vbibuf));
+ if (btv->vbibuf)
+ vfree((void *) btv->vbibuf);
+ free_irq(btv->irq,btv);
+ DEBUG(printk(KERN_DEBUG "bt848_mem: 0x%08x.\n", btv->bt848_mem));
+ if (btv->bt848_mem)
+ iounmap(btv->bt848_mem);
+ video_unregister_device(&btv->video_dev);
+ }
+}
+
+
+#ifdef MODULE
+
+int init_module(void)
+{
+#else
+int init_bttv_cards(struct video_init *unused)
+{
+#endif
+ int i;
+
+ handle_chipset();
+ if (find_bt848()<0)
+ return -EIO;
+
+ for (i=0; i<bttv_num; i++)
+ {
+ if (init_bt848(&bttvs[i])<0)
+ {
+ release_bttv();
+ return -EIO;
+ }
+ }
+ return 0;
+}
+
+#ifdef MODULE
+
+void cleanup_module(void)
+{
+ release_bttv();
+}
+
+#endif
diff --git a/drivers/char/bttv.h b/drivers/char/bttv.h
new file mode 100644
index 000000000..e1c54e94c
--- /dev/null
+++ b/drivers/char/bttv.h
@@ -0,0 +1,162 @@
+/*
+ bttv - Bt848 frame grabber driver
+
+ Copyright (C) 1996,97 Ralph Metzler (rjkm@thp.uni-koeln.de)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _BTTV_H_
+#define _BTTV_H_
+
+#define TEST_VBI
+
+#include <linux/types.h>
+#include <linux/wait.h>
+
+#include "bt848.h"
+
+typedef unsigned int dword;
+
+struct riscprog {
+ uint length;
+ dword *busadr;
+ dword *prog;
+};
+
+/* values that can be set by user programs */
+
+struct bttv_window {
+ int x, y;
+ ushort width, height;
+ ushort bpp, bpl;
+ ushort swidth, sheight;
+ short cropx, cropy;
+ ushort cropwidth, cropheight;
+ int vidadr;
+ ushort freq;
+ int norm;
+ int interlace;
+ int color_fmt;
+};
+
+/* private data that can only be read (or set indirectly) by user program */
+
+struct bttv {
+ struct video_device video_dev;
+ struct video_picture picture; /* Current picture params */
+ struct video_audio audio_dev; /* Current audio params */
+ u_char bus; /* PCI bus the Bt848 is on */
+ u_char devfn;
+ u_char revision;
+ u_char irq; /* IRQ used by Bt848 card */
+ uint bt848_adr; /* bus address of IO mem returned by PCI BIOS */
+ u_char *bt848_mem; /* pointer to mapped IO memory */
+ ulong busriscmem;
+ dword *riscmem;
+
+ u_char *vbibuf;
+ struct bttv_window win;
+ int type; /* card type */
+ int audio; /* audio mode */
+ int user;
+ int tuner;
+ int tuneradr;
+ int dbx;
+
+ dword *risc_jmp;
+ dword *vbi_odd;
+ dword *vbi_even;
+ dword bus_vbi_even;
+ dword bus_vbi_odd;
+ struct wait_queue *vbiq;
+ struct wait_queue *capq;
+ int vbip;
+
+ dword *risc_odd;
+ dword *risc_even;
+ int cap;
+};
+
+/*The following should be done in more portable way. It depends on define
+ of _ALPHA_BTTV in the Makefile.*/
+#ifdef _ALPHA_BTTV
+#define btwrite(dat,adr) writel((dat),(char *) (btv->bt848_adr+(adr)))
+#define btread(adr) readl(btv->bt848_adr+(adr))
+#else
+#define btwrite(dat,adr) writel((dat), (char *) (btv->bt848_mem+(adr)))
+#define btread(adr) readl(btv->bt848_mem+(adr))
+#endif
+
+#define btand(dat,adr) btwrite((dat) & btread(adr), adr)
+#define btor(dat,adr) btwrite((dat) | btread(adr), adr)
+#define btaor(dat,mask,adr) btwrite((dat) | ((mask) & btread(adr)), adr)
+
+/* bttv ioctls */
+#define BTTV_WRITE_BTREG 0x00
+#define BTTV_READ_BTREG 0x01
+#define BTTV_SET_BTREG 0x02
+#define BTTV_SETRISC 0x03
+#define BTTV_SETWTW 0x04
+#define BTTV_GETWTW 0x05
+#define BTTV_DMA 0x06
+#define BTTV_CAP_OFF 0x07
+#define BTTV_CAP_ON 0x08
+#define BTTV_GETBTTV 0x09
+#define BTTV_SETFREQ 0x0a
+#define BTTV_SETCHAN 0x0b
+#define BTTV_INPUT 0x0c
+#define BTTV_READEE 0x0d
+#define BTTV_WRITEEE 0x0e
+#define BTTV_BRIGHT 0x0f
+#define BTTV_HUE 0x10
+#define BTTV_COLOR 0x11
+#define BTTV_CONTRAST 0x12
+#define BTTV_SET_FFREQ 0x13
+#define BTTV_MUTE 0x14
+
+#define BTTV_GRAB 0x20
+#define BTTV_TESTM 0x20
+
+
+#define BTTV_UNKNOWN 0x00
+#define BTTV_MIRO 0x01
+#define BTTV_HAUPPAUGE 0x02
+#define BTTV_STB 0x03
+#define BTTV_INTEL 0x04
+#define BTTV_DIAMOND 0x05
+
+#define AUDIO_TUNER 0x00
+#define AUDIO_EXTERN 0x01
+#define AUDIO_INTERN 0x02
+#define AUDIO_OFF 0x03
+#define AUDIO_ON 0x04
+#define AUDIO_MUTE 0x80
+#define AUDIO_UNMUTE 0x81
+
+#define I2C_TSA5522 0xc2
+#define I2C_TDA9850 0xb6
+#define I2C_HAUPEE 0xa0
+#define I2C_STBEE 0xae
+
+#define TDA9850_CON1 0x04
+#define TDA9850_CON2 0x05
+#define TDA9850_CON3 0x06
+#define TDA9850_CON4 0x07
+#define TDA9850_ALI1 0x08
+#define TDA9850_ALI2 0x09
+#define TDA9850_ALI3 0x0a
+
+#endif
diff --git a/drivers/char/busmouse.c b/drivers/char/busmouse.c
index 9e160cb93..a33a6a8c2 100644
--- a/drivers/char/busmouse.c
+++ b/drivers/char/busmouse.c
@@ -159,8 +159,8 @@ static int open_mouse(struct inode * inode, struct file * file)
* writes are disallowed
*/
-static long write_mouse(struct inode * inode, struct file * file,
- const char * buffer, unsigned long count)
+static ssize_t write_mouse(struct file * file,
+ const char * buffer, size_t count, loff_t *ppos)
{
return -EINVAL;
}
@@ -169,8 +169,8 @@ static long write_mouse(struct inode * inode, struct file * file,
* read mouse data. Currently never blocks.
*/
-static long read_mouse(struct inode * inode, struct file * file,
- char * buffer, unsigned long count)
+static ssize_t read_mouse(struct file * file,
+ char * buffer, size_t count, loff_t *ppos)
{
int r;
int dx;
diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c
index fa4134dda..ed4ac9846 100644
--- a/drivers/char/cyclades.c
+++ b/drivers/char/cyclades.c
@@ -1,6 +1,6 @@
#define BLOCKMOVE
static char rcsid[] =
-"$Revision: 1.36.4.33 $$Date: 1997/06/27 19:00:00 $";
+"$Revision: 2.1 $$Date: 1997/11/01 17:42:41 $";
/*
* linux/drivers/char/cyclades.c
@@ -8,8 +8,9 @@ static char rcsid[] =
* This file contains the driver for the Cyclades Cyclom-Y multiport
* serial boards.
*
- * Maintained by Marcio Saito (marcio@cyclades.com) and
- * Randolph Bentson (bentson@grieg.seaslug.org)
+ * Maintained by Ivan Passos (ivan@cyclades.com),
+ * Marcio Saito (marcio@cyclades.com) and
+ * Randolph Bentson (bentson@grieg.seaslug.org).
*
* For Technical support and installation problems, please send e-mail
* to support@cyclades.com.
@@ -29,6 +30,18 @@ static char rcsid[] =
* void cleanup_module(void);
*
* $Log: cyclades.c,v $
+ * Revision 2.1 1997/11/01 17:42:41 ivan
+ * Changes in the driver to support Alpha systems (except 8Zo V_1);
+ * BREAK fix for the Cyclades-Z boards;
+ * driver inactivity control by FW implemented;
+ * introduction of flag that allows driver to take advantage of
+ * a special CD1400 feature related to HW flow control;
+ * added support for the CD1400 rev. J (Cyclom-Y boards);
+ * introduction of ioctls to:
+ * - control the rtsdtr_inv flag (Cyclom-Y);
+ * - control the rflow flag (Cyclom-Y);
+ * - adjust the polling interval (Cyclades-Z);
+ *
* Revision 1.36.4.33 1997/06/27 19:00:00 ivan
* Fixes related to kernel version conditional
* compilation.
@@ -435,7 +448,6 @@ static char rcsid[] =
constant in the definition below. No other change is necessary to
support more boards/ports. */
-//#define NR_PORTS 64
#define NR_PORTS 128
#define ZE_V1_NPORTS 64
@@ -463,18 +475,24 @@ static char rcsid[] =
#define cy_min(a,b) (((a)<(b))?(a):(b))
+#if 0
+/********
+ * For the next two macros, it is assumed that the buffer size is a
+ * power of 2
+ ********/
+
#define CHARS_IN_BUF(buf_ctrl) \
- ((buf_ctrl->rx_put - \
- buf_ctrl->rx_get + \
- buf_ctrl->rx_bufsize) % \
- buf_ctrl->rx_bufsize)
+ ((cy_readl(&buf_ctrl->rx_put) - \
+ cy_readl(&buf_ctrl->rx_get) + \
+ cy_readl(&buf_ctrl->rx_bufsize)) & \
+ (cy_readl(&buf_ctrl->rx_bufsize) - 1))
#define SPACE_IN_BUF(buf_ctrl) \
- ((buf_ctrl->tx_get - \
- buf_ctrl->tx_put + \
- buf_ctrl->tx_bufsize - 1) % \
- buf_ctrl->tx_bufsize)
-
+ ((cy_readl(&buf_ctrl->tx_get) - \
+ cy_readl(&buf_ctrl->tx_put) + \
+ cy_readl(&buf_ctrl->tx_bufsize) - 1) & \
+ (cy_readl(&buf_ctrl->tx_bufsize) - 1))
+#endif
#include <linux/module.h>
@@ -540,13 +558,14 @@ static unsigned long cy_get_user(unsigned long *addr)
#define IS_CYC_Z(card) ((card).num_chips == 1)
#define Z_FPGA_CHECK(card) \
- ((((struct RUNTIME_9060 *)((card).ctl_addr))->init_ctrl&(1<<17))!=0)
+ ((cy_readl(&((struct RUNTIME_9060 *) \
+ ((card).ctl_addr))->init_ctrl) & (1<<17)) != 0)
-#define ISZLOADED(card) (((ZO_V1==((struct RUNTIME_9060 *) \
- ((card).ctl_addr))->mail_box_0) || \
+#define ISZLOADED(card) (((ZO_V1==cy_readl(&((struct RUNTIME_9060 *) \
+ ((card).ctl_addr))->mail_box_0)) || \
Z_FPGA_CHECK(card)) && \
- (ZFIRM_ID==((struct FIRM_ID *) \
- ((card).base_addr+ID_ADDRESS))->signature))
+ (ZFIRM_ID==cy_readl(&((struct FIRM_ID *) \
+ ((card).base_addr+ID_ADDRESS))->signature)))
#define WAKEUP_CHARS (SERIAL_XMIT_SIZE-256)
@@ -563,7 +582,7 @@ struct tty_driver cy_serial_driver, cy_callout_driver;
static volatile int cy_irq_triggered;
static volatile int cy_triggered;
static int cy_wild_int_mask;
-static unsigned char *intr_base_addr;
+static volatile ucchar *intr_base_addr;
/* This is the address lookup table. The driver will probe for
@@ -632,26 +651,55 @@ static struct semaphore tmp_buf_sem = MUTEX;
* 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
* 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
* HI VHI
+ * 20
*/
static int baud_table[] = {
0, 50, 75, 110, 134, 150, 200, 300, 600, 1200,
1800, 2400, 4800, 9600, 19200, 38400, 57600, 76800,115200,150000,
- 0};
+ 230400, 0};
-static char baud_co[] = { /* 25 MHz clock option table */
+static char baud_co_25[] = { /* 25 MHz clock option table */
/* value => 00 01 02 03 04 */
/* divide by 8 32 128 512 2048 */
0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x02,
0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
-static char baud_bpr[] = { /* 25 MHz baud rate period table */
+static char baud_bpr_25[] = { /* 25 MHz baud rate period table */
0x00, 0xf5, 0xa3, 0x6f, 0x5c, 0x51, 0xf5, 0xa3, 0x51, 0xa3,
0x6d, 0x51, 0xa3, 0x51, 0xa3, 0x51, 0x36, 0x29, 0x1b, 0x15};
+static char baud_co_60[] = { /* 60 MHz clock option table (CD1400 J) */
+ /* value => 00 01 02 03 04 */
+ /* divide by 8 32 128 512 2048 */
+ 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03,
+ 0x03, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00};
+
+static char baud_bpr_60[] = { /* 60 MHz baud rate period table (CD1400 J) */
+ 0x00, 0x82, 0x21, 0xff, 0xdb, 0xc3, 0x92, 0x62, 0xc3, 0x62,
+ 0x41, 0xc3, 0x62, 0xc3, 0x62, 0xc3, 0x82, 0x62, 0x41, 0x32,
+ 0x21};
+
static char baud_cor3[] = { /* receive threshold */
0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
- 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x07};
+ 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x07,
+ 0x07};
+
+/*
+ * The Cyclades driver implements HW flow control as any serial driver.
+ * The cyclades_port structure member rflow and the vector rflow_thr
+ * allows us to take advantage of a special feature in the CD1400 to avoid
+ * data loss even when the system interrupt latency is too high. These flags
+ * are to be used only with very special applications. Setting these flags
+ * requires the use of a special cable (DTR and RTS reversed). In the new
+ * CD1400-based boards (rev. 6.00 or later), there is no need for special
+ * cables.
+ */
+static char rflow_thr[] = { /* rflow threshold */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a};
/* The Cyclom-Ye has placed the sequential chips in non-sequential
* address order. This look-up table overcomes that problem.
@@ -689,6 +737,8 @@ static void cyz_poll(unsigned long);
static void show_status(int);
#endif
+/* The Cyclades-Z polling cycle is defined by this variable */
+static long cyz_polling_cycle = CZ_DEF_POLL;
static int cyz_timeron = 0;
static struct timer_list
@@ -696,13 +746,12 @@ cyz_timerlist = {
NULL, NULL, 0, 0, cyz_poll
};
-
/**************************************************
error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned long));
-memcpy_tofs (to, from, count);
+copy_to_user (to, from, count);
***************************************************************
error = verify_area(VERIFY_READ, (void *) arg, sizeof(unsigned long *));
-memcpy_fromfs(to, from, count);
+copy_from_user(to, from, count);
**************************************************/
@@ -873,7 +922,7 @@ do_softint(void *private_)
didn't finish within the time limit.
*/
static int
-cyy_issue_cmd(u_char *base_addr, u_char cmd, int index)
+cyy_issue_cmd(volatile ucchar *base_addr, u_char cmd, int index)
{
unsigned long flags;
volatile int i;
@@ -881,7 +930,7 @@ cyy_issue_cmd(u_char *base_addr, u_char cmd, int index)
save_flags(flags); cli();
/* Check to see that the previous command has completed */
for(i = 0 ; i < 100 ; i++){
- if (base_addr[CyCCR<<index] == 0){
+ if (cy_readb(base_addr+(CyCCR<<index)) == 0){
break;
}
udelay(10L);
@@ -894,7 +943,7 @@ cyy_issue_cmd(u_char *base_addr, u_char cmd, int index)
}
/* Issue the new command */
- base_addr[CyCCR<<index] = cmd;
+ cy_writeb((u_long)base_addr+(CyCCR<<index), cmd);
restore_flags(flags);
return(0);
} /* cyy_issue_cmd */
@@ -932,8 +981,9 @@ free_all_interrupts(int irq_lines)
int i;
for (i = 0; i < 16; i++) {
- if (irq_lines & (1 << i))
+ if (irq_lines & (1 << i)) {
free_irq(i,NULL);
+ }
}
} /* free_all_interrupts */
@@ -959,13 +1009,13 @@ check_wild_interrupts(void)
* Delay for 0.1 seconds -- we use a busy loop since this may
* occur during the bootup sequence
*/
- timeout = jiffies+10;
+ timeout = jiffies+(HZ/10);
while (timeout >= jiffies)
;
cy_triggered = 0; /* Reset after letting things settle */
- timeout = jiffies+10;
+ timeout = jiffies+(HZ/10);
while (timeout >= jiffies)
;
@@ -986,11 +1036,11 @@ check_wild_interrupts(void)
* fool-proof, but it works a large part of the time.
*/
static int
-get_auto_irq(unsigned char *address)
+get_auto_irq(volatile ucchar *address)
{
- unsigned long timeout;
- unsigned char *base_addr;
- int index;
+ unsigned long timeout;
+ volatile ucchar *base_addr;
+ int index;
index = 0; /* IRQ probing is only for ISA */
base_addr = address;
@@ -1001,13 +1051,14 @@ get_auto_irq(unsigned char *address)
*/
cy_irq_triggered = 0;
cli();
- base_addr[CyCAR<<index] = 0;
+ cy_writeb((u_long)base_addr+(CyCAR<<index), 0);
cyy_issue_cmd(base_addr,CyCHAN_CTL|CyENB_XMTR,index);
- base_addr[CySRER<<index] |= CyTxMpty;
+ cy_writeb((u_long)base_addr+(CySRER<<index),
+ cy_readb(base_addr+(CySRER<<index)) | CyTxMpty);
probe_ready = 1;
sti();
- timeout = jiffies+2;
+ timeout = jiffies+(HZ/50);
while (timeout >= jiffies) {
if (cy_irq_triggered)
break;
@@ -1021,7 +1072,7 @@ get_auto_irq(unsigned char *address)
* faked out by random interrupts
*/
static int
-do_auto_irq(unsigned char *address)
+do_auto_irq(volatile ucchar *address)
{
int irq_lines = 0;
int irq_try_1 = 0, irq_try_2 = 0;
@@ -1065,27 +1116,28 @@ cy_probe(int irq, void *dev_id, struct pt_regs *regs)
int index = 0; /* probing interrupts is only for ISA */
if (!probe_ready) {
- *(intr_base_addr + (Cy_ClrIntr<<index)) = 0;
+ cy_writeb((u_long)intr_base_addr+(Cy_ClrIntr<<index), 0);
return;
}
cy_irq_triggered = irq;
cy_triggered |= 1 << irq;
- if(intr_base_addr[CySVRR<<index] != 0) {
- save_xir = (u_char) intr_base_addr[CyTIR<<index];
- save_car = intr_base_addr[CyCAR<<index];
+ if(cy_readb(intr_base_addr+(CySVRR<<index)) != 0) {
+ save_xir = (u_char) cy_readb(intr_base_addr+(CyTIR<<index));
+ save_car = cy_readb(intr_base_addr+(CyCAR<<index));
if ((save_xir & 0x3) != 0){
SP("channel ");
CP8(save_xir);
SP(" requesting unexpected interrupt\n");
}
- intr_base_addr[CyCAR<<index] = (save_xir & 0x3);
- intr_base_addr[CySRER<<index] &= ~CyTxMpty;
- intr_base_addr[CyTIR<<index] = (save_xir & 0x3f);
- intr_base_addr[CyCAR<<index] = (save_car);
+ cy_writeb((u_long)intr_base_addr+(CyCAR<<index), (save_xir & 0x3));
+ cy_writeb((u_long)intr_base_addr+(CySRER<<index),
+ cy_readb(intr_base_addr+(CySRER<<index)) & ~CyTxMpty);
+ cy_writeb((u_long)intr_base_addr+(CyTIR<<index), (save_xir & 0x3f));
+ cy_writeb((u_long)intr_base_addr+(CyCAR<<index), (save_car));
}
- *(intr_base_addr + (Cy_ClrIntr<<index)) = 0;
+ cy_writeb((u_long)intr_base_addr+(Cy_ClrIntr<<index), 0);
/* Cy_ClrIntr is 0x1800 */
return;
} /* cy_probe */
@@ -1105,7 +1157,7 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
int chip;
int save_xir, channel, save_car;
char data;
- int char_count;
+ volatile int char_count;
int outch;
int i,j,index;
int too_many;
@@ -1114,12 +1166,16 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
int mdm_status;
if((cinfo = IRQ_cards[irq]) == 0){
+#ifdef CY_DEBUG_INTERRUPTS
+printk("cy_interrupt: spurious interrupt %d\n\r", irq);
+#endif
return; /* spurious interrupt */
}
card_base_addr = (unsigned char *)cinfo->base_addr;
index = cinfo->bus_index;
+
/* This loop checks all chips in the card. Make a note whenever
_any_ chip had some work to do, as this is considered an
indication that there will be more to do. Only when no chip
@@ -1131,7 +1187,7 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
base_addr = (unsigned char *)
(cinfo->base_addr + (cy_chip_offset[chip]<<index));
too_many = 0;
- while ( (status = base_addr[CySVRR<<index]) != 0x00) {
+ while ( (status = cy_readb(base_addr+(CySVRR<<index))) != 0x00) {
had_work++;
/* The purpose of the following test is to ensure that
no chip can monopolize the driver. This forces the
@@ -1142,31 +1198,34 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
break;
}
if (status & CySRReceive) { /* reception interrupt */
+#ifdef CY_DEBUG_INTERRUPTS
+printk("cy_interrupt: rcvd intr, chip %d\n\r", chip);
+#endif
/* determine the channel & change to that context */
- save_xir = (u_char) base_addr[CyRIR<<index];
+ save_xir = (u_char) cy_readb(base_addr+(CyRIR<<index));
channel = (u_short ) (save_xir & CyIRChannel);
i = channel + chip * 4 + cinfo->first_line;
info = &cy_port[i];
info->last_active = jiffies;
- save_car = base_addr[CyCAR<<index];
- base_addr[CyCAR<<index] = save_xir;
+ save_car = cy_readb(base_addr+(CyCAR<<index));
+ cy_writeb((u_long)base_addr+(CyCAR<<index), save_xir);
/* if there is nowhere to put the data, discard it */
if(info->tty == 0){
- j = (base_addr[CyRIVR<<index] & CyIVRMask);
+ j = (cy_readb(base_addr+(CyRIVR<<index)) & CyIVRMask);
if ( j == CyIVRRxEx ) { /* exception */
- data = base_addr[CyRDSR<<index];
+ data = cy_readb(base_addr+(CyRDSR<<index));
} else { /* normal character reception */
- char_count = base_addr[CyRDCR<<index];
+ char_count = cy_readb(base_addr+(CyRDCR<<index));
while(char_count--){
- data = base_addr[CyRDSR<<index];
+ data = cy_readb(base_addr+(CyRDSR<<index));
}
}
}else{ /* there is an open port for this data */
tty = info->tty;
- j = (base_addr[CyRIVR<<index] & CyIVRMask);
+ j = (cy_readb(base_addr+(CyRIVR<<index)) & CyIVRMask);
if ( j == CyIVRRxEx ) { /* exception */
- data = base_addr[CyRDSR<<index];
+ data = cy_readb(base_addr+(CyRDSR<<index));
if(data & info->ignore_status_mask){
continue;
}
@@ -1177,7 +1236,7 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
*tty->flip.flag_buf_ptr++ =
TTY_BREAK;
*tty->flip.char_buf_ptr++ =
- base_addr[CyRDSR<<index];
+ cy_readb(base_addr+(CyRDSR<<index));
if (info->flags & ASYNC_SAK){
do_SAK(tty);
}
@@ -1185,12 +1244,12 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
*tty->flip.flag_buf_ptr++ =
TTY_FRAME;
*tty->flip.char_buf_ptr++ =
- base_addr[CyRDSR<<index];
+ cy_readb(base_addr+(CyRDSR<<index));
}else if(data & CyPARITY){
*tty->flip.flag_buf_ptr++ =
TTY_PARITY;
*tty->flip.char_buf_ptr++ =
- base_addr[CyRDSR<<index];
+ cy_readb(base_addr+(CyRDSR<<index));
}else if(data & CyOVERRUN){
*tty->flip.flag_buf_ptr++ =
TTY_OVERRUN;
@@ -1204,8 +1263,8 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
tty->flip.count++;
*tty->flip.flag_buf_ptr++ =
TTY_NORMAL;
- *tty->flip.char_buf_ptr++ =
- base_addr[CyRDSR<<index];
+ *tty->flip.char_buf_ptr++ =
+ cy_readb(base_addr+(CyRDSR<<index));
}
/* These two conditions may imply */
/* a normal read should be done. */
@@ -1226,7 +1285,7 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
}
} else { /* normal character reception */
/* load # chars available from the chip */
- char_count = base_addr[CyRDCR<<index];
+ char_count = cy_readb(base_addr+(CyRDCR<<index));
#ifdef CYCLOM_ENABLE_MONITORING
++info->mon.int_count;
@@ -1240,7 +1299,7 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
break;
}
tty->flip.count++;
- data = base_addr[CyRDSR<<index];
+ data = cy_readb(base_addr+(CyRDSR<<index));
*tty->flip.flag_buf_ptr++ = TTY_NORMAL;
*tty->flip.char_buf_ptr++ = data;
#ifdef CYCLOM_16Y_HACK
@@ -1251,8 +1310,8 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
queue_task(&tty->flip.tqueue, &tq_timer);
}
/* end of service */
- base_addr[CyRIR<<index] = (save_xir & 0x3f);
- base_addr[CyCAR<<index] = (save_car);
+ cy_writeb((u_long)base_addr+(CyRIR<<index), (save_xir & 0x3f));
+ cy_writeb((u_long)base_addr+(CyCAR<<index), (save_car));
}
@@ -1260,23 +1319,28 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
/* Since we only get here when the transmit buffer
is empty, we know we can always stuff a dozen
characters. */
+#ifdef CY_DEBUG_INTERRUPTS
+printk("cy_interrupt: xmit intr, chip %d\n\r", chip);
+#endif
/* determine the channel & change to that context */
- save_xir = (u_char) base_addr[CyTIR<<index];
+ save_xir = (u_char) cy_readb(base_addr+(CyTIR<<index));
channel = (u_short ) (save_xir & CyIRChannel);
i = channel + chip * 4 + cinfo->first_line;
- save_car = base_addr[CyCAR<<index];
- base_addr[CyCAR<<index] = save_xir;
+ save_car = cy_readb(base_addr+(CyCAR<<index));
+ cy_writeb((u_long)base_addr+(CyCAR<<index), save_xir);
/* validate the port# (as configured and open) */
if( (i < 0) || (NR_PORTS <= i) ){
- base_addr[CySRER<<index] &= ~CyTxMpty;
+ cy_writeb((u_long)base_addr+(CySRER<<index),
+ cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty);
goto txend;
}
info = &cy_port[i];
info->last_active = jiffies;
if(info->tty == 0){
- base_addr[CySRER<<index] &= ~CyTxMpty;
+ cy_writeb((u_long)base_addr+(CySRER<<index),
+ cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty);
goto txdone;
}
@@ -1286,7 +1350,7 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
if(info->x_char) { /* send special char */
outch = info->x_char;
- base_addr[CyTDR<<index] = outch;
+ cy_writeb((u_long)base_addr+(CyTDR<<index), outch);
char_count--;
info->x_char = 0;
}
@@ -1300,29 +1364,44 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
PPR runs at 200 Hz, so the delay is
duration * 200/HZ, and thus a break can
run from 1/100 sec to about 5/4 sec.
+ For CD1400 J or later, replace the 200 Hz
+ by 500 Hz.
*/
- base_addr[CyTDR<<index] = 0; /* start break */
- base_addr[CyTDR<<index] = 0x81;
- base_addr[CyTDR<<index] = 0; /* delay a bit */
- base_addr[CyTDR<<index] = 0x82;
- base_addr[CyTDR<<index] = info->x_break*200/HZ;
- base_addr[CyTDR<<index] = 0; /* finish break */
- base_addr[CyTDR<<index] = 0x83;
+ /* start break */
+ cy_writeb((u_long)base_addr + (CyTDR<<index), 0);
+ cy_writeb((u_long)base_addr + (CyTDR<<index), 0x81);
+ /* delay a bit */
+ cy_writeb((u_long)base_addr + (CyTDR<<index), 0);
+ cy_writeb((u_long)base_addr + (CyTDR<<index), 0x82);
+ if (cy_readb(base_addr + (CyGFRCR<<index)) >= 0x48 ) {
+ /* It is a CD1400 rev. J or later */
+ cy_writeb((u_long)base_addr + (CyTDR<<index),
+ info->x_break*500/HZ);
+ } else {
+ cy_writeb((u_long)base_addr + (CyTDR<<index),
+ info->x_break*200/HZ);
+ }
+ /* finish break */
+ cy_writeb((u_long)base_addr + (CyTDR<<index), 0);
+ cy_writeb((u_long)base_addr + (CyTDR<<index), 0x83);
char_count -= 7;
info->x_break = 0;
}
while (char_count-- > 0){
if (!info->xmit_cnt){
- base_addr[CySRER<<index] &= ~CyTxMpty;
+ cy_writeb((u_long)base_addr+(CySRER<<index),
+ cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty);
goto txdone;
}
if (info->xmit_buf == 0){
- base_addr[CySRER<<index] &= ~CyTxMpty;
+ cy_writeb((u_long)base_addr+(CySRER<<index),
+ cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty);
goto txdone;
}
if (info->tty->stopped || info->tty->hw_stopped){
- base_addr[CySRER<<index] &= ~CyTxMpty;
+ cy_writeb((u_long)base_addr+(CySRER<<index),
+ cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty);
goto txdone;
}
/* Because the Embedded Transmit Commands have
@@ -1340,15 +1419,16 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
if( outch ){
info->xmit_cnt--;
info->xmit_tail = (info->xmit_tail + 1)
- & (PAGE_SIZE - 1);
- base_addr[CyTDR<<index] = outch;
+ & (SERIAL_XMIT_SIZE - 1);
+ cy_writeb((u_long)base_addr+(CyTDR<<index), outch);
}else{
if(char_count > 1){
info->xmit_cnt--;
info->xmit_tail = (info->xmit_tail + 1)
- & (PAGE_SIZE - 1);
- base_addr[CyTDR<<index] = outch;
- base_addr[CyTDR<<index] = 0;
+ & (SERIAL_XMIT_SIZE - 1);
+ cy_writeb((u_long)base_addr+(CyTDR<<index),
+ outch);
+ cy_writeb((u_long)base_addr+(CyTDR<<index), 0);
char_count--;
}else{
}
@@ -1362,23 +1442,24 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
txend:
/* end of service */
- base_addr[CyTIR<<index] = (save_xir & 0x3f);
- base_addr[CyCAR<<index] = (save_car);
+ cy_writeb((u_long)base_addr+(CyTIR<<index),
+ (save_xir & 0x3f));
+ cy_writeb((u_long)base_addr+(CyCAR<<index), (save_car));
}
if (status & CySRModem) { /* modem interrupt */
/* determine the channel & change to that context */
- save_xir = (u_char) base_addr[CyMIR<<index];
+ save_xir = (u_char) cy_readb(base_addr+(CyMIR<<index));
channel = (u_short ) (save_xir & CyIRChannel);
info = &cy_port[channel + chip * 4
+ cinfo->first_line];
info->last_active = jiffies;
- save_car = base_addr[CyCAR<<index];
- base_addr[CyCAR<<index] = save_xir;
+ save_car = cy_readb(base_addr+(CyCAR<<index));
+ cy_writeb((u_long)base_addr+(CyCAR<<index), save_xir);
- mdm_change = base_addr[CyMISR<<index];
- mdm_status = base_addr[CyMSVR1<<index];
+ mdm_change = cy_readb(base_addr+(CyMISR<<index));
+ mdm_status = cy_readb(base_addr+(CyMSVR1<<index));
if(info->tty == 0){/* no place for data, ignore it*/
;
@@ -1403,7 +1484,9 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
/* cy_start isn't used
because... !!! */
info->tty->hw_stopped = 0;
- base_addr[CySRER<<index] |= CyTxMpty;
+ cy_writeb((u_long)base_addr+(CySRER<<index),
+ cy_readb(base_addr+(CySRER<<index)) |
+ CyTxMpty);
cy_sched_event(info,
Cy_EVENT_WRITE_WAKEUP);
}
@@ -1412,8 +1495,9 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
/* cy_stop isn't used
because ... !!! */
info->tty->hw_stopped = 1;
- base_addr[CySRER<<index] &=
- ~CyTxMpty;
+ cy_writeb((u_long)base_addr+(CySRER<<index),
+ cy_readb(base_addr+(CySRER<<index)) &
+ ~CyTxMpty);
}
}
}
@@ -1423,15 +1507,16 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
}
}
/* end of service */
- base_addr[CyMIR<<index] = (save_xir & 0x3f);
- base_addr[CyCAR<<index] = save_car;
+ cy_writeb((u_long)base_addr+(CyMIR<<index),
+ (save_xir & 0x3f));
+ cy_writeb((u_long)base_addr+(CyCAR<<index), save_car);
}
} /* end while status != 0 */
} /* end loop for chips... */
} while(had_work);
/* clear interrupts */
- *(card_base_addr + (Cy_ClrIntr<<index)) = 0;
+ cy_writeb((u_long)card_base_addr + (Cy_ClrIntr<<index), 0);
/* Cy_ClrIntr is 0x1800 */
} /* cyy_interrupt */
@@ -1444,7 +1529,7 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
static int
cyz_fetch_msg( struct cyclades_card *cinfo,
- u_long *channel, u_char *cmd, u_long **param)
+ uclong *channel, ucchar *cmd, uclong *param)
{
struct FIRM_ID *firm_id;
struct ZFW_CTRL *zfw_ctrl;
@@ -1456,17 +1541,17 @@ cyz_fetch_msg( struct cyclades_card *cinfo,
return (-1);
}
zfw_ctrl = (struct ZFW_CTRL *)
- (cinfo->base_addr + firm_id->zfwctrl_addr);
+ (cinfo->base_addr + cy_readl(&firm_id->zfwctrl_addr));
board_ctrl = &zfw_ctrl->board_ctrl;
- loc_doorbell = ((struct RUNTIME_9060 *)
- (cinfo->ctl_addr))->loc_doorbell;
+ loc_doorbell = cy_readl(&((struct RUNTIME_9060 *)
+ (cinfo->ctl_addr))->loc_doorbell);
if (loc_doorbell){
*cmd = (char)(0xff & loc_doorbell);
- *channel = board_ctrl->fwcmd_channel;
- *param = board_ctrl->fwcmd_param;
- ((struct RUNTIME_9060 *)
- (cinfo->ctl_addr))->loc_doorbell = 0xffffffff;
+ *channel = cy_readl(&board_ctrl->fwcmd_channel);
+ *param = (uclong)cy_readl(&board_ctrl->fwcmd_param);
+ cy_writel(&((struct RUNTIME_9060 *)(cinfo->ctl_addr))->loc_doorbell,
+ 0xffffffff);
return 1;
}
return 0;
@@ -1475,12 +1560,12 @@ cyz_fetch_msg( struct cyclades_card *cinfo,
static int
cyz_issue_cmd( struct cyclades_card *cinfo,
- u_long channel, u_char cmd, u_long *param)
+ uclong channel, ucchar cmd, uclong param)
{
struct FIRM_ID *firm_id;
struct ZFW_CTRL *zfw_ctrl;
struct BOARD_CTRL *board_ctrl;
- volatile unsigned long *pci_doorbell;
+ volatile uclong *pci_doorbell;
int index;
firm_id = (struct FIRM_ID *)(cinfo->base_addr + ID_ADDRESS);
@@ -1488,21 +1573,21 @@ cyz_issue_cmd( struct cyclades_card *cinfo,
return (-1);
}
zfw_ctrl = (struct ZFW_CTRL *)
- (cinfo->base_addr + firm_id->zfwctrl_addr);
+ (cinfo->base_addr + cy_readl(&firm_id->zfwctrl_addr));
board_ctrl = &zfw_ctrl->board_ctrl;
index = 0;
- pci_doorbell = &((struct RUNTIME_9060 *)
- (cinfo->ctl_addr))->pci_doorbell;
- while( (*pci_doorbell & 0xff) != 0){
+ pci_doorbell = (uclong *)(&((struct RUNTIME_9060 *)
+ (cinfo->ctl_addr))->pci_doorbell);
+ while( (cy_readl(pci_doorbell) & 0xff) != 0){
if (index++ == 1000){
return(-1);
}
udelay(50L);
}
- board_ctrl->hcmd_channel = channel;
- board_ctrl->hcmd_param = param;
- *pci_doorbell = (long)cmd;
+ cy_writel((u_long)&board_ctrl->hcmd_channel, channel);
+ cy_writel((u_long)&board_ctrl->hcmd_param , param);
+ cy_writel((u_long)pci_doorbell, (long)cmd);
return(0);
} /* cyz_issue_cmd */
@@ -1521,11 +1606,11 @@ cyz_update_channel( struct cyclades_card *cinfo,
if (!ISZLOADED(*cinfo)){
return (-1);
}
- zfw_ctrl =
- (struct ZFW_CTRL *)(cinfo->base_addr + firm_id->zfwctrl_addr);
+ zfw_ctrl = (struct ZFW_CTRL *)
+ (cinfo->base_addr + cy_readl(&firm_id->zfwctrl_addr));
ch_ctrl = zfw_ctrl->ch_ctrl;
- ch_ctrl[channel].op_mode = (long)mode;
+ cy_writel(&ch_ctrl[channel].op_mode, (uclong)mode);
return cyz_issue_cmd(cinfo, channel, cmd, 0L);
@@ -1542,42 +1627,53 @@ cyz_interrupt(int irq, void *dev_id, struct pt_regs *regs)
static void
cyz_poll(unsigned long arg)
{
- struct FIRM_ID *firm_id;
- struct ZFW_CTRL *zfw_ctrl;
- struct BOARD_CTRL *board_ctrl;
- struct CH_CTRL *ch_ctrl;
- struct BUF_CTRL *buf_ctrl;
+ static volatile struct FIRM_ID *firm_id;
+ static volatile struct ZFW_CTRL *zfw_ctrl;
+ static volatile struct BOARD_CTRL *board_ctrl;
+ static volatile struct CH_CTRL *ch_ctrl;
+ static volatile struct BUF_CTRL *buf_ctrl;
struct cyclades_card *cinfo;
struct cyclades_port *info;
struct tty_struct *tty;
int card, port;
- int char_count, small_count;
+ int char_count;
+#ifdef BLOCKMOVE
+ int small_count;
+#endif
char data;
- u_long channel;
- u_char cmd;
- u_long *param;
- u_long hw_ver, fw_ver;
-
- cyz_timerlist.expires = jiffies + 100;
-
+ uclong channel;
+ ucchar cmd;
+ uclong param;
+ uclong hw_ver, fw_ver;
+ volatile uclong tx_put, tx_get, tx_bufsize;
+ volatile uclong rx_put, rx_get, rx_bufsize;
+
+ cyz_timerlist.expires = jiffies + (HZ);
for (card = 0 ; card < NR_CARDS ; card++){
cinfo = &cy_card[card];
if (!IS_CYC_Z(*cinfo)) continue;
- firm_id = (struct FIRM_ID *)
- (cinfo->base_addr + ID_ADDRESS);
- if (!ISZLOADED(*cinfo)){
+
+ firm_id = (struct FIRM_ID *)(cinfo->base_addr + ID_ADDRESS);
+ if (!ISZLOADED(*cinfo)) {
+ cinfo->inact_ctrl = 0;
continue;
}
- zfw_ctrl =
- (struct ZFW_CTRL *)
- (cinfo->base_addr + firm_id->zfwctrl_addr);
- board_ctrl = &zfw_ctrl->board_ctrl;
- fw_ver = board_ctrl->fw_version;
- hw_ver = ((struct RUNTIME_9060 *)(cinfo->ctl_addr))->mail_box_0;
+ zfw_ctrl = (struct ZFW_CTRL *)
+ (cinfo->base_addr + cy_readl(&firm_id->zfwctrl_addr));
+ board_ctrl = &(zfw_ctrl->board_ctrl);
+ fw_ver = cy_readl(&board_ctrl->fw_version);
+ hw_ver = cy_readl(&((struct RUNTIME_9060 *)
+ (cinfo->ctl_addr))->mail_box_0);
+
+ /* Enables the firmware inactivity control */
+ if ((fw_ver > 0x00000310L) && (!cinfo->inact_ctrl)) {
+ param = cyz_issue_cmd( &cy_card[card], 0L, C_CM_TINACT, 0L);
+ cinfo->inact_ctrl = 1;
+ }
- while( cyz_fetch_msg( cinfo, &channel, &cmd, &param) == 1){
+ while(cyz_fetch_msg(cinfo, &channel, &cmd, &param) == 1){
char_count = 0;
info = &cy_port[ channel + cinfo->first_line ];
if((tty = info->tty) == 0) continue;
@@ -1606,9 +1702,9 @@ cyz_poll(unsigned long arg)
break;
case C_CM_MDCD:
if (info->flags & ASYNC_CHECK_CD){
- if (((hw_ver != 0 || fw_ver > 241)
- ? ((u_long)param)
- : ch_ctrl[channel].rs_status) & C_RS_DCD) {
+ if ((fw_ver > 241 ?
+ ((u_long)param) :
+ cy_readl(&ch_ctrl[channel].rs_status)) & C_RS_DCD) {
/* SP("Open Wakeup\n"); */
cy_sched_event(info,
Cy_EVENT_OPEN_WAKEUP);
@@ -1625,7 +1721,7 @@ cyz_poll(unsigned long arg)
case C_CM_MCTS:
if (info->flags & ASYNC_CTS_FLOW) {
if(info->tty->hw_stopped){
- if( ch_ctrl[channel].rs_status & C_RS_DCD){
+ if( cy_readl(&ch_ctrl[channel].rs_status) & C_RS_DCD){
/* cy_start isn't used because...
HW flow is handled by the board */
/* SP("Write Wakeup\n"); */
@@ -1633,7 +1729,7 @@ cyz_poll(unsigned long arg)
Cy_EVENT_WRITE_WAKEUP);
}
}else{
- if(!(ch_ctrl[channel].rs_status & C_RS_CTS)){
+ if(!(cy_readl(&ch_ctrl[channel].rs_status) & C_RS_CTS)){
/* cy_stop isn't used because
HW flow is handled by the board */
/* SP("Write stop\n"); */
@@ -1653,14 +1749,25 @@ cyz_poll(unsigned long arg)
queue_task(&tty->flip.tqueue, &tq_timer);
}
}
-
- for (port = 0; port < board_ctrl->n_channel; port++){
+ for (port = 0; port < cy_readl(&board_ctrl->n_channel); port++){
info = &cy_port[ port + cinfo->first_line ];
tty = info->tty;
ch_ctrl = &(zfw_ctrl->ch_ctrl[port]);
buf_ctrl = &(zfw_ctrl->buf_ctrl[port]);
- if ((char_count = CHARS_IN_BUF(buf_ctrl))){
+/* Removed due to compilation problems in Alpha systems */
+// if ((char_count = CHARS_IN_BUF(buf_ctrl))){
+
+ rx_get = cy_readl(&buf_ctrl->rx_get);
+ rx_put = cy_readl(&buf_ctrl->rx_put);
+ rx_bufsize = cy_readl(&buf_ctrl->rx_bufsize);
+ if (rx_put >= rx_get)
+ char_count = rx_put - rx_get;
+ else
+ char_count = rx_put - rx_get + rx_bufsize;
+
+ if ( char_count ){
+
info->last_active = jiffies;
info->jiffies[1] = jiffies;
@@ -1673,9 +1780,7 @@ cyz_poll(unsigned long arg)
#endif
if( tty == 0){
/* flush received characters */
- buf_ctrl->rx_get =
- (buf_ctrl->rx_get + char_count)
- % buf_ctrl->rx_bufsize;
+ rx_get = (rx_get + char_count) & (rx_bufsize - 1);
/* SP("-"); */
info->rflush_count++;
}else{
@@ -1684,22 +1789,22 @@ cyz_poll(unsigned long arg)
for performance, but because of buffer boundaries, there
may be several steps to the operation */
while(0 < (small_count
- = cy_min( (buf_ctrl->rx_bufsize - buf_ctrl->rx_get),
- cy_min( (TTY_FLIPBUF_SIZE - tty->flip.count),
- char_count)))){
- memcpy(tty->flip.char_buf_ptr,
- (char *)(cinfo->base_addr
- + buf_ctrl->rx_bufaddr
- + buf_ctrl->rx_get),
- small_count);
+ = cy_min((rx_bufsize - rx_get),
+ cy_min((TTY_FLIPBUF_SIZE - tty->flip.count),
+ char_count)))){
+
+ memcpy_fromio(tty->flip.char_buf_ptr,
+ (char *)(cinfo->base_addr
+ + cy_readl(&buf_ctrl->rx_bufaddr)
+ + rx_get),
+ small_count);
+
tty->flip.char_buf_ptr += small_count;
memset(tty->flip.flag_buf_ptr,
TTY_NORMAL,
small_count);
tty->flip.flag_buf_ptr += small_count;
- buf_ctrl->rx_get =
- (buf_ctrl->rx_get + small_count)
- % buf_ctrl->rx_bufsize;
+ rx_get = (rx_get + small_count) & (rx_bufsize - 1);
char_count -= small_count;
tty->flip.count += small_count;
}
@@ -1708,13 +1813,9 @@ cyz_poll(unsigned long arg)
if (tty->flip.count >= TTY_FLIPBUF_SIZE){
break;
}
- data = *(char *) (cinfo->base_addr +
- buf_ctrl->rx_bufaddr +
- buf_ctrl->rx_get);
- buf_ctrl->rx_get =
- (buf_ctrl->rx_get + 1)
- % buf_ctrl->rx_bufsize;
-
+ data = cy_readb(cinfo->base_addr +
+ cy_readl(&buf_ctrl->rx_bufaddr) + rx_get);
+ rx_get = (rx_get + 1) & (rx_bufsize - 1);
tty->flip.count++;
*tty->flip.flag_buf_ptr++ = TTY_NORMAL;
*tty->flip.char_buf_ptr++ = data;
@@ -1722,22 +1823,33 @@ cyz_poll(unsigned long arg)
#endif
queue_task(&tty->flip.tqueue, &tq_timer);
}
+ /* Update rx_get */
+ cy_writel(&buf_ctrl->rx_get, rx_get);
}
- if ((char_count = SPACE_IN_BUF(buf_ctrl))){
- if( tty == 0){
+/* Removed due to compilation problems in Alpha systems */
+// if ((char_count = SPACE_IN_BUF(buf_ctrl))){
+
+ tx_get = cy_readl(&buf_ctrl->tx_get);
+ tx_put = cy_readl(&buf_ctrl->tx_put);
+ tx_bufsize = cy_readl(&buf_ctrl->tx_bufsize);
+ if (tx_put >= tx_get)
+ char_count = tx_get - tx_put - 1 + tx_bufsize;
+ else
+ char_count = tx_get - tx_put - 1;
+
+ if ( char_count ){
+
+ if( tty == 0 ){
goto ztxdone;
}
if(info->x_char) { /* send special char */
data = info->x_char;
- *(char *) (cinfo->base_addr +
- buf_ctrl->tx_bufaddr +
- buf_ctrl->tx_put) = data;
- buf_ctrl->tx_put =
- (buf_ctrl->tx_put + 1)
- % buf_ctrl->tx_bufsize;
+ cy_writeb((cinfo->base_addr +
+ cy_readl(&buf_ctrl->tx_bufaddr) + tx_put), data);
+ tx_put = (tx_put + 1) & (tx_bufsize - 1);
info->x_char = 0;
char_count--;
info->last_active = jiffies;
@@ -1751,21 +1863,20 @@ cyz_poll(unsigned long arg)
}
#ifdef BLOCKMOVE
while(0 < (small_count
- = cy_min( (buf_ctrl->tx_bufsize - buf_ctrl->tx_put),
- cy_min ( (PAGE_SIZE - info->xmit_tail),
- cy_min( info->xmit_cnt, char_count))))){
- memcpy((char *)(cinfo->base_addr
- + buf_ctrl->tx_bufaddr
- + buf_ctrl->tx_put),
- &info->xmit_buf[info->xmit_tail],
- small_count);
- buf_ctrl->tx_put =
- (buf_ctrl->tx_put + small_count)
- % buf_ctrl->tx_bufsize;
+ = cy_min((tx_bufsize - tx_put),
+ cy_min ((SERIAL_XMIT_SIZE - info->xmit_tail),
+ cy_min(info->xmit_cnt, char_count))))){
+
+ memcpy_toio((char *)(cinfo->base_addr
+ + cy_readl(&buf_ctrl->tx_bufaddr) + tx_put),
+ &info->xmit_buf[info->xmit_tail],
+ small_count);
+
+ tx_put = (tx_put + small_count) & (tx_bufsize - 1);
char_count -= small_count;
info->xmit_cnt -= small_count;
info->xmit_tail =
- (info->xmit_tail + small_count) & (PAGE_SIZE - 1);
+ (info->xmit_tail + small_count) & (SERIAL_XMIT_SIZE - 1);
info->last_active = jiffies;
info->jiffies[2] = jiffies;
}
@@ -1774,28 +1885,33 @@ cyz_poll(unsigned long arg)
data = info->xmit_buf[info->xmit_tail];
info->xmit_cnt--;
info->xmit_tail =
- (info->xmit_tail + 1) & (PAGE_SIZE - 1);
-
- *(char *) (cinfo->base_addr +
- buf_ctrl->tx_bufaddr +
- buf_ctrl->tx_put) = data;
- buf_ctrl->tx_put =
- (buf_ctrl->tx_put + 1)
- % buf_ctrl->tx_bufsize;
+ (info->xmit_tail + 1) & (SERIAL_XMIT_SIZE - 1);
+
+ cy_writeb(cinfo->base_addr +
+ cy_readl(&buf_ctrl->tx_bufaddr) + tx_put,
+ data);
+ tx_put = (tx_put + 1) & (tx_bufsize - 1);
char_count--;
info->last_active = jiffies;
info->jiffies[2] = jiffies;
}
+
#endif
ztxdone:
if (info->xmit_cnt < WAKEUP_CHARS) {
cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP);
}
+ /* Update tx_put */
+ cy_writel(&buf_ctrl->tx_put, tx_put);
}
}
-
/* poll every 40 ms */
- cyz_timerlist.expires = jiffies + 4;
+ cyz_timerlist.expires = jiffies + cyz_polling_cycle;
+
+ /* refresh inactivity counter */
+ if (cinfo->inact_ctrl) {
+ cy_writel(&board_ctrl->inactivity, (uclong) ZF_TINACT);
+ }
}
add_timer(&cyz_timerlist);
@@ -1851,26 +1967,27 @@ startup(struct cyclades_port * info)
#endif
save_flags(flags); cli();
- base_addr[CyCAR<<index] = (u_char)channel;
+ cy_writeb((ulong)base_addr+(CyCAR<<index), (u_char)channel);
- base_addr[CyRTPR<<index] = (info->default_timeout
+ cy_writeb((ulong)base_addr+(CyRTPR<<index), (info->default_timeout
? info->default_timeout
- : 0x02); /* 10ms rx timeout */
+ : 0x02)); /* 10ms rx timeout */
cyy_issue_cmd(base_addr,CyCHAN_CTL|CyENB_RCVR|CyENB_XMTR,index);
- base_addr[CyCAR<<index] =
- (u_char)channel; /* !!! Is this needed? */
- base_addr[CyMSVR1<<index] = CyRTS;
- base_addr[CyMSVR2<<index] = CyDTR;
+ cy_writeb((ulong)base_addr+(CyCAR<<index), (u_char)channel);
+ cy_writeb((ulong)base_addr+(CyMSVR1<<index), CyRTS);
+ cy_writeb((ulong)base_addr+(CyMSVR2<<index), CyDTR);
#ifdef SERIAL_DEBUG_DTR
printk("cyc:startup raising DTR\n");
printk(" status: 0x%x, 0x%x\n",
- base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]);
+ cy_readb(base_addr+(CyMSVR1<<index)),
+ cy_readb(base_addr+(CyMSVR2<<index)));
#endif
- base_addr[CySRER<<index] |= CyRxData;
+ cy_writeb((u_long)base_addr+(CySRER<<index),
+ cy_readb(base_addr+(CySRER<<index)) | CyRxData);
info->flags |= ASYNC_INITIALIZED;
if (info->tty){
@@ -1894,7 +2011,7 @@ startup(struct cyclades_port * info)
zfw_ctrl =
(struct ZFW_CTRL *)
- (cy_card[card].base_addr + firm_id->zfwctrl_addr);
+ (cy_card[card].base_addr + cy_readl(&firm_id->zfwctrl_addr));
board_ctrl = &zfw_ctrl->board_ctrl;
ch_ctrl = zfw_ctrl->ch_ctrl;
@@ -1903,8 +2020,8 @@ startup(struct cyclades_port * info)
card, channel, (long)base_addr);/**/
#endif
- ch_ctrl[channel].op_mode = C_CH_ENABLE;
- ch_ctrl[channel].intr_enable = C_IN_MDCD|C_IN_MCTS;
+ cy_writel(&ch_ctrl[channel].op_mode, C_CH_ENABLE);
+ cy_writel(&ch_ctrl[channel].intr_enable, C_IN_MDCD|C_IN_MCTS);
retval = cyz_issue_cmd( &cy_card[card],
channel, C_CM_IOCTL, 0L); /* was C_CM_RESET */
if (retval != 0){
@@ -1913,8 +2030,8 @@ startup(struct cyclades_port * info)
/* set timeout !!! */
/* set RTS and DTR !!! */
- ch_ctrl[channel].rs_control |=
- C_RS_RTS | C_RS_DTR ;
+ cy_writel(&ch_ctrl[channel].rs_control,
+ cy_readl(&ch_ctrl[channel].rs_control) | C_RS_RTS | C_RS_DTR) ;
retval = cyz_issue_cmd(&cy_card[info->card],
channel, C_CM_IOCTLM, 0L);
if (retval != 0){
@@ -1959,8 +2076,9 @@ start_xmit( struct cyclades_port *info )
+ (cy_chip_offset[chip]<<index));
save_flags(flags); cli();
- base_addr[CyCAR<<index] = channel;
- base_addr[CySRER<<index] |= CyTxMpty;
+ cy_writeb((u_long)base_addr+(CyCAR<<index), channel);
+ cy_writeb((u_long)base_addr+(CySRER<<index),
+ cy_readb(base_addr+(CySRER<<index)) | CyTxMpty);
restore_flags(flags);
} else {
/* Don't have to do anything at this time */
@@ -2012,14 +2130,15 @@ shutdown(struct cyclades_port * info)
free_page((unsigned long) temp);
}
- base_addr[CyCAR<<index] = (u_char)channel;
+ cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
- base_addr[CyMSVR1<<index] = ~CyRTS;
- base_addr[CyMSVR2<<index] = ~CyDTR;
+ cy_writeb((u_long)base_addr+(CyMSVR1<<index), ~CyRTS);
+ cy_writeb((u_long)base_addr+(CyMSVR2<<index), ~CyDTR);
#ifdef SERIAL_DEBUG_DTR
printk("cyc shutdown dropping DTR\n");
printk(" status: 0x%x, 0x%x\n",
- base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]);
+ cy_readb(base_addr+(CyMSVR1<<index)),
+ cy_readb(base_addr+(CyMSVR2<<index)));
#endif
}
cyy_issue_cmd(base_addr,CyCHAN_CTL|CyDIS_RCVR,index);
@@ -2051,10 +2170,11 @@ shutdown(struct cyclades_port * info)
zfw_ctrl =
(struct ZFW_CTRL *)
- (cy_card[card].base_addr + firm_id->zfwctrl_addr);
- board_ctrl = &zfw_ctrl->board_ctrl;
+ (cy_card[card].base_addr + cy_readl(&firm_id->zfwctrl_addr));
+ board_ctrl = &(zfw_ctrl->board_ctrl);
ch_ctrl = zfw_ctrl->ch_ctrl;
+
save_flags(flags); cli();
if (info->xmit_buf){
unsigned char * temp;
@@ -2063,8 +2183,9 @@ shutdown(struct cyclades_port * info)
free_page((unsigned long) temp);
}
if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
- ch_ctrl[channel].rs_control &=
- ~(C_RS_RTS | C_RS_DTR );
+ cy_writel((u_long)&ch_ctrl[channel].rs_control,
+ (uclong)(cy_readl(&ch_ctrl[channel].rs_control) &
+ ~(C_RS_RTS | C_RS_DTR)));
retval = cyz_issue_cmd(&cy_card[info->card],
channel, C_CM_IOCTLM, 0L);
if (retval != 0){
@@ -2187,13 +2308,14 @@ block_til_ready(struct tty_struct *tty, struct file * filp,
while (1) {
save_flags(flags); cli();
if (!(info->flags & ASYNC_CALLOUT_ACTIVE)){
- base_addr[CyCAR<<index] = (u_char)channel;
- base_addr[CyMSVR1<<index] = CyRTS;
- base_addr[CyMSVR2<<index] = CyDTR;
+ cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+ cy_writeb((u_long)base_addr+(CyMSVR1<<index), CyRTS);
+ cy_writeb((u_long)base_addr+(CyMSVR2<<index), CyDTR);
#ifdef SERIAL_DEBUG_DTR
printk("cyc:block_til_ready raising DTR\n");
printk(" status: 0x%x, 0x%x\n",
- base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]);
+ cy_readb(base_addr+(CyMSVR1<<index)),
+ cy_readb(base_addr+(CyMSVR2<<index)));
#endif
}
restore_flags(flags);
@@ -2208,11 +2330,11 @@ block_til_ready(struct tty_struct *tty, struct file * filp,
break;
}
save_flags(flags); cli();
- base_addr[CyCAR<<index] = (u_char)channel;
+ cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
if (!(info->flags & ASYNC_CALLOUT_ACTIVE)
&& !(info->flags & ASYNC_CLOSING)
&& (C_CLOCAL(tty)
- || (base_addr[CyMSVR1<<index] & CyDCD))) {
+ || (cy_readb(base_addr+(CyMSVR1<<index)) & CyDCD))) {
restore_flags(flags);
break;
}
@@ -2243,13 +2365,13 @@ block_til_ready(struct tty_struct *tty, struct file * filp,
zfw_ctrl =
(struct ZFW_CTRL *)
- (base_addr + firm_id->zfwctrl_addr);
+ (base_addr + cy_readl(&firm_id->zfwctrl_addr));
board_ctrl = &zfw_ctrl->board_ctrl;
ch_ctrl = zfw_ctrl->ch_ctrl;
while (1) {
- ch_ctrl[channel].rs_control |=
- C_RS_RTS | C_RS_DTR ;
+ cy_writel(&ch_ctrl[channel].rs_control,
+ cy_readl(&ch_ctrl[channel].rs_control) | C_RS_RTS | C_RS_DTR);
retval = cyz_issue_cmd(&cy_card[info->card],
channel, C_CM_IOCTLM, 0L);
if (retval != 0){
@@ -2272,7 +2394,7 @@ block_til_ready(struct tty_struct *tty, struct file * filp,
if (!(info->flags & ASYNC_CALLOUT_ACTIVE)
&& !(info->flags & ASYNC_CLOSING)
&& (C_CLOCAL(tty)
- || (ch_ctrl[channel].rs_status & C_RS_DCD))) {
+ || (cy_readl(&ch_ctrl[channel].rs_status) & C_RS_DCD))) {
break;
}
if (current->signal & ~current->blocked) {
@@ -2332,11 +2454,11 @@ cy_open(struct tty_struct *tty, struct file * filp)
*/
if (IS_CYC_Z(cy_card[info->card])) {
if (!ISZLOADED(cy_card[info->card])) {
- if (((ZE_V1 ==((struct RUNTIME_9060 *)
- ((cy_card[info->card]).ctl_addr))->mail_box_0) &&
+ if (((ZE_V1 ==cy_readl(&((struct RUNTIME_9060 *)
+ ((cy_card[info->card]).ctl_addr))->mail_box_0)) &&
Z_FPGA_CHECK(cy_card[info->card])) &&
- (ZFIRM_HLT==((struct FIRM_ID *)
- ((cy_card[info->card]).base_addr+ID_ADDRESS))->signature))
+ (ZFIRM_HLT==cy_readl(&((struct FIRM_ID *)
+ ((cy_card[info->card]).base_addr+ID_ADDRESS))->signature)))
{
printk ("Cyclades-Z Error: you need an external power supply for this number of ports.\n\rFirmware halted.\r\n");
} else {
@@ -2403,6 +2525,7 @@ cy_open(struct tty_struct *tty, struct file * filp)
#ifdef SERIAL_DEBUG_OPEN
printk(" cyc:cy_open done\n");/**/
#endif
+
return 0;
} /* cy_open */
@@ -2603,13 +2726,13 @@ cy_put_char(struct tty_struct *tty, unsigned char ch)
return;
save_flags(flags); cli();
- if (info->xmit_cnt >= PAGE_SIZE - 1) {
+ if (info->xmit_cnt >= SERIAL_XMIT_SIZE - 1) {
restore_flags(flags);
return;
}
info->xmit_buf[info->xmit_head++] = ch;
- info->xmit_head &= PAGE_SIZE - 1;
+ info->xmit_head &= SERIAL_XMIT_SIZE - 1;
info->xmit_cnt++;
restore_flags(flags);
#if 0
@@ -2652,8 +2775,9 @@ cy_flush_chars(struct tty_struct *tty)
+ (cy_chip_offset[chip]<<index));
save_flags(flags); cli();
- base_addr[CyCAR<<index] = channel;
- base_addr[CySRER<<index] |= CyTxMpty;
+ cy_writeb((u_long)base_addr+(CyCAR<<index), channel);
+ cy_writeb((u_long)base_addr+(CySRER<<index),
+ cy_readb(base_addr+(CySRER<<index)) | CyTxMpty);
restore_flags(flags);
} else {
/* Since polling is already in place,
@@ -2680,7 +2804,7 @@ cy_write_room(struct tty_struct *tty)
if (serial_paranoia_check(info, tty->device, "cy_write_room"))
return 0;
- ret = PAGE_SIZE - info->xmit_cnt - 1;
+ ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1;
if (ret < 0)
ret = 0;
return ret;
@@ -2724,6 +2848,7 @@ set_line_char(struct cyclades_port * info)
unsigned cflag;
int i;
+
if (!info->tty || !info->tty->termios){
return;
}
@@ -2736,6 +2861,9 @@ set_line_char(struct cyclades_port * info)
channel = (info->line) - (cy_card[card].first_line);
if (!IS_CYC_Z(cy_card[card])) {
+
+ index = cy_card[card].bus_index;
+
/* baud rate */
i = cflag & CBAUD;
@@ -2744,6 +2872,11 @@ set_line_char(struct cyclades_port * info)
i = 16;
else if(i == B115200)
i = 18;
+ else if(i == B230400 &&
+ cy_readb(cy_card[card].base_addr+(CyGFRCR<<index)) >= 0x48) {
+ /* It is a CD1400 rev. J or later */
+ i = 20;
+ }
#ifdef B76800
else if(i == B76800)
i = 17;
@@ -2753,15 +2886,35 @@ set_line_char(struct cyclades_port * info)
}
if (i == 15) {
- if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
- i += 1;
- if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
- i += 3;
+ if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+ i += 1;
+ if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+ i += 3;
+ if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST){
+ switch(info->baud) {
+ case 57600:
+ i += 1; break;
+ case 115200:
+ i += 3; break;
+ case 230400:
+ i += 5; break;
+ default:
+ break;
+ }
+ }
+ }
+ if(cy_readb(cy_card[card].base_addr+(CyGFRCR<<index)) >= 0x48) {
+ /* It is a CD1400 rev. J or later */
+ info->tbpr = baud_bpr_60[i]; /* Tx BPR */
+ info->tco = baud_co_60[i]; /* Tx CO */
+ info->rbpr = baud_bpr_60[i]; /* Rx BPR */
+ info->rco = baud_co_60[i]; /* Rx CO */
+ } else {
+ info->tbpr = baud_bpr_25[i]; /* Tx BPR */
+ info->tco = baud_co_25[i]; /* Tx CO */
+ info->rbpr = baud_bpr_25[i]; /* Rx BPR */
+ info->rco = baud_co_25[i]; /* Rx CO */
}
- info->tbpr = baud_bpr[i]; /* Tx BPR */
- info->tco = baud_co[i]; /* Tx CO */
- info->rbpr = baud_bpr[i]; /* Rx BPR */
- info->rco = baud_co[i]; /* Rx CO */
if (baud_table[i] == 134) {
info->timeout = (info->xmit_fifo_size*HZ*30/269) + 2;
/* get it right for 134.5 baud */
@@ -2834,70 +2987,94 @@ set_line_char(struct cyclades_port * info)
chip = channel>>2;
channel &= 0x03;
- index = cy_card[card].bus_index;
base_addr = (unsigned char*)
(cy_card[card].base_addr
+ (cy_chip_offset[chip]<<index));
save_flags(flags); cli();
- base_addr[CyCAR<<index] = (u_char)channel;
+ cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
/* tx and rx baud rate */
- base_addr[CyTCOR<<index] = info->tco;
- base_addr[CyTBPR<<index] = info->tbpr;
- base_addr[CyRCOR<<index] = info->rco;
- base_addr[CyRBPR<<index] = info->rbpr;
+ cy_writeb((u_long)base_addr+(CyTCOR<<index), info->tco);
+ cy_writeb((u_long)base_addr+(CyTBPR<<index), info->tbpr);
+ cy_writeb((u_long)base_addr+(CyRCOR<<index), info->rco);
+ cy_writeb((u_long)base_addr+(CyRBPR<<index), info->rbpr);
/* set line characteristics according configuration */
- base_addr[CySCHR1<<index] = START_CHAR(info->tty);
- base_addr[CySCHR2<<index] = STOP_CHAR(info->tty);
- base_addr[CyCOR1<<index] = info->cor1;
- base_addr[CyCOR2<<index] = info->cor2;
- base_addr[CyCOR3<<index] = info->cor3;
- base_addr[CyCOR4<<index] = info->cor4;
- base_addr[CyCOR5<<index] = info->cor5;
+ cy_writeb((u_long)base_addr+(CySCHR1<<index),
+ START_CHAR(info->tty));
+ cy_writeb((u_long)base_addr+(CySCHR2<<index),
+ STOP_CHAR(info->tty));
+ cy_writeb((u_long)base_addr+(CyCOR1<<index), info->cor1);
+ cy_writeb((u_long)base_addr+(CyCOR2<<index), info->cor2);
+ cy_writeb((u_long)base_addr+(CyCOR3<<index), info->cor3);
+ cy_writeb((u_long)base_addr+(CyCOR4<<index), info->cor4);
+ cy_writeb((u_long)base_addr+(CyCOR5<<index), info->cor5);
cyy_issue_cmd(base_addr,
CyCOR_CHANGE|CyCOR1ch|CyCOR2ch|CyCOR3ch,index);
- base_addr[CyCAR<<index] =
- (u_char)channel; /* !!! Is this needed? */
-
- base_addr[CyRTPR<<index] = (info->default_timeout
- ? info->default_timeout
- : 0x02); /* 10ms rx timeout */
+ cy_writeb((u_long)base_addr+(CyCAR<<index),
+ (u_char)channel); /* !!! Is this needed? */
+ cy_writeb((u_long)base_addr+(CyRTPR<<index), (info->default_timeout
+ ? info->default_timeout
+ : 0x02)); /* 10ms rx timeout */
if (C_CLOCAL(info->tty)) {
- base_addr[CySRER<<index] |= CyMdmCh; /* without modem intr */
+ /* without modem intr */
+ cy_writeb((u_long)base_addr+(CySRER<<index),
+ cy_readb(base_addr+(CySRER<<index)) | CyMdmCh);
/* act on 1->0 modem transitions */
- base_addr[CyMCOR1<<index] = CyCTS;
+ if ((cflag & CRTSCTS) && info->rflow) {
+ cy_writeb((u_long)base_addr+(CyMCOR1<<index),
+ (CyCTS|rflow_thr[i]));
+ } else {
+ cy_writeb((u_long)base_addr+(CyMCOR1<<index), CyCTS);
+ }
/* act on 0->1 modem transitions */
- base_addr[CyMCOR2<<index] = CyCTS;
+ cy_writeb((u_long)base_addr+(CyMCOR2<<index), CyCTS);
} else {
- base_addr[CySRER<<index] |= CyMdmCh; /* with modem intr */
+ /* without modem intr */
+ cy_writeb((u_long)base_addr+(CySRER<<index),
+ cy_readb(base_addr+(CySRER<<index)) | CyMdmCh);
/* act on 1->0 modem transitions */
- base_addr[CyMCOR1<<index] = CyDSR|CyCTS|CyRI|CyDCD;
+ if ((cflag & CRTSCTS) && info->rflow) {
+ cy_writeb((u_long)base_addr+(CyMCOR1<<index),
+ (CyDSR|CyCTS|CyRI|CyDCD|rflow_thr[i]));
+ } else {
+ cy_writeb((u_long)base_addr+(CyMCOR1<<index),
+ CyDSR|CyCTS|CyRI|CyDCD);
+ }
/* act on 0->1 modem transitions */
- base_addr[CyMCOR2<<index] = CyDSR|CyCTS|CyRI|CyDCD;
+ cy_writeb((u_long)base_addr+(CyMCOR2<<index),
+ CyDSR|CyCTS|CyRI|CyDCD);
}
if(i == 0){ /* baud rate is zero, turn off line */
- base_addr[CyMSVR2<<index] = ~CyDTR;
+ if (info->rtsdtr_inv) {
+ cy_writeb((u_long)base_addr+(CyMSVR1<<index), ~CyRTS);
+ } else {
+ cy_writeb((u_long)base_addr+(CyMSVR2<<index), ~CyDTR);
+ }
#ifdef SERIAL_DEBUG_DTR
printk("cyc:set_line_char dropping DTR\n");
printk(" status: 0x%x,
- 0x%x\n", base_addr[CyMSVR1<<index],
- base_addr[CyMSVR2<<index]);
+ 0x%x\n", cy_readb(base_addr+(CyMSVR1<<index)),
+ cy_readb(base_addr+(CyMSVR2<<index)));
#endif
}else{
- base_addr[CyMSVR2<<index] = CyDTR;
+ if (info->rtsdtr_inv) {
+ cy_writeb((u_long)base_addr+(CyMSVR1<<index), CyRTS);
+ } else {
+ cy_writeb((u_long)base_addr+(CyMSVR2<<index), CyDTR);
+ }
#ifdef SERIAL_DEBUG_DTR
printk("cyc:set_line_char raising DTR\n");
printk(" status: 0x%x, 0x%x\n",
- base_addr[CyMSVR1<<index],
- base_addr[CyMSVR2<<index]);
+ cy_readb(base_addr+(CyMSVR1<<index)),
+ cy_readb(base_addr+(CyMSVR2<<index)));
#endif
}
@@ -2920,82 +3097,84 @@ set_line_char(struct cyclades_port * info)
return;
}
- zfw_ctrl =
- (struct ZFW_CTRL *)
- (cy_card[card].base_addr + firm_id->zfwctrl_addr);
+ zfw_ctrl = (struct ZFW_CTRL *)
+ (cy_card[card].base_addr + cy_readl(&firm_id->zfwctrl_addr));
board_ctrl = &zfw_ctrl->board_ctrl;
- ch_ctrl = &zfw_ctrl->ch_ctrl[channel];
+ ch_ctrl = &(zfw_ctrl->ch_ctrl[channel]);
buf_ctrl = &zfw_ctrl->buf_ctrl[channel];
/* baud rate */
switch(i = cflag & CBAUD){
/*
- case B0: ch_ctrl->comm_baud = 0; break;
+ case B0: cy_writel(&ch_ctrl->comm_baud , 0); break;
*/
- case B50: ch_ctrl->comm_baud = 50; break;
- case B75: ch_ctrl->comm_baud = 75; break;
- case B110: ch_ctrl->comm_baud = 110; break;
- case B134: ch_ctrl->comm_baud = 134; break;
- case B150: ch_ctrl->comm_baud = 150; break;
- case B200: ch_ctrl->comm_baud = 200; break;
- case B300: ch_ctrl->comm_baud = 300; break;
- case B600: ch_ctrl->comm_baud = 600; break;
- case B1200: ch_ctrl->comm_baud = 1200; break;
- case B1800: ch_ctrl->comm_baud = 1800; break;
- case B2400: ch_ctrl->comm_baud = 2400; break;
- case B4800: ch_ctrl->comm_baud = 4800; break;
- case B9600: ch_ctrl->comm_baud = 9600; break;
- case B19200: ch_ctrl->comm_baud = 19200; break;
+ case B50: cy_writel(&ch_ctrl->comm_baud , 50); break;
+ case B75: cy_writel(&ch_ctrl->comm_baud , 75); break;
+ case B110: cy_writel(&ch_ctrl->comm_baud , 110); break;
+ case B134: cy_writel(&ch_ctrl->comm_baud , 134); break;
+ case B150: cy_writel(&ch_ctrl->comm_baud , 150); break;
+ case B200: cy_writel(&ch_ctrl->comm_baud , 200); break;
+ case B300: cy_writel(&ch_ctrl->comm_baud , 300); break;
+ case B600: cy_writel(&ch_ctrl->comm_baud , 600); break;
+ case B1200: cy_writel(&ch_ctrl->comm_baud , 1200); break;
+ case B1800: cy_writel(&ch_ctrl->comm_baud , 1800); break;
+ case B2400: cy_writel(&ch_ctrl->comm_baud , 2400); break;
+ case B4800: cy_writel(&ch_ctrl->comm_baud , 4800); break;
+ case B9600: cy_writel(&ch_ctrl->comm_baud , 9600); break;
+ case B19200: cy_writel(&ch_ctrl->comm_baud , 19200); break;
case B38400:
if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI){
- ch_ctrl->comm_baud = 57600;
+ cy_writel(&ch_ctrl->comm_baud , 57600);
}else if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI){
- ch_ctrl->comm_baud = 115200;
+ cy_writel(&ch_ctrl->comm_baud , 115200);
+ }else if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST){
+ cy_writel(&ch_ctrl->comm_baud , info->baud);
}else{
- ch_ctrl->comm_baud = 38400;
+ cy_writel(&ch_ctrl->comm_baud , 38400);
}
break;
- case B57600: ch_ctrl->comm_baud = 57600; break;
+ case B57600: cy_writel(&ch_ctrl->comm_baud , 57600); break;
#ifdef B76800
- case B76800: ch_ctrl->comm_baud = 76800; break;
+ case B76800: cy_writel(&ch_ctrl->comm_baud , 76800); break;
#endif
- case B115200: ch_ctrl->comm_baud = 115200; break;
- case B230400: ch_ctrl->comm_baud = 230400; break;
- case B460800: ch_ctrl->comm_baud = 460800; break;
+ case B115200: cy_writel(&ch_ctrl->comm_baud , 115200); break;
+ case B230400: cy_writel(&ch_ctrl->comm_baud , 230400); break;
+ case B460800: cy_writel(&ch_ctrl->comm_baud , 460800); break;
}
- if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST){
- ch_ctrl->comm_baud = info->baud;
- }
/* byte size and parity */
switch(cflag & CSIZE){
- case CS5: ch_ctrl->comm_data_l = C_DL_CS5; break;
- case CS6: ch_ctrl->comm_data_l = C_DL_CS6; break;
- case CS7: ch_ctrl->comm_data_l = C_DL_CS7; break;
- case CS8: ch_ctrl->comm_data_l = C_DL_CS8; break;
+ case CS5: cy_writel(&ch_ctrl->comm_data_l , C_DL_CS5); break;
+ case CS6: cy_writel(&ch_ctrl->comm_data_l , C_DL_CS6); break;
+ case CS7: cy_writel(&ch_ctrl->comm_data_l , C_DL_CS7); break;
+ case CS8: cy_writel(&ch_ctrl->comm_data_l , C_DL_CS8); break;
}
if(cflag & CSTOPB){
- ch_ctrl->comm_data_l |= C_DL_2STOP;
+ cy_writel(&ch_ctrl->comm_data_l,
+ cy_readl(&ch_ctrl->comm_data_l) | C_DL_2STOP);
}else{
- ch_ctrl->comm_data_l |= C_DL_1STOP;
+ cy_writel(&ch_ctrl->comm_data_l,
+ cy_readl(&ch_ctrl->comm_data_l) | C_DL_1STOP);
}
if (cflag & PARENB){
if (cflag & PARODD){
- ch_ctrl->comm_parity = C_PR_ODD;
+ cy_writel(&ch_ctrl->comm_parity , C_PR_ODD);
}else{
- ch_ctrl->comm_parity = C_PR_EVEN;
+ cy_writel(&ch_ctrl->comm_parity , C_PR_EVEN);
}
}else{
- ch_ctrl->comm_parity = C_PR_NONE;
+ cy_writel(&ch_ctrl->comm_parity , C_PR_NONE);
}
/* CTS flow control flag */
if (cflag & CRTSCTS){
info->flags |= ASYNC_CTS_FLOW;
- ch_ctrl->hw_flow |= C_RS_CTS | C_RS_RTS;
+ cy_writel(&ch_ctrl->hw_flow,
+ cy_readl(&ch_ctrl->hw_flow) | C_RS_CTS | C_RS_RTS);
}else{
info->flags &= ~ASYNC_CTS_FLOW;
- ch_ctrl->hw_flow &= ~(C_RS_CTS | C_RS_RTS);
+ cy_writel(&ch_ctrl->hw_flow,
+ cy_readl(&ch_ctrl->hw_flow) & ~(C_RS_CTS | C_RS_RTS));
}
retval = cyz_issue_cmd( &cy_card[card], channel, C_CM_IOCTL, 0L);
@@ -3012,12 +3191,14 @@ set_line_char(struct cyclades_port * info)
}
if(i == 0){ /* baud rate is zero, turn off line */
- ch_ctrl->rs_control &= ~C_RS_DTR;
+ cy_writel(&ch_ctrl->rs_control,
+ cy_readl(&ch_ctrl->rs_control) & ~C_RS_DTR);
#ifdef SERIAL_DEBUG_DTR
printk("cyc:set_line_char dropping Z DTR\n");
#endif
}else{
- ch_ctrl->rs_control |= C_RS_DTR;
+ cy_writel(&ch_ctrl->rs_control,
+ cy_readl(&ch_ctrl->rs_control) | C_RS_DTR);
#ifdef SERIAL_DEBUG_DTR
printk("cyc:set_line_char raising Z DTR\n");
#endif
@@ -3028,6 +3209,7 @@ set_line_char(struct cyclades_port * info)
printk("cyc:set_line_char retval at %d was %x\n",
__LINE__, retval);
}
+ cy_readl(&ch_ctrl->comm_baud);
if (info->tty){
clear_bit(TTY_IO_ERROR, &info->tty->flags);
@@ -3131,9 +3313,9 @@ get_modem_info(struct cyclades_port * info, unsigned int *value)
+ (cy_chip_offset[chip]<<index));
save_flags(flags); cli();
- base_addr[CyCAR<<index] = (u_char)channel;
- status = base_addr[CyMSVR1<<index];
- status |= base_addr[CyMSVR2<<index];
+ cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+ status = cy_readb(base_addr+(CyMSVR1<<index));
+ status |= cy_readb(base_addr+(CyMSVR2<<index));
restore_flags(flags);
result = ((status & CyRTS) ? TIOCM_RTS : 0)
@@ -3152,12 +3334,11 @@ get_modem_info(struct cyclades_port * info, unsigned int *value)
firm_id = (struct FIRM_ID *)
(cy_card[card].base_addr + ID_ADDRESS);
if (ISZLOADED(cy_card[card])) {
- zfw_ctrl =
- (struct ZFW_CTRL *)
- (cy_card[card].base_addr + firm_id->zfwctrl_addr);
+ zfw_ctrl = (struct ZFW_CTRL *)
+ (cy_card[card].base_addr + cy_readl(&firm_id->zfwctrl_addr));
board_ctrl = &zfw_ctrl->board_ctrl;
ch_ctrl = zfw_ctrl->ch_ctrl;
- lstatus = ch_ctrl[channel].rs_status;
+ lstatus = cy_readl(&ch_ctrl[channel].rs_status);
result = ((lstatus & C_RS_RTS) ? TIOCM_RTS : 0)
| ((lstatus & C_RS_DTR) ? TIOCM_DTR : 0)
| ((lstatus & C_RS_DCD) ? TIOCM_CAR : 0)
@@ -3203,18 +3384,27 @@ set_modem_info(struct cyclades_port * info, unsigned int cmd,
case TIOCMBIS:
if (arg & TIOCM_RTS){
save_flags(flags); cli();
- base_addr[CyCAR<<index] = (u_char)channel;
- base_addr[CyMSVR1<<index] = CyRTS;
+ cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+ if (info->rtsdtr_inv) {
+ cy_writeb((u_long)base_addr+(CyMSVR2<<index), CyDTR);
+ } else {
+ cy_writeb((u_long)base_addr+(CyMSVR1<<index), CyRTS);
+ }
restore_flags(flags);
}
if (arg & TIOCM_DTR){
save_flags(flags); cli();
- base_addr[CyCAR<<index] = (u_char)channel;
- base_addr[CyMSVR2<<index] = CyDTR;
+ cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+ if (info->rtsdtr_inv) {
+ cy_writeb((u_long)base_addr+(CyMSVR1<<index), CyRTS);
+ } else {
+ cy_writeb((u_long)base_addr+(CyMSVR2<<index), CyDTR);
+ }
#ifdef SERIAL_DEBUG_DTR
printk("cyc:set_modem_info raising DTR\n");
printk(" status: 0x%x, 0x%x\n",
- base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]);
+ cy_readb(base_addr+(CyMSVR1<<index)),
+ cy_readb(base_addr+(CyMSVR2<<index)));
#endif
restore_flags(flags);
}
@@ -3222,18 +3412,28 @@ set_modem_info(struct cyclades_port * info, unsigned int cmd,
case TIOCMBIC:
if (arg & TIOCM_RTS){
save_flags(flags); cli();
- base_addr[CyCAR<<index] = (u_char)channel;
- base_addr[CyMSVR1<<index] = ~CyRTS;
+ cy_writeb((u_long)base_addr+(CyCAR<<index),
+ (u_char)channel);
+ if (info->rtsdtr_inv) {
+ cy_writeb((u_long)base_addr+(CyMSVR2<<index), ~CyDTR);
+ } else {
+ cy_writeb((u_long)base_addr+(CyMSVR1<<index), ~CyRTS);
+ }
restore_flags(flags);
}
if (arg & TIOCM_DTR){
save_flags(flags); cli();
- base_addr[CyCAR<<index] = (u_char)channel;
- base_addr[CyMSVR2<<index] = ~CyDTR;
+ cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+ if (info->rtsdtr_inv) {
+ cy_writeb((u_long)base_addr+(CyMSVR1<<index), ~CyRTS);
+ } else {
+ cy_writeb((u_long)base_addr+(CyMSVR2<<index), ~CyDTR);
+ }
#ifdef SERIAL_DEBUG_DTR
printk("cyc:set_modem_info dropping DTR\n");
printk(" status: 0x%x, 0x%x\n",
- base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]);
+ cy_readb(base_addr+(CyMSVR1<<index)),
+ cy_readb(base_addr+(CyMSVR2<<index)));
#endif
restore_flags(flags);
}
@@ -3241,33 +3441,52 @@ set_modem_info(struct cyclades_port * info, unsigned int cmd,
case TIOCMSET:
if (arg & TIOCM_RTS){
save_flags(flags); cli();
- base_addr[CyCAR<<index] = (u_char)channel;
- base_addr[CyMSVR1<<index] = CyRTS;
+ cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+ if (info->rtsdtr_inv) {
+ cy_writeb((u_long)base_addr+(CyMSVR2<<index), CyDTR);
+ } else {
+ cy_writeb((u_long)base_addr+(CyMSVR1<<index), CyRTS);
+ }
restore_flags(flags);
}else{
save_flags(flags); cli();
- base_addr[CyCAR<<index] = (u_char)channel;
- base_addr[CyMSVR1<<index] = ~CyRTS;
+ cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+ if (info->rtsdtr_inv) {
+ cy_writeb((u_long)base_addr+(CyMSVR2<<index), ~CyDTR);
+ } else {
+ cy_writeb((u_long)base_addr+(CyMSVR1<<index), ~CyRTS);
+ }
restore_flags(flags);
}
if (arg & TIOCM_DTR){
save_flags(flags); cli();
- base_addr[CyCAR<<index] = (u_char)channel;
- base_addr[CyMSVR2<<index] = CyDTR;
+ cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+ if (info->rtsdtr_inv) {
+ cy_writeb((u_long)base_addr+(CyMSVR1<<index), CyRTS);
+ } else {
+ cy_writeb((u_long)base_addr+(CyMSVR2<<index), CyDTR);
+ }
#ifdef SERIAL_DEBUG_DTR
printk("cyc:set_modem_info raising DTR\n");
printk(" status: 0x%x, 0x%x\n",
- base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]);
+ cy_readb(base_addr+(CyMSVR1<<index)),
+ cy_readb(base_addr+(CyMSVR2<<index)));
#endif
restore_flags(flags);
}else{
save_flags(flags); cli();
- base_addr[CyCAR<<index] = (u_char)channel;
- base_addr[CyMSVR2<<index] = ~CyDTR;
+ cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+ if (info->rtsdtr_inv) {
+ cy_writeb((u_long)base_addr+(CyMSVR1<<index), ~CyRTS);
+ } else {
+ cy_writeb((u_long)base_addr+(CyMSVR2<<index), ~CyDTR);
+ }
+
#ifdef SERIAL_DEBUG_DTR
printk("cyc:set_modem_info dropping DTR\n");
printk(" status: 0x%x, 0x%x\n",
- base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]);
+ cy_readb(base_addr+(CyMSVR1<<index)),
+ cy_readb(base_addr+(CyMSVR2<<index)));
#endif
restore_flags(flags);
}
@@ -3281,19 +3500,20 @@ set_modem_info(struct cyclades_port * info, unsigned int cmd,
firm_id = (struct FIRM_ID *)
(cy_card[card].base_addr + ID_ADDRESS);
if (ISZLOADED(cy_card[card])) {
- zfw_ctrl =
- (struct ZFW_CTRL *)
- (cy_card[card].base_addr + firm_id->zfwctrl_addr);
+ zfw_ctrl = (struct ZFW_CTRL *)
+ (cy_card[card].base_addr + cy_readl(&firm_id->zfwctrl_addr));
board_ctrl = &zfw_ctrl->board_ctrl;
ch_ctrl = zfw_ctrl->ch_ctrl;
switch (cmd) {
case TIOCMBIS:
if (arg & TIOCM_RTS){
- ch_ctrl[channel].rs_control |= C_RS_RTS;
+ cy_writel(&ch_ctrl[channel].rs_control,
+ cy_readl(&ch_ctrl[channel].rs_control) | C_RS_RTS);
}
if (arg & TIOCM_DTR){
- ch_ctrl[channel].rs_control |= C_RS_DTR;
+ cy_writel(&ch_ctrl[channel].rs_control,
+ cy_readl(&ch_ctrl[channel].rs_control) | C_RS_DTR);
#ifdef SERIAL_DEBUG_DTR
printk("cyc:set_modem_info raising Z DTR\n");
#endif
@@ -3301,10 +3521,12 @@ set_modem_info(struct cyclades_port * info, unsigned int cmd,
break;
case TIOCMBIC:
if (arg & TIOCM_RTS){
- ch_ctrl[channel].rs_control &= ~C_RS_RTS;
+ cy_writel(&ch_ctrl[channel].rs_control,
+ cy_readl(&ch_ctrl[channel].rs_control) & ~C_RS_RTS);
}
if (arg & TIOCM_DTR){
- ch_ctrl[channel].rs_control &= ~C_RS_DTR;
+ cy_writel(&ch_ctrl[channel].rs_control,
+ cy_readl(&ch_ctrl[channel].rs_control) & ~C_RS_DTR);
#ifdef SERIAL_DEBUG_DTR
printk("cyc:set_modem_info clearing Z DTR\n");
#endif
@@ -3312,17 +3534,21 @@ set_modem_info(struct cyclades_port * info, unsigned int cmd,
break;
case TIOCMSET:
if (arg & TIOCM_RTS){
- ch_ctrl[channel].rs_control |= C_RS_RTS;
+ cy_writel(&ch_ctrl[channel].rs_control,
+ cy_readl(&ch_ctrl[channel].rs_control) | C_RS_RTS);
}else{
- ch_ctrl[channel].rs_control &= ~C_RS_RTS;
+ cy_writel(&ch_ctrl[channel].rs_control,
+ cy_readl(&ch_ctrl[channel].rs_control) & ~C_RS_RTS);
}
if (arg & TIOCM_DTR){
- ch_ctrl[channel].rs_control |= C_RS_DTR;
+ cy_writel(&ch_ctrl[channel].rs_control,
+ cy_readl(&ch_ctrl[channel].rs_control) | C_RS_DTR);
#ifdef SERIAL_DEBUG_DTR
printk("cyc:set_modem_info raising Z DTR\n");
#endif
}else{
- ch_ctrl[channel].rs_control &= ~C_RS_DTR;
+ cy_writel(&ch_ctrl[channel].rs_control,
+ cy_readl(&ch_ctrl[channel].rs_control) & ~C_RS_DTR);
#ifdef SERIAL_DEBUG_DTR
printk("cyc:set_modem_info clearing Z DTR\n");
#endif
@@ -3362,8 +3588,8 @@ send_break( struct cyclades_port * info, int duration)
A better implementation will use C_CM_SET_BREAK
and C_CM_CLR_BREAK with the appropriate delay.
*/
-#if 0
-this appears to wedge the output data stream
+#if 1
+// this appears to wedge the output data stream
int retval;
retval = cyz_issue_cmd(&cy_card[info->card],
(info->line) - (cy_card[info->card].first_line),
@@ -3408,7 +3634,7 @@ set_threshold(struct cyclades_port * info, unsigned long value)
info->cor3 &= ~CyREC_FIFO;
info->cor3 |= value & CyREC_FIFO;
- base_addr[CyCOR3<<index] = info->cor3;
+ cy_writeb((u_long)base_addr+(CyCOR3<<index), info->cor3);
cyy_issue_cmd(base_addr,CyCOR_CHANGE|CyCOR3ch,index);
} else {
// Nothing to do!
@@ -3434,7 +3660,7 @@ get_threshold(struct cyclades_port * info, unsigned long *value)
(cy_card[card].base_addr
+ (cy_chip_offset[chip]<<index));
- tmp = base_addr[CyCOR3<<index] & CyREC_FIFO;
+ tmp = cy_readb(base_addr+(CyCOR3<<index)) & CyREC_FIFO;
cy_put_user(tmp,value);
} else {
// Nothing to do!
@@ -3475,7 +3701,7 @@ set_timeout(struct cyclades_port * info, unsigned long value)
(cy_card[card].base_addr
+ (cy_chip_offset[chip]<<index));
- base_addr[CyRTPR<<index] = value & 0xff;
+ cy_writeb((u_long)base_addr+(CyRTPR<<index), value & 0xff);
} else {
// Nothing to do!
}
@@ -3500,7 +3726,7 @@ get_timeout(struct cyclades_port * info, unsigned long *value)
(cy_card[card].base_addr
+ (cy_chip_offset[chip]<<index));
- tmp = base_addr[CyRTPR<<index];
+ tmp = cy_readb(base_addr+(CyRTPR<<index));
cy_put_user(tmp,value);
} else {
// Nothing to do!
@@ -3524,7 +3750,6 @@ get_default_timeout(struct cyclades_port * info, unsigned long *value)
return 0;
}/* get_default_timeout */
-
/*
* This routine allows the tty driver to implement device-
* specific ioctl's. If the ioctl number passed in cmd is
@@ -3601,6 +3826,26 @@ cy_ioctl(struct tty_struct *tty, struct file * file,
case CYSETDEFTIMEOUT:
ret_val = set_default_timeout(info, (unsigned long)arg);
break;
+ case CYSETRFLOW:
+ info->rflow = 1;
+ ret_val = 0;
+ break;
+ case CYRESETRFLOW:
+ info->rflow = 0;
+ ret_val = 0;
+ break;
+ case CYSETRTSDTR_INV:
+ info->rtsdtr_inv = 1;
+ ret_val = 0;
+ break;
+ case CYRESETRTSDTR_INV:
+ info->rtsdtr_inv = 0;
+ ret_val = 0;
+ break;
+ case CYZPOLLCYCLE:
+ cyz_polling_cycle = (HZ * arg) / 1000;
+ ret_val = 0;
+ break;
case TCSBRK: /* SVID version: non-zero arg --> no break */
ret_val = tty_check_change(tty);
if (ret_val)
@@ -3745,7 +3990,7 @@ cy_throttle(struct tty_struct * tty)
#ifdef SERIAL_DEBUG_THROTTLE
char buf[64];
- printk("cyc:throttle %s: %d....ttyC%d\n", _tty_name(tty, buf),
+ printk("cyc:throttle %s: %d....ttyC%d\n", tty_name(tty, buf),
tty->ldisc.chars_in_buffer(tty), info->line);
#endif
@@ -3769,8 +4014,12 @@ cy_throttle(struct tty_struct * tty)
+ (cy_chip_offset[chip]<<index));
save_flags(flags); cli();
- base_addr[CyCAR<<index] = (u_char)channel;
- base_addr[CyMSVR1<<index] = ~CyRTS;
+ cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+ if (info->rtsdtr_inv) {
+ cy_writeb((u_long)base_addr+(CyMSVR2<<index), ~CyDTR);
+ } else {
+ cy_writeb((u_long)base_addr+(CyMSVR1<<index), ~CyRTS);
+ }
restore_flags(flags);
} else {
// Nothing to do!
@@ -3796,7 +4045,7 @@ cy_unthrottle(struct tty_struct * tty)
#ifdef SERIAL_DEBUG_THROTTLE
char buf[64];
- printk("cyc:throttle %s: %d....ttyC%d\n", _tty_name(tty, buf),
+ printk("cyc:throttle %s: %d....ttyC%d\n", tty_name(tty, buf),
tty->ldisc.chars_in_buffer(tty), info->line);
#endif
@@ -3820,8 +4069,12 @@ cy_unthrottle(struct tty_struct * tty)
+ (cy_chip_offset[chip]<<index));
save_flags(flags); cli();
- base_addr[CyCAR<<index] = (u_char)channel;
- base_addr[CyMSVR1<<index] = CyRTS;
+ cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+ if (info->rtsdtr_inv) {
+ cy_writeb((u_long)base_addr+(CyMSVR2<<index), CyDTR);
+ } else {
+ cy_writeb((u_long)base_addr+(CyMSVR1<<index), CyRTS);
+ }
restore_flags(flags);
}else{
// Nothing to do!
@@ -3861,9 +4114,10 @@ cy_stop(struct tty_struct *tty)
+ (cy_chip_offset[chip]<<index));
save_flags(flags); cli();
- base_addr[CyCAR<<index] =
- (u_char)(channel & 0x0003); /* index channel */
- base_addr[CySRER<<index] &= ~CyTxMpty;
+ cy_writeb((u_long)base_addr+(CyCAR<<index),
+ (u_char)(channel & 0x0003)); /* index channel */
+ cy_writeb((u_long)base_addr+(CySRER<<index),
+ cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty);
restore_flags(flags);
} else {
// Nothing to do!
@@ -3900,8 +4154,10 @@ cy_start(struct tty_struct *tty)
+ (cy_chip_offset[chip]<<index));
save_flags(flags); cli();
- base_addr[CyCAR<<index] = (u_char)(channel & 0x0003);
- base_addr[CySRER<<index] |= CyTxMpty;
+ cy_writeb((u_long)base_addr+(CyCAR<<index),
+ (u_char)(channel & 0x0003)); /* index channel */
+ cy_writeb((u_long)base_addr+(CySRER<<index),
+ cy_readb(base_addr+(CySRER<<index)) | CyTxMpty);
restore_flags(flags);
} else {
// Nothing to do!
@@ -3968,24 +4224,25 @@ cy_flush_buffer(struct tty_struct *tty)
* ---------------------------------------------------------------------
*/
-
/* initialize chips on Cyclom-Y card -- return number of valid
chips (which is number of ports/4) */
-__initfunc(static int
-cyy_init_card(unsigned char *true_base_addr,int index))
+__initfunc(static unsigned short
+cyy_init_card(volatile ucchar *true_base_addr,int index))
{
unsigned int chip_number;
- unsigned char* base_addr;
+ volatile ucchar* base_addr;
- true_base_addr[Cy_HwReset<<index] = 0; /* Cy_HwReset is 0x1400 */
- true_base_addr[Cy_ClrIntr<<index] = 0; /* Cy_ClrIntr is 0x1800 */
+ cy_writeb((u_long)true_base_addr+(Cy_HwReset<<index), 0);
+ /* Cy_HwReset is 0x1400 */
+ cy_writeb((u_long)true_base_addr+(Cy_ClrIntr<<index), 0);
+ /* Cy_ClrIntr is 0x1800 */
udelay(500L);
- for(chip_number=0; chip_number<CyMaxChipsPerCard; chip_number++){
+ for(chip_number=0; chip_number<CyMAX_CHIPS_PER_CARD; chip_number++){
base_addr = true_base_addr
+ (cy_chip_offset[chip_number]<<index);
udelay(1000L);
- if(base_addr[CyCCR<<index] != 0x00){
+ if(cy_readb(base_addr+(CyCCR<<index)) != 0x00){
/*************
printk(" chip #%d at %#6lx is never idle (CCR != 0)\n",
chip_number, (unsigned long)base_addr);
@@ -3993,7 +4250,7 @@ cyy_init_card(unsigned char *true_base_addr,int index))
return chip_number;
}
- base_addr[CyGFRCR<<index] = 0;
+ cy_writeb((u_long)base_addr+(CyGFRCR<<index), 0);
udelay(10L);
/* The Cyclom-16Y does not decode address bit 9 and therefore
@@ -4003,16 +4260,16 @@ cyy_init_card(unsigned char *true_base_addr,int index))
and this must be a Cyclom-16Y, not a Cyclom-32Ye.
*/
if (chip_number == 4
- && *(true_base_addr
+ && cy_readb(true_base_addr
+ (cy_chip_offset[0]<<index)
+ (CyGFRCR<<index)) == 0){
return chip_number;
}
- base_addr[CyCCR<<index] = CyCHIP_RESET;
+ cy_writeb((u_long)base_addr+(CyCCR<<index), CyCHIP_RESET);
udelay(1000L);
- if(base_addr[CyGFRCR<<index] == 0x00){
+ if(cy_readb(base_addr+(CyGFRCR<<index)) == 0x00){
/*
printk(" chip #%d at %#6lx is not responding ",
chip_number, (unsigned long)base_addr);
@@ -4020,7 +4277,7 @@ cyy_init_card(unsigned char *true_base_addr,int index))
*/
return chip_number;
}
- if((0xf0 & base_addr[CyGFRCR<<index]) != 0x40){
+ if((0xf0 & (cy_readb(base_addr+(CyGFRCR<<index)))) != 0x40){
/*
printk(" chip #%d at %#6lx is not valid (GFRCR == %#2x)\n",
chip_number, (unsigned long)base_addr,
@@ -4028,10 +4285,16 @@ cyy_init_card(unsigned char *true_base_addr,int index))
*/
return chip_number;
}
- base_addr[CyGCR<<index] = CyCH0_SERIAL;
- base_addr[CyPPR<<index] = 244;
- /* better value than CyCLOCK_25_1MS * 5
- to run clock at 200 Hz */
+ cy_writeb((u_long)base_addr+(CyGCR<<index), CyCH0_SERIAL);
+ if (cy_readb(base_addr+(CyGFRCR<<index)) >= 0x48){
+ /* It is a CD1400 rev. J or later */
+ /* Impossible to reach 5ms with this chip.
+ Changed to 2ms instead (f = 500 Hz). */
+ cy_writeb((u_long)base_addr+(CyPPR<<index), CyCLOCK_60_2MS);
+ } else {
+ /* f = 200 Hz */
+ cy_writeb((u_long)base_addr+(CyPPR<<index), CyCLOCK_25_5MS);
+ }
/*
printk(" chip #%d at %#6lx is rev 0x%2x\n",
@@ -4039,11 +4302,9 @@ cyy_init_card(unsigned char *true_base_addr,int index))
base_addr[CyGFRCR<<index]);
*/
}
-
return chip_number;
} /* cyy_init_card */
-
/*
* ---------------------------------------------------------------------
* cy_detect_isa() - Probe for Cyclom-Y/ISA boards.
@@ -4053,9 +4314,9 @@ cyy_init_card(unsigned char *true_base_addr,int index))
__initfunc(static int
cy_detect_isa(void))
{
- unsigned int cy_isa_irq,nboard;
- unsigned char *cy_isa_address;
- unsigned short i,j,cy_isa_nchan;
+ unsigned short cy_isa_irq,nboard;
+ volatile ucchar *cy_isa_address;
+ unsigned short i,j,cy_isa_nchan;
nboard = 0;
@@ -4067,10 +4328,13 @@ cy_detect_isa(void))
}
/* probe for CD1400... */
-#if (LINUX_VERSION_CODE >= 0x020100)
- cy_isa_address = ioremap((unsigned int)cy_isa_address,0x2000);
+
+#if !defined(__alpha__) && (LINUX_VERSION_CODE >= 0x020100)
+ cy_isa_address = ioremap((unsigned int)cy_isa_address,
+ CyISA_Ywin);
#endif
- cy_isa_nchan = 4 * cyy_init_card(cy_isa_address,0);
+ cy_isa_nchan = CyPORTS_PER_CHIP *
+ cyy_init_card(cy_isa_address,0);
if (cy_isa_nchan == 0) {
continue;
}
@@ -4078,15 +4342,15 @@ cy_detect_isa(void))
/* find out the board's irq by probing */
cy_isa_irq = do_auto_irq(cy_isa_address);
if (cy_isa_irq == 0) {
- printk("Cyclom-Y/ISA found at 0x%x ",
- (unsigned int) cy_isa_address);
+ printk("Cyclom-Y/ISA found at 0x%lx ",
+ (unsigned long) cy_isa_address);
printk("but the IRQ could not be detected.\n");
continue;
}
if((cy_next_channel+cy_isa_nchan) > NR_PORTS) {
- printk("Cyclom-Y/ISA found at 0x%x ",
- (unsigned int) cy_isa_address);
+ printk("Cyclom-Y/ISA found at 0x%lx ",
+ (unsigned long) cy_isa_address);
printk("but no more channels are available.\n");
printk("Change NR_PORTS in cyclades.c and recompile kernel.\n");
return(nboard);
@@ -4096,8 +4360,8 @@ cy_detect_isa(void))
if (cy_card[j].base_addr == 0) break;
}
if (j == NR_CARDS) { /* no more cy_cards available */
- printk("Cyclom-Y/ISA found at 0x%x ",
- (unsigned int) cy_isa_address);
+ printk("Cyclom-Y/ISA found at 0x%lx ",
+ (unsigned long) cy_isa_address);
printk("but no more cards can be used .\n");
printk("Change NR_CARDS in cyclades.c and recompile kernel.\n");
return(nboard);
@@ -4107,15 +4371,15 @@ cy_detect_isa(void))
if(request_irq(cy_isa_irq, cyy_interrupt,
SA_INTERRUPT, "cyclomY", NULL))
{
- printk("Cyclom-Y/ISA found at 0x%x ",
- (unsigned int) cy_isa_address);
+ printk("Cyclom-Y/ISA found at 0x%lx ",
+ (unsigned long) cy_isa_address);
printk("but could not allocate IRQ#%d.\n",
cy_isa_irq);
return(nboard);
}
/* set cy_card */
- cy_card[j].base_addr = (int) cy_isa_address;
+ cy_card[j].base_addr = (u_long) cy_isa_address;
cy_card[j].ctl_addr = 0;
cy_card[j].irq = (int) cy_isa_irq;
cy_card[j].bus_index = 0;
@@ -4125,10 +4389,10 @@ cy_detect_isa(void))
nboard++;
/* print message */
- printk("Cyclom-Y/ISA #%d: 0x%x-0x%x, IRQ%d, ",
- j+1, (unsigned int) cy_isa_address,
- (unsigned int)(cy_isa_address + 0x1fff),
- cy_isa_irq);
+ printk("Cyclom-Y/ISA #%d: 0x%lx-0x%lx, IRQ%d, ",
+ j+1, (unsigned long) cy_isa_address,
+ (unsigned long)(cy_isa_address + (CyISA_Ywin - 1)),
+ cy_isa_irq);
printk("%d channels starting from port %d.\n",
cy_isa_nchan, cy_next_channel);
cy_next_channel += cy_isa_nchan;
@@ -4150,11 +4414,11 @@ cy_detect_pci(void))
unsigned char cyy_bus, cyy_dev_fn, cyy_rev_id;
unsigned long pci_intr_ctrl;
unsigned char cy_pci_irq;
- unsigned int cy_pci_addr0, cy_pci_addr1, cy_pci_addr2;
+ uclong cy_pci_addr0, cy_pci_addr1, cy_pci_addr2;
unsigned short i,j,cy_pci_nchan;
unsigned short device_id,dev_index = 0,board_index = 0;
- unsigned long mailbox;
- unsigned int Ze_addr0[NR_CARDS], Ze_addr2[NR_CARDS], ZeIndex = 0;
+ uclong mailbox;
+ uclong Ze_addr0[NR_CARDS], Ze_addr2[NR_CARDS], ZeIndex = 0;
if(pcibios_present() == 0) { /* PCI bus not present */
return(0);
@@ -4181,13 +4445,17 @@ cy_detect_pci(void))
pcibios_read_config_byte(cyy_bus, cyy_dev_fn,
PCI_INTERRUPT_LINE, &cy_pci_irq);
pcibios_read_config_dword(cyy_bus, cyy_dev_fn,
- PCI_BASE_ADDRESS_0, &cy_pci_addr0);
+ PCI_BASE_ADDRESS_0,
+ (unsigned int *) &cy_pci_addr0);
pcibios_read_config_dword(cyy_bus, cyy_dev_fn,
- PCI_BASE_ADDRESS_1, &cy_pci_addr1);
+ PCI_BASE_ADDRESS_1,
+ (unsigned int *) &cy_pci_addr1);
pcibios_read_config_dword(cyy_bus, cyy_dev_fn,
- PCI_BASE_ADDRESS_2, &cy_pci_addr2);
+ PCI_BASE_ADDRESS_2,
+ (unsigned int *) &cy_pci_addr2);
pcibios_read_config_byte(cyy_bus, cyy_dev_fn,
PCI_REVISION_ID, &cyy_rev_id);
+
if ((device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo)
|| (device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi)){
#ifdef CY_PCI_DEBUG
@@ -4195,34 +4463,48 @@ cy_detect_pci(void))
cyy_bus, cyy_dev_fn);
printk("rev_id=%d) IRQ%d\n",
cyy_rev_id, (int)cy_pci_irq);
- printk("Cyclom-Y/PCI:found winaddr=0x%x ioaddr=0x%x\n",
- cy_pci_addr2, cy_pci_addr1);
+ printk("Cyclom-Y/PCI:found winaddr=0x%lx ioaddr=0x%lx\n",
+ (ulong)cy_pci_addr2, (ulong)cy_pci_addr1);
#endif
cy_pci_addr1 &= 0xfffffffc;
cy_pci_addr2 &= 0xfffffff0;
-#if (LINUX_VERSION_CODE < 0x020100)
+#if defined(__alpha__)
+ if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo) { /* below 1M? */
+ printk("Cyclom-Y/PCI (bus=0x0%x, pci_id=0x%x, ",
+ cyy_bus, cyy_dev_fn);
+ printk("rev_id=%d) IRQ%d\n",
+ cyy_rev_id, (int)cy_pci_irq);
+ printk("Cyclom-Y/PCI:found winaddr=0x%lx ioaddr=0x%lx\n",
+ (ulong)cy_pci_addr2, (ulong)cy_pci_addr1);
+ printk("Cyclom-Y/PCI not supported for low addresses in "
+ "Alpha systems.\n");
+ i--;
+ continue;
+ }
+#else
+#if (LINUX_VERSION_CODE < 0x020100)
if ((ulong)cy_pci_addr2 >= 0x100000) /* above 1M? */
#endif
- cy_pci_addr2 =
- (unsigned int) ioremap(cy_pci_addr2,CyPCI_Ywin);
+ cy_pci_addr2 = (ulong) ioremap(cy_pci_addr2, CyPCI_Ywin);
+#endif
#ifdef CY_PCI_DEBUG
- printk("Cyclom-Y/PCI: relocate winaddr=0x%x ioaddr=0x%x\n",
- cy_pci_addr2, cy_pci_addr1);
+ printk("Cyclom-Y/PCI: relocate winaddr=0x%lx ioaddr=0x%lx\n",
+ (u_long)cy_pci_addr2, (u_long)cy_pci_addr1);
#endif
- cy_pci_nchan = 4 * cyy_init_card((unsigned char *)
- cy_pci_addr2,1);
+ cy_pci_nchan = (unsigned short)(CyPORTS_PER_CHIP *
+ cyy_init_card((volatile ucchar *)cy_pci_addr2, 1));
if(cy_pci_nchan == 0) {
printk("Cyclom-Y PCI host card with ");
- printk("no Serial-Modules at 0x%x.\n",
- (unsigned int) cy_pci_addr2);
+ printk("no Serial-Modules at 0x%lx.\n",
+ (ulong) cy_pci_addr2);
i--;
continue;
}
if((cy_next_channel+cy_pci_nchan) > NR_PORTS) {
- printk("Cyclom-Y/PCI found at 0x%x ",
- (unsigned int) cy_pci_addr2);
+ printk("Cyclom-Y/PCI found at 0x%lx ",
+ (ulong) cy_pci_addr2);
printk("but no channels are available.\n");
printk("Change NR_PORTS in cyclades.c and recompile kernel.\n");
return(i);
@@ -4232,8 +4514,8 @@ cy_detect_pci(void))
if (cy_card[j].base_addr == 0) break;
}
if (j == NR_CARDS) { /* no more cy_cards available */
- printk("Cyclom-Y/PCI found at 0x%x ",
- (unsigned int) cy_pci_addr2);
+ printk("Cyclom-Y/PCI found at 0x%lx ",
+ (ulong) cy_pci_addr2);
printk("but no more cards can be used.\n");
printk("Change NR_CARDS in cyclades.c and recompile kernel.\n");
return(i);
@@ -4243,15 +4525,15 @@ cy_detect_pci(void))
if(request_irq(cy_pci_irq, cyy_interrupt,
SA_INTERRUPT, "cyclomY", NULL))
{
- printk("Cyclom-Y/PCI found at 0x%x ",
- (unsigned int) cy_pci_addr2);
+ printk("Cyclom-Y/PCI found at 0x%lx ",
+ (ulong) cy_pci_addr2);
printk("but could not allocate IRQ%d.\n",
cy_pci_irq);
return(i);
}
/* set cy_card */
- cy_card[j].base_addr = (int) cy_pci_addr2;
+ cy_card[j].base_addr = (ulong)cy_pci_addr2;
cy_card[j].ctl_addr = 0;
cy_card[j].irq = (int) cy_pci_irq;
cy_card[j].bus_index = 1;
@@ -4266,9 +4548,11 @@ cy_detect_pci(void))
| inw(cy_pci_addr1+0x6a)<<16);
/* print message */
- printk("Cyclom-Y/PCI #%d: 0x%x-0x%x, IRQ%d, ",
- j+1, cy_pci_addr2, (cy_pci_addr2 + CyPCI_Ywin - 1),
- (int)cy_pci_irq);
+ printk("Cyclom-Y/PCI #%d: 0x%lx-0x%lx, IRQ%d, ",
+ j+1,
+ (ulong)cy_pci_addr2,
+ (ulong)(cy_pci_addr2 + CyPCI_Ywin - 1),
+ (int)cy_pci_irq);
printk("%d channels starting from port %d.\n",
cy_pci_nchan, cy_next_channel);
@@ -4279,8 +4563,8 @@ cy_detect_pci(void))
cyy_bus, cyy_dev_fn);
printk("rev_id=%d) IRQ%d\n",
cyy_rev_id, (int)cy_pci_irq);
- printk("Cyclades-Z/PCI: found winaddr=0x%x ctladdr=0x%x\n",
- cy_pci_addr2, cy_pci_addr0);
+ printk("Cyclades-Z/PCI: found winaddr=0x%lx ctladdr=0x%lx\n",
+ (ulong)cy_pci_addr2, (ulong)cy_pci_addr0);
printk("Cyclades-Z/PCI not supported for low addresses\n");
break;
}else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Hi){
@@ -4289,25 +4573,29 @@ cy_detect_pci(void))
cyy_bus, cyy_dev_fn);
printk("rev_id=%d) IRQ%d\n",
cyy_rev_id, (int)cy_pci_irq);
- printk("Cyclades-Z/PCI: found winaddr=0x%x ctladdr=0x%x\n",
- cy_pci_addr2, cy_pci_addr0);
+ printk("Cyclades-Z/PCI: found winaddr=0x%lx ctladdr=0x%lx\n",
+ (ulong)cy_pci_addr2, (ulong)cy_pci_addr0);
#endif
cy_pci_addr0 &= 0xfffffff0;
+#if !defined(__alpha__)
cy_pci_addr0 = (unsigned int) ioremap(
cy_pci_addr0 & PAGE_MASK,
PAGE_ALIGN(CyPCI_Zctl))
+ (cy_pci_addr0 & (PAGE_SIZE-1));
-
- mailbox = ((struct RUNTIME_9060 *) cy_pci_addr0)->mail_box_0;
+#endif
+ mailbox = (uclong)cy_readl(&((struct RUNTIME_9060 *)
+ cy_pci_addr0)->mail_box_0);
cy_pci_addr2 &= 0xfffffff0;
if (mailbox == ZE_V1) {
+#if !defined(__alpha__)
cy_pci_addr2 = (unsigned int) ioremap(
cy_pci_addr2 & PAGE_MASK,
PAGE_ALIGN(CyPCI_Ze_win))
+ (cy_pci_addr2 & (PAGE_SIZE-1));
+#endif
if (ZeIndex == NR_CARDS) {
- printk("Cyclades-Z/PCI found at 0x%x ",
- (unsigned int) cy_pci_addr2);
+ printk("Cyclades-Ze/PCI found at 0x%lx ",
+ (ulong)cy_pci_addr2);
printk("but no more cards can be used.\n");
printk("Change NR_CARDS in cyclades.c and recompile kernel.\n");
} else {
@@ -4318,24 +4606,28 @@ cy_detect_pci(void))
i--;
continue;
} else {
+#if !defined(__alpha__)
cy_pci_addr2 = (unsigned int) ioremap(
cy_pci_addr2 & PAGE_MASK,
PAGE_ALIGN(CyPCI_Zwin))
+ (cy_pci_addr2 & (PAGE_SIZE-1));
+#endif
}
#ifdef CY_PCI_DEBUG
- printk("Cyclades-Z/PCI: relocate winaddr=0x%x ctladdr=0x%x\n",
- cy_pci_addr2, cy_pci_addr0);
+ printk("Cyclades-Z/PCI: relocate winaddr=0x%lx ctladdr=0x%lx\n",
+ (ulong)cy_pci_addr2, (ulong)cy_pci_addr0);
if (mailbox == ZO_V1) {
- ((struct RUNTIME_9060 *)(cy_pci_addr0))
- ->loc_addr_base = WIN_CREG;
+ cy_writel(&((struct RUNTIME_9060 *)
+ (cy_pci_addr0))->loc_addr_base, WIN_CREG);
PAUSE
- printk("Cyclades-Z/PCI: FPGA id %lx, ver %lx\n",
- 0xff & ((struct CUSTOM_REG *)(cy_pci_addr2))->fpga_id,
- 0xff & ((struct CUSTOM_REG *)(cy_pci_addr2))->fpga_version);
- ((struct RUNTIME_9060 *)(cy_pci_addr0))
- ->loc_addr_base = WIN_RAM;
+ printk("Cyclades-8Zo/PCI: FPGA id %lx, ver %lx\n",
+ (ulong)(0xff & cy_readl(&((struct CUSTOM_REG *)
+ (cy_pci_addr2))->fpga_id)),
+ (ulong)(0xff & cy_readl(&((struct CUSTOM_REG *)
+ (cy_pci_addr2))->fpga_version)));
+ cy_writel(&((struct RUNTIME_9060 *)
+ (cy_pci_addr0))->loc_addr_base, WIN_RAM);
} else {
printk("Cyclades-Z/PCI: New Cyclades-Z board. FPGA not loaded\n");
}
@@ -4345,8 +4637,8 @@ cy_detect_pci(void))
until it has been properly initialized.
*/
PAUSE
- if (mailbox == ZO_V1)
- *(unsigned long *)(cy_pci_addr2+ID_ADDRESS) = 0L;
+ if ((mailbox == ZO_V1) || (mailbox == ZO_V2))
+ cy_writel((ulong)(cy_pci_addr2+ID_ADDRESS), 0L);
/* This must be a Cyclades-8Zo/PCI. The extendable
version will have a different device_id and will
@@ -4354,8 +4646,8 @@ cy_detect_pci(void))
cy_pci_nchan = 8;
if((cy_next_channel+cy_pci_nchan) > NR_PORTS) {
- printk("Cyclades-Z/PCI found at 0x%x ",
- (unsigned int) cy_pci_addr2);
+ printk("Cyclades-8Zo/PCI found at 0x%lx ",
+ (ulong)cy_pci_addr2);
printk("but no channels are available.\n");
printk("Change NR_PORTS in cyclades.c and recompile kernel.\n");
return(i);
@@ -4366,8 +4658,8 @@ cy_detect_pci(void))
if (cy_card[j].base_addr == 0) break;
}
if (j == NR_CARDS) { /* no more cy_cards available */
- printk("Cyclades-Z/PCI found at 0x%x ",
- (unsigned int) cy_pci_addr2);
+ printk("Cyclades-8Zo/PCI found at 0x%lx ",
+ (ulong)cy_pci_addr2);
printk("but no more cards can be used.\n");
printk("Change NR_CARDS in cyclades.c and recompile kernel.\n");
return(i);
@@ -4380,12 +4672,13 @@ cy_detect_pci(void))
{
printk("Could not allocate IRQ%d ",
cy_pci_irq);
- printk("for Cyclades-Z/PCI at 0x%x.\n",
- (unsigned int) cy_pci_addr2);
+ printk("for Cyclades-8Zo/PCI at 0x%lx.\n",
+ (ulong)cy_pci_addr2);
return(i);
}
}
+
/* set cy_card */
cy_card[j].base_addr = cy_pci_addr2;
cy_card[j].ctl_addr = cy_pci_addr0;
@@ -4398,14 +4691,14 @@ cy_detect_pci(void))
/* print message */
/* don't report IRQ if board is no IRQ */
if( (cy_pci_irq < 15) && (cy_pci_irq > 1) ) {
- printk("Cyclades-Z/PCI #%d: 0x%x-0x%x, IRQ%d, ",
- j+1,cy_pci_addr2,
- (cy_pci_addr2 + CyPCI_Zwin - 1),
+ printk("Cyclades-8Zo/PCI #%d: 0x%lx-0x%lx, IRQ%d, ",
+ j+1,(ulong)cy_pci_addr2,
+ (ulong)(cy_pci_addr2 + CyPCI_Zwin - 1),
(int)cy_pci_irq);
}else{
- printk("Cyclades-Z/PCI #%d: 0x%x-0x%x, ",
- j+1,cy_pci_addr2,
- (cy_pci_addr2 + CyPCI_Zwin - 1));
+ printk("Cyclades-8Zo/PCI #%d: 0x%lx-0x%lx, ",
+ j+1,(ulong)cy_pci_addr2,
+ (ulong)(cy_pci_addr2 + CyPCI_Zwin - 1));
}
printk("%d channels starting from port %d.\n",
cy_pci_nchan,cy_next_channel);
@@ -4421,10 +4714,11 @@ cy_detect_pci(void))
Ze_addr2[j] = Ze_addr2[j+1];
}
ZeIndex--;
- mailbox = ((struct RUNTIME_9060 *) cy_pci_addr0)->mail_box_0;
+ mailbox = (uclong)cy_readl(&((struct RUNTIME_9060 *)
+ cy_pci_addr0)->mail_box_0);
#ifdef CY_PCI_DEBUG
- printk("Cyclades-Z/PCI: relocate winaddr=0x%x ctladdr=0x%x\n",
- cy_pci_addr2, cy_pci_addr0);
+ printk("Cyclades-Z/PCI: relocate winaddr=0x%lx ctladdr=0x%lx\n",
+ (ulong)cy_pci_addr2, (ulong)cy_pci_addr0);
printk("Cyclades-Z/PCI: New Cyclades-Z board. FPGA not loaded\n");
#endif
/* The following clears the firmware id word. This ensures
@@ -4436,8 +4730,8 @@ cy_detect_pci(void))
cy_pci_nchan = ZE_V1_NPORTS;
if((cy_next_channel+cy_pci_nchan) > NR_PORTS) {
- printk("Cyclades-Z/PCI found at 0x%x ",
- (unsigned int) cy_pci_addr2);
+ printk("Cyclades-Ze/PCI found at 0x%lx ",
+ (ulong)cy_pci_addr2);
printk("but no channels are available.\n");
printk("Change NR_PORTS in cyclades.c and recompile kernel.\n");
return(i);
@@ -4448,8 +4742,8 @@ cy_detect_pci(void))
if (cy_card[j].base_addr == 0) break;
}
if (j == NR_CARDS) { /* no more cy_cards available */
- printk("Cyclades-Z/PCI found at 0x%x ",
- (unsigned int) cy_pci_addr2);
+ printk("Cyclades-Ze/PCI found at 0x%lx ",
+ (ulong)cy_pci_addr2);
printk("but no more cards can be used.\n");
printk("Change NR_CARDS in cyclades.c and recompile kernel.\n");
return(i);
@@ -4462,8 +4756,8 @@ cy_detect_pci(void))
{
printk("Could not allocate IRQ%d ",
cy_pci_irq);
- printk("for Cyclades-Z/PCI at 0x%x.\n",
- (unsigned int) cy_pci_addr2);
+ printk("for Cyclades-Ze/PCI at 0x%lx.\n",
+ (ulong) cy_pci_addr2);
return(i);
}
}
@@ -4480,21 +4774,21 @@ cy_detect_pci(void))
/* print message */
/* don't report IRQ if board is no IRQ */
if( (cy_pci_irq < 15) && (cy_pci_irq > 1) ) {
- printk("Cyclades-Z/PCI #%d: 0x%x-0x%x, IRQ%d, ",
- j+1,cy_pci_addr2,
- (cy_pci_addr2 + CyPCI_Ze_win - 1),
+ printk("Cyclades-Ze/PCI #%d: 0x%lx-0x%lx, IRQ%d, ",
+ j+1,(ulong)cy_pci_addr2,
+ (ulong)(cy_pci_addr2 + CyPCI_Ze_win - 1),
(int)cy_pci_irq);
}else{
- printk("Cyclades-Z/PCI #%d: 0x%x-0x%x, ",
- j+1,cy_pci_addr2,
- (cy_pci_addr2 + CyPCI_Ze_win - 1));
+ printk("Cyclades-Ze/PCI #%d: 0x%lx-0x%lx, ",
+ j+1,(ulong)cy_pci_addr2,
+ (ulong)(cy_pci_addr2 + CyPCI_Ze_win - 1));
}
printk("%d channels starting from port %d.\n",
cy_pci_nchan,cy_next_channel);
cy_next_channel += cy_pci_nchan;
}
if (ZeIndex != 0) {
- printk("Cyclades-Z/PCI found at 0x%x ",
+ printk("Cyclades-Ze/PCI found at 0x%x ",
(unsigned int) Ze_addr2[0]);
printk("but no more cards can be used.\n");
printk("Change NR_CARDS in cyclades.c and recompile kernel.\n");
@@ -4520,7 +4814,7 @@ show_version(void)
tmp = strrchr(rcsdate, ' '); *tmp = '\0';
printk("Cyclom driver %s %s\n",
rcsvers, rcsdate);
- printk("\tbuilt %s %s\n",
+ printk(" built %s %s\n",
__DATE__, __TIME__);
} /* show_version */
@@ -4549,7 +4843,7 @@ cy_init(void))
struct cyclades_port *info;
struct cyclades_card *cinfo;
int number_z_boards = 0;
- int board,port,i;
+ int board,port,i,index;
unsigned long mailbox;
int nports;
@@ -4623,7 +4917,7 @@ cy_init(void))
/* look for isa boards */
cy_isa_nboard = cy_detect_isa();
-
+
/* look for pci boards */
cy_pci_nboard = cy_detect_pci();
@@ -4649,10 +4943,10 @@ cy_init(void))
/* initialize per-port data structures for each valid board found */
for (board = 0 ; board < cy_nboard ; board++) {
cinfo = &cy_card[board];
- if (cinfo->num_chips == 1){ /* Cyclades-8Zo/PCI */
+ if (cinfo->num_chips == 1){ /* Cyclades-Z */
number_z_boards++;
- mailbox = ((struct RUNTIME_9060 *)
- cy_card[board].ctl_addr)->mail_box_0;
+ mailbox = cy_readl(&((struct RUNTIME_9060 *)
+ cy_card[board].ctl_addr)->mail_box_0);
nports = (mailbox == ZE_V1) ? ZE_V1_NPORTS : 8;
for (port = cinfo->first_line ;
port < cinfo->first_line + nports;
@@ -4680,7 +4974,7 @@ cy_init(void))
info->event = 0;
info->count = 0;
#ifdef SERIAL_DEBUG_COUNT
- printk("cyc:cy_init(1) setting Z count to 0\n");
+// printk("cyc:cy_init(1) setting Z count to 0\n");
#endif
info->blocked_open = 0;
info->default_threshold = 0;
@@ -4705,6 +4999,7 @@ cy_init(void))
}
continue;
}else{ /* Cyclom-Y of some kind*/
+ index = cinfo->bus_index;
for (port = cinfo->first_line ;
port < cinfo->first_line + 4*cinfo->num_chips ;
port++)
@@ -4722,16 +5017,28 @@ cy_init(void))
info->cor3 = 0x08; /* _very_ small rcv threshold */
info->cor4 = 0;
info->cor5 = 0;
- info->tbpr = baud_bpr[13]; /* Tx BPR */
- info->tco = baud_co[13]; /* Tx CO */
- info->rbpr = baud_bpr[13]; /* Rx BPR */
- info->rco = baud_co[13]; /* Rx CO */
info->close_delay = 0;
+ if (cy_readb(cinfo->base_addr+(CyGFRCR<<index)) >= 0x48) {
+ /* It is a CD1400 rev. J or later */
+ info->tbpr = baud_bpr_60[13]; /* Tx BPR */
+ info->tco = baud_co_60[13]; /* Tx CO */
+ info->rbpr = baud_bpr_60[13]; /* Rx BPR */
+ info->rco = baud_co_60[13]; /* Rx CO */
+ info->rflow = 0;
+ info->rtsdtr_inv = 1;
+ } else {
+ info->tbpr = baud_bpr_25[13]; /* Tx BPR */
+ info->tco = baud_co_25[13]; /* Tx CO */
+ info->rbpr = baud_bpr_25[13]; /* Rx BPR */
+ info->rco = baud_co_25[13]; /* Rx CO */
+ info->rflow = 0;
+ info->rtsdtr_inv = 0;
+ }
info->x_char = 0;
info->event = 0;
info->count = 0;
#ifdef SERIAL_DEBUG_COUNT
- printk("cyc:cy_init(2) setting Y count to 0\n");
+// printk("cyc:cy_init(2) setting Y count to 0\n");
#endif
info->blocked_open = 0;
info->default_threshold = 0;
@@ -4874,54 +5181,54 @@ show_status(int line_num)
/* Global Registers */
- printk(" CyGFRCR %x\n", base_addr[CyGFRCR<<index]);
- printk(" CyCAR %x\n", base_addr[CyCAR<<index]);
- printk(" CyGCR %x\n", base_addr[CyGCR<<index]);
- printk(" CySVRR %x\n", base_addr[CySVRR<<index]);
- printk(" CyRICR %x\n", base_addr[CyRICR<<index]);
- printk(" CyTICR %x\n", base_addr[CyTICR<<index]);
- printk(" CyMICR %x\n", base_addr[CyMICR<<index]);
- printk(" CyRIR %x\n", base_addr[CyRIR<<index]);
- printk(" CyTIR %x\n", base_addr[CyTIR<<index]);
- printk(" CyMIR %x\n", base_addr[CyMIR<<index]);
- printk(" CyPPR %x\n", base_addr[CyPPR<<index]);
+ printk(" CyGFRCR %x\n", cy_readb(base_addr + CyGFRCR<<index));
+ printk(" CyCAR %x\n", cy_readb(base_addr + CyCAR<<index));
+ printk(" CyGCR %x\n", cy_readb(base_addr + CyGCR<<index));
+ printk(" CySVRR %x\n", cy_readb(base_addr + CySVRR<<index));
+ printk(" CyRICR %x\n", cy_readb(base_addr + CyRICR<<index));
+ printk(" CyTICR %x\n", cy_readb(base_addr + CyTICR<<index));
+ printk(" CyMICR %x\n", cy_readb(base_addr + CyMICR<<index));
+ printk(" CyRIR %x\n", cy_readb(base_addr + CyRIR<<index));
+ printk(" CyTIR %x\n", cy_readb(base_addr + CyTIR<<index));
+ printk(" CyMIR %x\n", cy_readb(base_addr + CyMIR<<index));
+ printk(" CyPPR %x\n", cy_readb(base_addr + CyPPR<<index));
- base_addr[CyCAR<<index] = (u_char)channel;
+ cy_writeb(base_addr + CyCAR<<index, (u_char)channel);
/* Virtual Registers */
- printk(" CyRIVR %x\n", base_addr[CyRIVR<<index]);
- printk(" CyTIVR %x\n", base_addr[CyTIVR<<index]);
- printk(" CyMIVR %x\n", base_addr[CyMIVR<<index]);
- printk(" CyMISR %x\n", base_addr[CyMISR<<index]);
+ printk(" CyRIVR %x\n", cy_readb(base_addr + CyRIVR<<index));
+ printk(" CyTIVR %x\n", cy_readb(base_addr + CyTIVR<<index));
+ printk(" CyMIVR %x\n", cy_readb(base_addr + CyMIVR<<index));
+ printk(" CyMISR %x\n", cy_readb(base_addr + CyMISR<<index));
/* Channel Registers */
- printk(" CyCCR %x\n", base_addr[CyCCR<<index]);
- printk(" CySRER %x\n", base_addr[CySRER<<index]);
- printk(" CyCOR1 %x\n", base_addr[CyCOR1<<index]);
- printk(" CyCOR2 %x\n", base_addr[CyCOR2<<index]);
- printk(" CyCOR3 %x\n", base_addr[CyCOR3<<index]);
- printk(" CyCOR4 %x\n", base_addr[CyCOR4<<index]);
- printk(" CyCOR5 %x\n", base_addr[CyCOR5<<index]);
- printk(" CyCCSR %x\n", base_addr[CyCCSR<<index]);
- printk(" CyRDCR %x\n", base_addr[CyRDCR<<index]);
- printk(" CySCHR1 %x\n", base_addr[CySCHR1<<index]);
- printk(" CySCHR2 %x\n", base_addr[CySCHR2<<index]);
- printk(" CySCHR3 %x\n", base_addr[CySCHR3<<index]);
- printk(" CySCHR4 %x\n", base_addr[CySCHR4<<index]);
- printk(" CySCRL %x\n", base_addr[CySCRL<<index]);
- printk(" CySCRH %x\n", base_addr[CySCRH<<index]);
- printk(" CyLNC %x\n", base_addr[CyLNC<<index]);
- printk(" CyMCOR1 %x\n", base_addr[CyMCOR1<<index]);
- printk(" CyMCOR2 %x\n", base_addr[CyMCOR2<<index]);
- printk(" CyRTPR %x\n", base_addr[CyRTPR<<index]);
- printk(" CyMSVR1 %x\n", base_addr[CyMSVR1<<index]);
- printk(" CyMSVR2 %x\n", base_addr[CyMSVR2<<index]);
- printk(" CyRBPR %x\n", base_addr[CyRBPR<<index]);
- printk(" CyRCOR %x\n", base_addr[CyRCOR<<index]);
- printk(" CyTBPR %x\n", base_addr[CyTBPR<<index]);
- printk(" CyTCOR %x\n", base_addr[CyTCOR<<index]);
+ printk(" CyCCR %x\n", cy_readb(base_addr + CyCCR<<index));
+ printk(" CySRER %x\n", cy_readb(base_addr + CySRER<<index));
+ printk(" CyCOR1 %x\n", cy_readb(base_addr + CyCOR1<<index));
+ printk(" CyCOR2 %x\n", cy_readb(base_addr + CyCOR2<<index));
+ printk(" CyCOR3 %x\n", cy_readb(base_addr + CyCOR3<<index));
+ printk(" CyCOR4 %x\n", cy_readb(base_addr + CyCOR4<<index));
+ printk(" CyCOR5 %x\n", cy_readb(base_addr + CyCOR5<<index));
+ printk(" CyCCSR %x\n", cy_readb(base_addr + CyCCSR<<index));
+ printk(" CyRDCR %x\n", cy_readb(base_addr + CyRDCR<<index));
+ printk(" CySCHR1 %x\n", cy_readb(base_addr + CySCHR1<<index));
+ printk(" CySCHR2 %x\n", cy_readb(base_addr + CySCHR2<<index));
+ printk(" CySCHR3 %x\n", cy_readb(base_addr + CySCHR3<<index));
+ printk(" CySCHR4 %x\n", cy_readb(base_addr + CySCHR4<<index));
+ printk(" CySCRL %x\n", cy_readb(base_addr + CySCRL<<index));
+ printk(" CySCRH %x\n", cy_readb(base_addr + CySCRH<<index));
+ printk(" CyLNC %x\n", cy_readb(base_addr + CyLNC<<index));
+ printk(" CyMCOR1 %x\n", cy_readb(base_addr + CyMCOR1<<index));
+ printk(" CyMCOR2 %x\n", cy_readb(base_addr + CyMCOR2<<index));
+ printk(" CyRTPR %x\n", cy_readb(base_addr + CyRTPR<<index));
+ printk(" CyMSVR1 %x\n", cy_readb(base_addr + CyMSVR1<<index));
+ printk(" CyMSVR2 %x\n", cy_readb(base_addr + CyMSVR2<<index));
+ printk(" CyRBPR %x\n", cy_readb(base_addr + CyRBPR<<index));
+ printk(" CyRCOR %x\n", cy_readb(base_addr + CyRCOR<<index));
+ printk(" CyTBPR %x\n", cy_readb(base_addr + CyTBPR<<index));
+ printk(" CyTCOR %x\n", cy_readb(base_addr + CyTCOR<<index));
restore_flags(flags);
} /* show_status */
diff --git a/drivers/char/epca.c b/drivers/char/epca.c
index 63da8b5cb..bc25dca1d 100644
--- a/drivers/char/epca.c
+++ b/drivers/char/epca.c
@@ -1384,7 +1384,7 @@ static int block_til_ready(struct tty_struct *tty,
(do_clocal || (ch->imodem & ch->dcd)))
break;
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
{
retval = -ERESTARTSYS;
break;
diff --git a/drivers/char/esp.c b/drivers/char/esp.c
index 636c6369d..5fdcd8a77 100644
--- a/drivers/char/esp.c
+++ b/drivers/char/esp.c
@@ -47,6 +47,7 @@
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/serial.h>
+#include <linux/serialP.h>
#include <linux/serial_reg.h>
#include <linux/major.h>
#include <linux/string.h>
@@ -140,11 +141,8 @@ static void change_speed(struct esp_struct *info);
static void rs_wait_until_sent(struct tty_struct *, int);
/*
- * This assumes you have a 1.8432 MHz clock for your UART.
- *
- * It'd be nice if someone built a serial card with a 24.576 MHz
- * clock, since the 16550A is capable of handling a top speed of 1.5
- * megabits/second; but this requires the faster clock.
+ * The ESP card has a clock rate of 14.7456 MHz (that is, 2**ESPC_SCALE
+ * times the normal 1.8432 Mhz clock of most serial boards).
*/
#define BASE_BAUD ((1843200 / 16) * (1 << ESPC_SCALE))
@@ -192,15 +190,6 @@ static inline int serial_paranoia_check(struct esp_struct *info,
return 0;
}
-/*
- * This is used to figure out the divisor speeds
- */
-static int quot_table[] = {
-/* 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, */
- 0, 18432, 12288, 8378, 6878, 6144, 4608, 3072, 1536, 768,
-/* 1800,2400,4800,9600,19200,38400,57600,115200,230400,460800 */
- 512, 384, 192, 96, 48, 24, 16, 8, 4, 2, 0 };
-
static inline unsigned int serial_in(struct esp_struct *info, int offset)
{
return inb(info->port + offset);
@@ -1096,7 +1085,7 @@ static void change_speed(struct esp_struct *info)
unsigned short port;
int quot = 0;
unsigned cflag,cval;
- int i, bits;
+ int baud, bits;
unsigned char flow1 = 0, flow2 = 0;
unsigned long flags;
@@ -1104,27 +1093,7 @@ static void change_speed(struct esp_struct *info)
return;
cflag = info->tty->termios->c_cflag;
port = info->port;
- i = cflag & CBAUD;
- if (i & CBAUDEX) {
- i &= ~CBAUDEX;
- if (i < 1 || i > 2)
- info->tty->termios->c_cflag &= ~CBAUDEX;
- else
- i += 15;
- }
- if (i == 15) {
- if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
- i += 1;
- if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
- i += 2;
- if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
- i += 3;
- if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
- i += 4;
- if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)
- quot = info->custom_divisor;
- }
-
+
/* byte size and parity */
switch (cflag & CSIZE) {
case CS5: cval = 0x00; bits = 7; break;
@@ -1148,14 +1117,20 @@ static void change_speed(struct esp_struct *info)
cval |= UART_LCR_SPAR;
#endif
- if (!quot) {
- quot = quot_table[i];
-
- /* default to 9600 bps */
- if (!quot)
- quot = BASE_BAUD / 9600;
- }
-
+ baud = tty_get_baud_rate(info->tty);
+ if (baud == 38400)
+ quot = info->custom_divisor;
+ else {
+ if (baud == 134)
+ /* Special case since 134 is really 134.5 */
+ quot = (2*BASE_BAUD / 269);
+ else if (baud)
+ quot = BASE_BAUD / baud;
+ }
+ /* If the quotient is ever zero, default to 9600 bps */
+ if (!quot)
+ quot = BASE_BAUD / 9600;
+
info->timeout = ((1024 * HZ * bits * quot) / BASE_BAUD) + (HZ / 50);
/* CTS flow control flag and modem status interrupts */
@@ -1632,8 +1607,17 @@ check_and_exit:
if (((old_info.flags & ASYNC_SPD_MASK) !=
(info->flags & ASYNC_SPD_MASK)) ||
(old_info.custom_divisor != info->custom_divisor) ||
- change_flow)
+ change_flow) {
+ if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+ info->tty->alt_speed = 57600;
+ if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+ info->tty->alt_speed = 115200;
+ if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
+ info->tty->alt_speed = 230400;
+ if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
+ info->tty->alt_speed = 460800;
change_speed(info);
+ }
} else
retval = startup(info);
return retval;
@@ -1723,30 +1707,27 @@ static int set_modem_info(struct esp_struct * info, unsigned int cmd,
}
/*
- * This routine sends a break character out the serial port.
+ * rs_break() --- routine which turns the break handling on or off
*/
-static void send_break( struct esp_struct * info, int duration)
+static void esp_break(struct tty_struct *tty, int break_state)
{
- cli();
- serial_out(info, UART_ESI_CMD1, ESI_ISSUE_BREAK);
- serial_out(info, UART_ESI_CMD2, 0x01);
+ struct esp_struct * info = (struct esp_struct *)tty->driver_data;
+ unsigned long flags;
+
+ if (serial_paranoia_check(info, tty->device, "esp_break"))
+ return;
- interruptible_sleep_on(&info->break_wait);
+ save_flags(flags); cli();
+ if (break_state == -1) {
+ serial_out(info, UART_ESI_CMD1, ESI_ISSUE_BREAK);
+ serial_out(info, UART_ESI_CMD2, 0x01);
- if (current->signal & ~current->blocked) {
+ interruptible_sleep_on(&info->break_wait);
+ } else {
serial_out(info, UART_ESI_CMD1, ESI_ISSUE_BREAK);
serial_out(info, UART_ESI_CMD2, 0x00);
- sti();
- return;
}
-
- current->state = TASK_INTERRUPTIBLE;
- current->timeout = jiffies + duration;
- schedule();
-
- serial_out(info, UART_ESI_CMD1, ESI_ISSUE_BREAK);
- serial_out(info, UART_ESI_CMD2, 0x00);
- sti();
+ restore_flags(flags);
}
static int rs_ioctl(struct tty_struct *tty, struct file * file,
@@ -1754,7 +1735,6 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file,
{
int error;
struct esp_struct * info = (struct esp_struct *)tty->driver_data;
- int retval;
struct async_icount cprev, cnow; /* kernel counter temps */
struct serial_icounter_struct *p_cuser; /* user space */
@@ -1770,41 +1750,6 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file,
}
switch (cmd) {
- case TCSBRK: /* SVID version: non-zero arg --> no break */
- retval = tty_check_change(tty);
- if (retval)
- return retval;
- tty_wait_until_sent(tty, 0);
- if (current->signal & ~current->blocked)
- return -EINTR;
- if (!arg) {
- send_break(info, HZ/4); /* 1/4 second */
- if (current->signal & ~current->blocked)
- return -EINTR;
- }
- return 0;
- case TCSBRKP: /* support for POSIX tcsendbreak() */
- retval = tty_check_change(tty);
- if (retval)
- return retval;
- tty_wait_until_sent(tty, 0);
- if (current->signal & ~current->blocked)
- return -EINTR;
- send_break(info, arg ? arg*(HZ/10) : HZ/4);
- if (current->signal & ~current->blocked)
- return -EINTR;
- return 0;
- case TIOCGSOFTCAR:
- return put_user(C_CLOCAL(tty) ? 1 : 0,
- (int *) arg);
- case TIOCSSOFTCAR:
- error = get_user(arg, (unsigned int *)arg);
- if (error)
- return error;
- tty->termios->c_cflag =
- ((tty->termios->c_cflag & ~CLOCAL) |
- (arg ? CLOCAL : 0));
- return 0;
case TIOCMGET:
return get_modem_info(info, (unsigned int *) arg);
case TIOCMBIS:
@@ -1845,7 +1790,7 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file,
while (1) {
interruptible_sleep_on(&info->delta_msr_wait);
/* see if a signal did it */
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
return -ERESTARTSYS;
cli();
cnow = info->icount; /* atomic copy */
@@ -2086,7 +2031,7 @@ static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
current->timeout = jiffies + char_time;
schedule();
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
break;
if (timeout && ((orig_jiffies + timeout) < jiffies))
@@ -2243,7 +2188,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
!(info->flags & ASYNC_CLOSING) &&
(do_clocal))
break;
- if (current->signal & ~current->blocked) {
+ if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
@@ -2527,6 +2472,7 @@ __initfunc(int espserial_init(void))
esp_driver.stop = rs_stop;
esp_driver.start = rs_start;
esp_driver.hangup = esp_hangup;
+ esp_driver.break_ctl = esp_break;
esp_driver.wait_until_sent = rs_wait_until_sent;
/*
diff --git a/drivers/char/ftape/Config.in b/drivers/char/ftape/Config.in
new file mode 100644
index 000000000..858f327d3
--- /dev/null
+++ b/drivers/char/ftape/Config.in
@@ -0,0 +1,38 @@
+#
+# Ftape configuration
+#
+dep_tristate 'Zftape, the VFS interface' CONFIG_ZFTAPE $CONFIG_FTAPE
+if [ "$CONFIG_ZFTAPE" != "n" ]; then
+ int 'Default block size' CONFIG_ZFT_DFLT_BLK_SZ 10240
+ comment 'The compressor will be built as a module only!'
+ define_bool CONFIG_ZFT_COMPRESSOR m
+fi
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ int 'Number of ftape buffers (EXPERIMENTAL)' CONFIG_FT_NR_BUFFERS 3
+fi
+if [ "$CONFIG_PROC_FS" = "y" ]; then
+ bool 'Enable procfs status report (+2kb)' CONFIG_FT_PROC_FS y
+fi
+choice 'Debugging output' \
+ "Normal CONFIG_FT_NORMAL_DEBUG \
+ Excessive CONFIG_FT_FULL_DEBUG \
+ Reduced CONFIG_FT_NO_TRACE \
+ None CONIFG_FT_NO_TRACE_AT_ALL" Normal
+comment 'Hardware configuration'
+choice 'Floppy tape controllers' \
+ "Standard CONFIG_FT_STD_FDC \
+ MACH-2 CONFIG_FT_MACH2 \
+ FC-10/FC-20 CONFIG_FT_PROBE_FC10 \
+ Alt/82078 CONFIG_FT_ALT_FDC" Standard
+if [ "$CONFIG_FT_STD_FDC" != "y" ]; then
+ comment ' Consult the manuals of your tape drive for the correct settings!'
+ hex ' IO base of the floppy disk controller' CONFIG_FT_FDC_BASE 0
+ int ' IRQ channel of the floppy disk controller' CONFIG_FT_FDC_IRQ 0
+ int ' DMA channel of the floppy disk controller' CONFIG_FT_FDC_DMA 0
+fi
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ int 'Default FIFO threshold (EXPERIMENTAL)' CONFIG_FT_FDC_THR 8
+ int 'Maximal data rate to use (EXPERIMENTAL)' CONFIG_FT_FDC_MAX_RATE 2000
+fi
+comment 'ONLY for DEC Alpha architectures'
+int 'CPU clock frequency of your DEC Alpha' CONFIG_FT_ALPHA_CLOCK 0
diff --git a/drivers/char/ftape/Makefile b/drivers/char/ftape/Makefile
index 1da53beb1..5726e788e 100644
--- a/drivers/char/ftape/Makefile
+++ b/drivers/char/ftape/Makefile
@@ -1,65 +1,67 @@
#
-# Makefile for the ftape device driver.
+# Copyright (C) 1997 Claus Heine.
#
-# 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).
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; see the file COPYING. If not, write to
+# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
#
-# Note 2! The CFLAGS definitions are now inherited from the
-# parent makes..
+# $Source: /homes/cvs/ftape-stacked/ftape/Makefile,v $
+# $Revision: 1.4 $
+# $Date: 1997/10/05 19:17:56 $
#
-
-# Valid ftape options are:
-# NO_TRACE - if defined, only information and errors show up.
-# NO_TRACE_AT_ALL - if defined, no trace output shows up.
-# GCC_2_4_5_BUG - must be set if using gcc-2.4.5 to prevent
-# bad assembler-code for the dma handling.
-# NR_BUFFERS - Number of ftape DMA buffers (keep it at 3!)
-# VERIFY_HEADERS - if set the headers segments are verified after
-# being written.
-# PROBE_FC10 - if defined will look for a FC-10 card at specified
-# settings (FDC_BASE,FDC_IRQ,FDC_DMA) before using
-# the standard fd controller.
-# FDC_BASE - sets base address (only!) if using non-standard fdc
-# FDC_IRQ - sets interrupt if FDC_BASE is defined
-# FDC_DMA - sets dma channel if FDC_BASE is defined
-# MACH2 - Support for Mountain MACH-2 controller at either 1E0
-# or 3E0, don't forget the FDC_OPT's !
-# CLK_48MHZ - Set to 1. If you have a i82078-1 FDC and it does not
-# work, try setting it to 0. (only used for i82078-1's)
-# FDC_82078SL - If you have a 82078SL, define this.
-
-FTAPE_OPT = -DVERIFY_HEADERS -DNR_BUFFERS=3 -DCLK_48MHZ=1 \
- -DNO_TRACE -DFDC_82078SL
-
-# If you're using a non-standard floppy disk controller for the
-# tape drive, enable one (only!) of the following lines and set
-# the FDC_BASE, FDC_IRQ and FDC_DMA parameters to the actual values.
-#
-# Note1: A FC-10/FC-20 controller must use either of DMA 1, 2, or 3.
-# DMA 5 and 7 does NOT work!.
-#
-# Note2: IRQ 2 and IRQ 9 can be considered the same. When using IRQ 2
-# on a controller you must specify IRQ 9 here!
+# Makefile for the QIC-40/80/3010/3020 floppy-tape driver for
+# Linux.
#
-# For a Mountain MACH-2 controller, try
-#FDC_OPT = -DMACH2 -DFDC_BASE=0x1E0 -DFDC_IRQ=6 -DFDC_DMA=2
-#
-# For Colorado CMS FC-10 or FC-20 controllers:
-#FDC_OPT = -DPROBE_FC10 -DFDC_BASE=0x180 -DFDC_IRQ=9 -DFDC_DMA=3
+
#
-# Secondary floppy disk controller:
-#FDC_OPT = -DFDC_BASE=0x370 -DFDC_IRQ=9 -DFDC_DMA=3
+# This isn't used inside the kernel, only for my private development
+# version
#
-# This enables some (most?) 2Mbps controllers:
-#FDC_OPT = -DFDC_BASE=0x3E0 -DFDC_IRQ=6 -DFDC_DMA=2
+ifndef TOPDIR
+TOPDIR= ..
+include $(TOPDIR)/MCONFIG
+endif
+
+SUB_DIRS :=
+MOD_SUB_DIRS := $(SUB_DIRS)
+ALL_SUB_DIRS := $(SUB_DIRS) lowlevel zftape compressor
+
+ifeq ($(CONFIG_FTAPE),y)
+ O_TARGET := ftape.o
+ SUB_DIRS += lowlevel
+ O_OBJS += lowlevel/ftape.o
+else
+ ifeq ($(CONFIG_FTAPE),m)
+ MOD_SUB_DIRS += lowlevel
+ endif
+endif
-EXTRA_CFLAGS := $(FTAPE_OPT) $(FDC_OPT)
+ifeq ($(CONFIG_ZFTAPE),y)
+ SUB_DIRS += zftape
+ O_OBJS += zftape/zftape.o
+else
+ ifeq ($(CONFIG_ZFTAPE),m)
+ MOD_SUB_DIRS += zftape
+ endif
+endif
-O_TARGET := ftape.o
-O_OBJS = kernel-interface.o tracing.o fdc-io.o fdc-isr.o \
- ftape-bsm.o ftape-ctl.o ftape-eof.o ftape-read.o ftape-rw.o \
- ftape-write.o ftape-io.o calibr.o ecc.o fc-10.o
-M_OBJS = $(O_TARGET)
+ifeq ($(CONFIG_ZFT_COMPRESSOR),y)
+ SUB_DIRS += compressor
+ O_OBJS += compressor/zft-compressor.o
+else
+ ifeq ($(CONFIG_ZFT_COMPRESSOR),m)
+ MOD_SUB_DIRS += compressor
+ endif
+endif
include $(TOPDIR)/Rules.make
diff --git a/drivers/char/ftape/README.PCI b/drivers/char/ftape/README.PCI
index 05842f071..18de159d3 100644
--- a/drivers/char/ftape/README.PCI
+++ b/drivers/char/ftape/README.PCI
@@ -30,7 +30,7 @@ bus timing.
I judge this a hardware problem and not a limitation of ftape ;-)
My DOS backup software seems to be suffering from the same problems
and even refuses to run at 1 Mbps !
-Ftape will reduce the datarate from 1 Mbps to 500 Kbps if the number
+Ftape will reduce the data-rate from 1 Mbps to 500 Kbps if the number
of overrun errors on a track exceeds a threshold.
@@ -77,3 +77,5 @@ under DOS. If it's very slow and often repositions you're
probably having this problem.
--//--
+ LocalWords: ftape PCI bios GAT ISA DMA chipset Mbps Kbps FDC isa AF ok ASUS
+ LocalWords: SP linebuffer masterbuffer XPS http www com
diff --git a/drivers/char/ftape/RELEASE-NOTES b/drivers/char/ftape/RELEASE-NOTES
index fc82f9900..dcc250808 100644
--- a/drivers/char/ftape/RELEASE-NOTES
+++ b/drivers/char/ftape/RELEASE-NOTES
@@ -1,3 +1,392 @@
+Hey, Emacs, we're -*-Text-*- mode!
+
+===== Release notes for ftape-3.04d 25/11/97 =====
+- The correct pre-processor statement for "else if" is "#elif" not
+ "elsif".
+- Need to call zft_reset_position() when overwriting cartridges
+ previously written with ftape-2.x, sftape, or ancient
+ (pre-ftape-3.x) versions of zftape.
+
+===== Release notes for ftape-3.04c 16/11/97 =====
+- fdc_probe() was calling DUMPREGS with a result length of "1" which
+ was just fine. Undo previous change.
+
+===== Release notes for ftape-3.04b 14/11/97 =====
+
+- patches/2.x.x/floppy.c.diff was somewhat broken, releasing i/o
+ regions it never had allocated.
+- fdc_probe() was calling DUMPREGS with a result length of "1" instead
+ of "10"
+- Writing deleted data marks if the first segents on track zero are
+ should work now.
+- ftformat should now be able to handle those cases where the tape
+ drive sets the read only status bit (QIC-40/80 cartridges with
+ QIC-3010/3020 tape drives) because the header segment is damaged.
+- the MTIOCFTCMD ioctl may now be issued by the superuser ONLY.
+
+===== Release notes for ftape-3.04a 12/11/97 =====
+- Fix an "infinite loop can't be killed by signal" bug in
+ ftape_get_drive_status(). Only relevant when trying to access
+ buggy/misconfigured hardware
+- Try to compensate a bug in the HP Colorado T3000's firmware: it
+ doesn't set the write protect bit for QIC80/QIC40 cartridges.
+
+===== Release notes for ftape-3.04 06/11/97 =====
+- If positioning with fast seeking fails fall back to a slow seek
+ before giving up.
+- (nearly) no retries on "no data errors" when verifying after
+ formatting. Improved tuning of the bad sector map after formatting.
+- the directory layout has changed again to allow for easier kernel
+ integration
+- Module parameter "ftape_tracing" now is called "ft_tracing" because
+ the "ftape_tracing" variable has the version checksum attached to it.
+- `/proc/ftape' interface for 2.0.* kernels. `/proc/ftape' no longer
+ is a directory but a file that contains all the information formerly
+ provided in separate files under the `/proc/ftape/' directory.
+- Most of the configuration options have been prefixed by "CONFIG_FT_"
+ in preparation of the kernel inclusion. The Makefiles under
+ "./ftape/" should be directly usable by the kernel.
+- The MODVERSIONS stuff is now auto-detected.
+- Broke backslashed multi line options in MCONFIG into separate lines
+ using GNU-make's "+=" feature.
+- The html and dvi version of the manual is now installed under
+ '/usr/doc/ftape` with 'make install`
+- New SMP define in MCONFIG. ftape works with SMP if this is defined.
+- attempt to cope with "excessive overrun errors" by gradually
+ increasing FDC FIFO threshold. But this doesn't seem to have too
+ much an effect.
+- New load time configuration parameter "ft_fdc_rate_limit". If you
+ encounter too many overrun errors with a 2Mb controller then you
+ might want to set this to 1000.
+- overrun errors on the last sector in a segment sometimes result in
+ a zero DMA residue. Dunno why, but compensate for it.
+- there were still fdc_read() timeout errors. I think I have fixed it
+ now, please FIXME.
+- Sometimes ftape_write() failed to re-start the tape drive when a
+ segment without a good sector was reached ("wait for empty segment
+ failed"). This is fixed. Especially important for > QIC-3010.
+- sftape (aka ftape-2.x) has vanished. I didn't work on it for
+ ages. It is probably still possible to use the old code with
+ ftape-3.04, if one really needs it (BUT RECOMPILE IT)
+- zftape no longer alters the contents of already existing volume
+ table entries, which makes it possible to fill in missing fields,
+ like time stamps using some user space program.
+- ./contrib/vtblc/ contains such a program.
+- new perl script ./contrib/scripts/listtape that list the contents of a
+ floppy tape cartridge parsing the output of "mt volinfo" + "mt fsf"
+- the MTWEOF implementation has changed a little bit (after I had a
+ look at amanda). Calling MTWEOF while the tape is still held open
+ after writing something to the tape now will terminate the current
+ volume, and start a new one at the current position.
+- the volume table maintained by zftape now is a doubly linked list
+ that grows dynamically as needed.
+
+ formatting floppy tape cartridges
+ ---------------------------------
+ * there is a new user space formatting program that does most of the
+ dirty work in user space (auto-detect, computing the sector
+ coordinates, adjusting time stamps and statistics). It has a
+ simple command line interface.
+ * ftape-format.o has vanished, it has been folded into the low level
+ ftape.o module, and the ioctl interface into zftape.o. Most of the
+ complicated stuff has been moved to user space, so there was no
+ need for a separate module anymore.
+ * there is a new ioctl MTIOCFTCMD that sends a bare QIC-117 command
+ to the tape drive.
+ * there is a new mmap() feature to map the dma buffers into user
+ space to be used by the user level formatting program.
+ * Formatting of yet unformatted or totally degaussed cartridges
+ should be possible now. FIXME.
+
+===== Release notes for ftape-3.03b, <forgot the exact date> ====
+
+ftape-3.03b was released as a beta release only. Its main new feature
+was support of the DITTO-2GB drive. This was made possible by reverse
+engineering done by <fill in his name> after Iomega failed to support
+ftape. Although they had promised to do so (this makes me feel a bit
+sad and uncomfortable about Iomega).
+
+===== Release notes for ftape-3.03a, 22/05/97 ====
+
+- Finally fixed auto-un-loading of modules for kernels > 2.1.18
+- Add an "uninstall" target to the Makefile
+- removed the kdtime hack
+- texi2www didn't properly set the back-reference from a footnote back
+ to the regular text.
+
+ zftape specific
+ ---------------
+ * hide the old compression map volume. Taper doesn't accept the
+ presence of non-Taper volumes and Taper-written volume on the same
+ tape.
+ * EOD (End Of Data) handling was still broken: the expected behavior
+ is to return a zero byte count at the first attempt to read past
+ EOD, return a zero byte count at the second attempt to read past
+ EOD and THEN return -EIO.
+
+ ftape-format specific
+ ---------------------
+ * Detection of QIC-40 cartridges in select_tape_format() was broken
+ and made it impossible to format QIC-3010/3020 cartridges.
+ * There are strange "TR-1 Extra" cartridges out there which weren't
+ detected properly because the don't strictly conform to the
+ QIC-80, Rev. N, spec.
+
+===== Release notes for ftape-3.03, 30/04/97 =====
+
+- Removed kernel integration code from the package. I plan to provide
+ a package that can be integrated into the stock kernel separately
+ (hopefully soon).
+ As a result, a simple `make' command now will build everything.
+- ALL compile time configuration options have been moved to the file
+ `MCONFIG'.
+- Quite a few `low level' changes to allow formatting of cartridges.
+- formatting is implemented as a separate module `ftape-format.o'. The
+ modified `mt' program contains sample code that shows how to use it.
+- The VFS interface has been moved from the `ftape.o' module to the
+ high level modules `zftape.o' resp. `sftape.o'. `ftape.o' contains
+ the hardware support only.
+- A bit of /proc support for kernels > 2.1.28
+- Moved documentation to Doc subdir. INSTALL now contains some real
+ installation notes.
+- `install' target in Makefile.
+
+zftape specific:
+----------------
+
+- zftape works for large cartridges now ( > 2^31 bytes)
+- MTIOCVOLINFO and MTIOCGETSIZE now return the size in KILOBYTES,
+ NO LONGER in bytes.
+
+- permissions for write access to a cartridge have changed:
+ * zftape now also takes the file access mode into account
+ * zftape no longer allows writing in the middle of the recorded
+ media. The tape has to be positioned at BOT or EOD for write
+ access.
+
+- MTBSF has changed. It used to position at the beginning of the
+ previous file when called with count 1. This was different from the
+ expected behavior for other Un*x tape drivers (i.e. SCSI). MTBSF
+ with count 1 should merely position at the beginning of the current
+ volume. Fixed. As a result, `tar --verify' now produces the desired
+ result: it verifies the last written volume, not the pre-last
+ written volume.
+
+- The compression map has vanished --> no need for `mt erase' any
+ more. Fast seeking in a compressed volume is still be possible, but
+ takes slightly longer. As a side effect, you may experience an
+ additional volume showing up in front of all others for old
+ cartridges. This is the tape volume that holds the compression map.
+
+- The compression support for zftape has been moved to a separate
+ module `zft-compressor'. DON'T forget to load it before trying to
+ read back compressed volumes. The stock `zftape.o' module probes for
+ the module `zft-compressor' using the kerneld message channel; you
+ have to install `zft-compressor.o' in a place where modprobe can
+ find it if you want to use this.
+
+- New experimental feature that tries to get the broken down GMT time
+ from user space via a kernel daemon message channel. You need to
+ compile and start the `kdtime' daemon contained in the contrib
+ directory to use it. Needed (?) for time stamps in the header
+ segments and the volume table.
+
+- variable block size mode via MTSETBLK 0
+
+- keep modules locked in memory after the block size has been changed
+
+sftape specific:
+----------------
+
+- end of tape handling should be fixed, i.e. multi volume archives
+ written with `afio' can be read back now.
+
+
+===== Release notes for ftape-3.02a, 09/01/97 =====
+
+No big news:
+- call zft_init() resp. sft_init() when compiling the entire stuff
+ into the kernel image.
+- fix bug in ftape-setup.c when NO_TRACE_AT_ALL was defined.
+- fix bug in sftape-eof.c/zftape-eof.c for old kernels (1.2.*)
+- add support for new module interface for recent kernels
+
+===== Release notes for ftape-3.02, 16/12/96 =====
+- Fixed the `FDC unlock command failed' bug in fdc-io.c. When the FIFO
+ was already locked when ftape was loaded, ftape failed to unlock it.
+- Fixed compilation of `contrib/gnumt'. It now finds `mtio.h' even if
+ ftape is NOT included into the kernel source tree.
+- fc-10.c: include <asm/io.h> for inb() and outb().
+- ftape/sftape/zftape: all global variable now have either a `ftape_',
+ a `ft_', `sft_', `zft_' or `qic_' prefix to prevent name clashes
+ with other parts of the kernel when including ftape into the kernel
+ source tree.
+- Kerneld support has changed. `ftape' now searches for a module
+ `ftape-frontend' when none of the frontend (`sftape' or `zftape') is
+ loaded. Please refer to the `Installation/Loading ftape' section of
+ the TeXinfo manual.
+- Add load resp. boot-time configuration of ftape. There are now
+ variables ft_fdc_base, ft_fdc_dma and ft_fdc_irq corresponding to
+ the former FDC_BASE etc. compile time definitions. One can also use
+ the kernel command line parameters to configure the driver if it is
+ compiled into the kernel. Also, the FC-10/FC-20 support is load-time
+ configurable now as well as the MACH-II hack (ft_probe_fc10,
+ resp. ft_mach2). Please refer to the section `Installation/Configure
+ ftape' of the TeXinfo manual.
+- I removed the MODVERSIONS option from `Makefile.module'. Let me alone
+ with ftape and MODVERSIONS unless you include the ftape sources into
+ the kernel source tree.
+- new vendors in `vendors.h':
+ * HP Colorado T3000
+ * ComByte DoublePlay (including a bug fix for their broken
+ formatting software, thanks to whraven@njackn.com)
+ * Iomega DITTO 2GIG. NOTE: this drive cannot work with ftape because
+ the logical data layout of the cartridges used by this drive does
+ NOT conform to the QIC standards, it is a special Iomega specific
+ format. I've sent mail to Iomega but didn't receive an answer
+ yet. If you want this drive to be supported by ftape, ask Iomega
+ to give me information about it.
+- zftape:
+ * re-introduced the MTIOC_ZFTAPE_GETBLKSZ ioctl for compatibility
+ with zftape 1.06a and earlier. Please don't use it when writing
+ new software, use the MTIOCVOLINFO ioctl instead.
+ * Major overhaul of the code that updates the header segments. Never
+ change the tape label unless erasing the tape. Thus we almost
+ never need to write the header segments, unless we would modify
+ the bad sector map which isn't done yet. Updating of volume table
+ and compression map more secure now although it takes a bit
+ longer.
+ * Fixed bug when aborting a write operation with a signal: zftape
+ now finishes the current volume (i.e. writes an eof marker) at the
+ current position. It didn't before which led to somehow *strange*
+ behavior in this cases.
+ * Keep module locked in memory when using it with the non-rewinding
+ devices and the tape is not logical at BOT. Needed for kerneld
+ support.
+- sftape:
+ * Keep module locked in memory when using it with the non-rewinding
+ devices and the tape is not logical at BOT. Needed for kerneld
+ support.
+
+===== Release notes for ftape-3.01, 14/11/96 =====
+
+- Fixed silly bugs in ftape-3.00:
+ * MAKEDEV.ftape: major device number must be 27, not 23
+ * sftape/sftape-read.c: sftape_read_header_segments() called
+ itself recursively instead of calling ftape_read_header_segment()
+ * zftape/qic-vtbl.h: conversion of ftape's file marks to zftape's
+ internal volume table was broken.
+ * patches/2.x.x/linux-2.0.21.dif: my RCS (resp. CVS) system replaced
+ the `$Revison:' etc. macros in the `ftape.h' concerning part of the
+ patch :-( Fixed.
+ * info/ftape.info: Fixed misspellings (`cp' <-> `cp -r' etc.)
+ * when ftape/sftape or ftape/zftape was compiled into the kernel the
+ variable ftape_status was declared twice. Fixed.
+ * removed reference to undeclared variable kernel_version when not
+ compiling as module
+ * fixed a bug introduced by the use of bit-fields for some flags
+ (i.e. write_protected, no_cartridge, formatted)
+ * flag `header_read' is now reset correctly to zero when tape is
+ removed.
+- fixed a bug in sftape/sftape-eof.c that was already in the original
+ ftape code. MTFSF/BSF was not handled correctly when positioned
+ right before the file mark (think of tar)
+- Changed TRACE macros (following a suggestion of Marcin Dalecki) to use
+ the predefined __FUNCTION__ macro of GCC. Spares about 4k of code.
+- added new vendor id for Iomega DITTO 2GIG
+- fixed a bug already present in zftape-1.06 when aborting a write
+ with a signal: we now finish the current volume at that
+ position. Header segments remain NOT up to date until an explicit call
+ to MTREW or MTOFFL is done.
+
+===== Release notes for ftape-3.00, 14/10/96 =====
+
+- Merged ftape with zftape. There are three modules now:
+ ftape for the hardware support, sftape for the implementation of the
+ original ftape eof mark stuff and zftape that implements zftape's way
+ of handling things (compression, volume table, tape blocks of
+ constant length)
+- Documentation in TeXinfo format in the `info' subdirectory.
+- New ioctls for zftape. See zftape/zftape.h
+- Dummy formatting ioctl for ftape. See ftape.h
+- Kernel patch files for the 2.*.* series to include ftape-3.00 in the
+ kernel source tree. These includes a kernel compatible Config.in
+ script and fairly large online information for the kernel configure
+ script.
+- Support for compiling with Linux-1.2.13.
+- Modified GNU mt from their cpio package that can handle the new
+ ioctls.
+- ftape/sftape/zftape is kerneld save now!
+
+Notes on sftape:
+- sftape implements the eof handling code of the original ftape. If
+ you like to stick with the original ftape stuff, you have to use
+ this module, not zftape.
+- sftape is kerneld save, unlike the original ftape.
+- we keep the entire header segment now in memory, so no need to read
+ it before updating the header segments. Additional memory
+ consumption: 256 bytes.
+
+Notes for zftape:
+- zftape has support for tapes with format code 6 now, which use a
+ slightly different volume table format compared with other floppy
+ tapes.
+- new ioctls for zftape. Have a look at zftape/zftape.h
+- The internal volume table representation has changed for zftape. Old
+ cartridges are converted automatically.
+- zftape no longer uses compression map segments, which have vanished
+ from the QIC specs, but creates volume table entry that reserves
+ enough space for the compression map.
+- zftape is kerneld save now.
+- we keep the entire header segment now in memory, so no need to read
+ it before updating the header segments. Additional memory
+ consumption: 256 bytes.
+
+Notes for contrib/gnumt:
+- modified mt from the GNU cpio package that supports all the new
+ ioctls of zftape.
+Notes for contrib/swapout:
+- This contains the swapout.c program that was written by Kai
+ Harrekilde-Pederson. I simply added a Makefile.
+
+===== Release notes for ftape-2.10, 14/10/96 =====
+
+The ftape maintainer has changed.
+Kai Harrekilde-Petersen <khp@dolphinics.no>
+has resigned from maintaining ftape, and I,
+Claus-Justus Heine <claus@momo.math.rwth-aachen.de>,
+have taken over.
+
+- Added support for tapes with `format code 6', i.e. QIC-3020 tapes
+ with more than 2^16 segments.
+- merged changes made by Bas Laarhoven with ftape-2.09. Refer
+ to his release notes below. I've included them into this
+ file unchanged for your reference.
+- disabled call stack back trace for now. This new feature
+ introduced by the interim release 2.0.x still seems to
+ be buggy.
+- Tried to minimize differences between the ftape version
+ to be included into the kernel source tree and the standalone
+ module version.
+- Reintroduced support for Linux-1.2.13. Please refer to the
+ Install-guide.
+
+===== Release notes for ftape-2.09, 16/06/96 =====
+
+There aren't any really big news in this release, mostly just that I
+(the maintainer) have changed my email address (due to a new job). My
+new address is <khp@dolphinics.no>
+
+- The CLK_48MHZ and FDC_82078SL options has gone (all 2Mbps cards seem
+ to use a 48MHz oscillator anyway and I haven't heard of an 'SL
+ chip out there).
+- The S82078B has been `downgraded' to i82077AA compability.
+- TESTING option revived. Right now, it'll enable the (seriously broken)
+ 2Mbps code. If you enable it, you'll experience a tape drive that's
+ *really* out to lunch!
+- Some (bold) changes in the init code. Please notify me if they
+ break things for you.
+
===== Release notes for ftape-2.08, 14/03/96 =====
If you correct a problem with ftape, please send your patch to
@@ -567,3 +956,13 @@ Have fun,
Bas.
----
+ LocalWords: ftape MCONFIG mt VFS zftape resp sftape proc subdir MTIOCVOLINFO
+ LocalWords: MTIOCGETSIZE BOT EOD MTBSF zft kerneld modprobe kdtime contrib TR
+ LocalWords: MTSETBLK afio uninstall texi www EIO QIC init sft eof aka dma GB
+ LocalWords: SIGKILL MTIOCFTCMD mmap Iomega FDC fdc io gnumt mtio fc asm inb
+ LocalWords: outb ft qic frontend TeXinfo irq mach MODVERSIONS CONFIG html dvi
+ LocalWords: usr doc SMP Mb Dunno FIXME vtblc perl listtape volinfo fsf MTWEOF
+ LocalWords: amanda degaussed ComByte DoublePlay whraven njackn com MTIOC vtbl
+ LocalWords: GETBLKSZ MAKEDEV zftape's linux dif CVS Revison cp MTREW MTOFFL
+ LocalWords: MTFSF BSF Marcin Dalecki GCC Config cpio swapout Kai Harrekilde
+ LocalWords: Pederson khp dolphinics Justus claus momo rwth aachen Laarhoven
diff --git a/drivers/char/ftape/calibr.c b/drivers/char/ftape/calibr.c
deleted file mode 100644
index bd41bddb8..000000000
--- a/drivers/char/ftape/calibr.c
+++ /dev/null
@@ -1,183 +0,0 @@
-/* Yo, Emacs! we're -*- Linux-C -*-
- *
- * Copyright (C) 1993-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING. If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- * GP calibration routine for processor speed dependent
- * functions.
- */
-
-#include <linux/errno.h>
-#include <linux/sched.h>
-#include <linux/ftape.h>
-#include <asm/system.h>
-#include <asm/io.h>
-
-#include "tracing.h"
-#include "calibr.h"
-#include "fdc-io.h"
-
-#undef DEBUG
-
-unsigned timestamp(void)
-{
- unsigned count;
- unsigned long flags;
-
- save_flags(flags);
- cli();
- outb_p(0x00, 0x43); /* latch the count ASAP */
- count = inb_p(0x40); /* read the latched count */
- count |= inb(0x40) << 8;
- restore_flags(flags);
- return (LATCH - count); /* normal: downcounter */
-}
-
-int timediff(int t0, int t1)
-{
- /* Calculate difference in usec for timestamp results t0 & t1.
- * Note that the maximum timespan allowed is 1/HZ or we'll lose ticks!
- */
- if (t1 < t0) {
- t1 += LATCH;
- }
- return (1000 * (t1 - t0)) / ((CLOCK_TICK_RATE + 500) / 1000);
-}
-
-/* To get an indication of the I/O performance,
- * measure the duration of the inb() function.
- */
-void time_inb(void)
-{
- TRACE_FUN(8, "time_inb");
- int i;
- int t0, t1;
- unsigned long flags;
- int status;
-
- save_flags(flags);
- cli();
- t0 = timestamp();
- for (i = 0; i < 1000; ++i) {
- status = inb(fdc.msr);
- }
- t1 = timestamp();
- restore_flags(flags);
- if (t1 - t0 <= 0) {
- t1 += LATCH;
- }
- TRACEx1(4, "inb() duration: %d nsec", timediff(t0, t1));
- TRACE_EXIT;
-}
-
-/* Haven't studied on why, but there sometimes is a problem
- * with the tick timer readout. The two bytes get swapped.
- * This hack solves that problem by doing one extra input.
- */
-void fix_clock(void)
-{
- TRACE_FUN(8, "fix_clock");
- int t;
- int i;
-
- for (i = 0; i < 1000; ++i) {
- t = timestamp();
- if (t < 0) {
- inb_p(0x40); /* get in sync again */
- TRACE(2, "clock counter fixed");
- break;
- }
- }
- TRACE_EXIT;
-}
-
-/*
- * Input: function taking int count as parameter.
- * pointers to calculated calibration variables.
- */
-int calibrate(char *name, void (*fun) (int), int *calibr_count, int *calibr_time)
-{
- TRACE_FUN(5, "calibrate");
- static int first_time = 1;
- int i;
- int old_tc = 0;
- int old_count = 1;
- int old_time = 1;
-
- if (first_time) { /* get idea of I/O performance */
- fix_clock();
- time_inb();
- first_time = 0;
- }
- /* value of timeout must be set so that on very slow systems
- * it will give a time less than one jiffy, and on
- * very fast systems it'll give reasonable precision.
- */
-
- *calibr_count = 10;
- for (i = 0; i < 15; ++i) {
- int t0, t1;
- unsigned long flags;
- int once;
- int multiple;
- int tc;
-
- *calibr_time = *calibr_count; /* set TC to 1 */
- fun(0); /* dummy, get code into cache */
- save_flags(flags);
- cli();
- t0 = timestamp();
- fun(0); /* overhead + one test */
- t1 = timestamp();
- if (t1 < t0) {
- t1 += LATCH;
- }
- once = t1 - t0;
- t0 = timestamp();
- fun(*calibr_count); /* overhead + multiple tests */
- t1 = timestamp();
- if (t1 < t0) {
- t1 += LATCH;
- }
- multiple = t1 - t0;
- restore_flags(flags);
- *calibr_time = (10000 * (multiple - once)) / (CLOCK_TICK_RATE / 100);
- --*calibr_count; /* because delta corresponds to this count */
- tc = (1000 * *calibr_time) / *calibr_count;
- TRACEx4(8, "once:%4d us,%5d times:%6d us, TC:%5d ns",
- (10000 * once) / (CLOCK_TICK_RATE / 100),
- *calibr_count,
- (10000 * multiple) / (CLOCK_TICK_RATE / 100),
- tc);
- /*
- * increase the count until the resulting time nears 2/HZ,
- * then the tc will drop sharply because we lose LATCH counts.
- */
- if (tc <= old_tc / 2) {
- *calibr_time = old_time;
- *calibr_count = old_count;
- break;
- }
- old_tc = tc;
- old_count = *calibr_count;
- old_time = *calibr_time;
- *calibr_count *= 2;
- }
- TRACEx3(4, "TC for `%s()' = %d nsec (at %d counts)",
- name, (1000 * *calibr_time) / *calibr_count, *calibr_count);
- TRACE_EXIT;
- return 0;
-}
diff --git a/drivers/char/ftape/compressor/Makefile b/drivers/char/ftape/compressor/Makefile
new file mode 100644
index 000000000..fde3fd10b
--- /dev/null
+++ b/drivers/char/ftape/compressor/Makefile
@@ -0,0 +1,48 @@
+#
+# Copyright (C) 1997 Claus-Justus Heine.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; see the file COPYING. If not, write to
+# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+# $Source: /homes/cvs/ftape-stacked/ftape/compressor/Makefile,v $
+# $Revision: 1.1 $
+# $Date: 1997/10/05 19:12:28 $
+#
+# Makefile for the optional compressor for th zftape VFS
+# interface to the QIC-40/80/3010/3020 floppy-tape driver for
+# Linux.
+#
+
+#
+# This isn't used inside the kernel, only for my private development
+# version
+#
+ifndef TOPDIR
+TOPDIR=../..
+include $(TOPDIR)/MCONFIG
+endif
+
+O_TARGET := zft-compressor.o
+O_OBJS = zftape-compress.o lzrw3.o
+
+M_OBJS = $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
+
+#
+# sorry, a special rule.
+#
+lzrw3.o: lzrw3.c
+ $(CC) $(CFLAGS) -O6 -funroll-all-loops -c $<
+
diff --git a/drivers/char/ftape/compressor/lzrw3.c b/drivers/char/ftape/compressor/lzrw3.c
new file mode 100644
index 000000000..64ad32556
--- /dev/null
+++ b/drivers/char/ftape/compressor/lzrw3.c
@@ -0,0 +1,750 @@
+/*
+ * $Source: /homes/cvs/ftape-stacked/ftape/compressor/lzrw3.c,v $
+ * $Revision: 1.1 $
+ * $Date: 1997/10/05 19:12:29 $
+ *
+ * Implementation of Ross Williams lzrw3 algorithm. Adaption for zftape.
+ *
+ */
+
+#include "../compressor/lzrw3.h" /* Defines single exported function "compress". */
+
+/******************************************************************************/
+/* */
+/* LZRW3.C */
+/* */
+/******************************************************************************/
+/* */
+/* Author : Ross Williams. */
+/* Date : 30-Jun-1991. */
+/* Release : 1. */
+/* */
+/******************************************************************************/
+/* */
+/* This file contains an implementation of the LZRW3 data compression */
+/* algorithm in C. */
+/* */
+/* The algorithm is a general purpose compression algorithm that runs fast */
+/* and gives reasonable compression. The algorithm is a member of the Lempel */
+/* Ziv family of algorithms and bases its compression on the presence in the */
+/* data of repeated substrings. */
+/* */
+/* This algorithm is unpatented and the code is public domain. As the */
+/* algorithm is based on the LZ77 class of algorithms, it is unlikely to be */
+/* the subject of a patent challenge. */
+/* */
+/* Unlike the LZRW1 and LZRW1-A algorithms, the LZRW3 algorithm is */
+/* deterministic and is guaranteed to yield the same compressed */
+/* representation for a given file each time it is run. */
+/* */
+/* The LZRW3 algorithm was originally designed and implemented */
+/* by Ross Williams on 31-Dec-1990. */
+/* */
+/* Here are the results of applying this code, compiled under THINK C 4.0 */
+/* and running on a Mac-SE (8MHz 68000), to the standard calgary corpus. */
+/* */
+/* +----------------------------------------------------------------+ */
+/* | DATA COMPRESSION TEST | */
+/* | ===================== | */
+/* | Time of run : Sun 30-Jun-1991 09:31PM | */
+/* | Timing accuracy : One part in 100 | */
+/* | Context length : 262144 bytes (= 256.0000K) | */
+/* | Test suite : Calgary Corpus Suite | */
+/* | Files in suite : 14 | */
+/* | Algorithm : LZRW3 | */
+/* | Note: All averages are calculated from the un-rounded values. | */
+/* +----------------------------------------------------------------+ */
+/* | File Name Length CxB ComLen %Remn Bits Com K/s Dec K/s | */
+/* | ---------- ------ --- ------ ----- ---- ------- ------- | */
+/* | rpus:Bib.D 111261 1 55033 49.5 3.96 19.46 32.27 | */
+/* | us:Book1.D 768771 3 467962 60.9 4.87 17.03 31.07 | */
+/* | us:Book2.D 610856 3 317102 51.9 4.15 19.39 34.15 | */
+/* | rpus:Geo.D 102400 1 82424 80.5 6.44 11.65 18.18 | */
+/* | pus:News.D 377109 2 205670 54.5 4.36 17.14 27.47 | */
+/* | pus:Obj1.D 21504 1 13027 60.6 4.85 13.40 18.95 | */
+/* | pus:Obj2.D 246814 1 116286 47.1 3.77 19.31 30.10 | */
+/* | s:Paper1.D 53161 1 27522 51.8 4.14 18.60 31.15 | */
+/* | s:Paper2.D 82199 1 45160 54.9 4.40 18.45 32.84 | */
+/* | rpus:Pic.D 513216 2 122388 23.8 1.91 35.29 51.05 | */
+/* | us:Progc.D 39611 1 19669 49.7 3.97 18.87 30.64 | */
+/* | us:Progl.D 71646 1 28247 39.4 3.15 24.34 40.66 | */
+/* | us:Progp.D 49379 1 19377 39.2 3.14 23.91 39.23 | */
+/* | us:Trans.D 93695 1 33481 35.7 2.86 25.48 40.37 | */
+/* +----------------------------------------------------------------+ */
+/* | Average 224401 1 110953 50.0 4.00 20.17 32.72 | */
+/* +----------------------------------------------------------------+ */
+/* */
+/******************************************************************************/
+
+/******************************************************************************/
+
+/* The following structure is returned by the "compress" function below when */
+/* the user asks the function to return identifying information. */
+/* The most important field in the record is the working memory field which */
+/* tells the calling program how much working memory should be passed to */
+/* "compress" when it is called to perform a compression or decompression. */
+/* LZRW3 uses the same amount of memory during compression and decompression. */
+/* For more information on this structure see "compress.h". */
+
+#define U(X) ((ULONG) X)
+#define SIZE_P_BYTE (U(sizeof(UBYTE *)))
+#define SIZE_WORD (U(sizeof(UWORD )))
+#define ALIGNMENT_FUDGE (U(16))
+#define MEM_REQ ( U(4096)*(SIZE_P_BYTE) + ALIGNMENT_FUDGE )
+
+static struct compress_identity identity =
+{
+ U(0x032DDEA8), /* Algorithm identification number. */
+ MEM_REQ, /* Working memory (bytes) required. */
+ "LZRW3", /* Name of algorithm. */
+ "1.0", /* Version number of algorithm. */
+ "31-Dec-1990", /* Date of algorithm. */
+ "Public Domain", /* Copyright notice. */
+ "Ross N. Williams", /* Author of algorithm. */
+ "Renaissance Software", /* Affiliation of author. */
+ "Public Domain" /* Vendor of algorithm. */
+};
+
+LOCAL void compress_compress (UBYTE *,UBYTE *,ULONG,UBYTE *, LONG *);
+LOCAL void compress_decompress(UBYTE *,UBYTE *,LONG, UBYTE *, ULONG *);
+
+/******************************************************************************/
+
+/* This function is the only function exported by this module. */
+/* Depending on its first parameter, the function can be requested to */
+/* compress a block of memory, decompress a block of memory, or to identify */
+/* itself. For more information, see the specification file "compress.h". */
+
+EXPORT void lzrw3_compress(action,wrk_mem,src_adr,src_len,dst_adr,p_dst_len)
+UWORD action; /* Action to be performed. */
+UBYTE *wrk_mem; /* Address of working memory we can use. */
+UBYTE *src_adr; /* Address of input data. */
+LONG src_len; /* Length of input data. */
+UBYTE *dst_adr; /* Address to put output data. */
+void *p_dst_len; /* Address of longword for length of output data. */
+{
+ switch (action)
+ {
+ case COMPRESS_ACTION_IDENTITY:
+ *((struct compress_identity **)p_dst_len)= &identity;
+ break;
+ case COMPRESS_ACTION_COMPRESS:
+ compress_compress(wrk_mem,src_adr,src_len,dst_adr,(LONG *)p_dst_len);
+ break;
+ case COMPRESS_ACTION_DECOMPRESS:
+ compress_decompress(wrk_mem,src_adr,src_len,dst_adr,(LONG *)p_dst_len);
+ break;
+ }
+}
+
+/******************************************************************************/
+/* */
+/* BRIEF DESCRIPTION OF THE LZRW3 ALGORITHM */
+/* ======================================== */
+/* The LZRW3 algorithm is identical to the LZRW1-A algorithm except that */
+/* instead of transmitting history offsets, it transmits hash table indexes. */
+/* In order to decode the indexes, the decompressor must maintain an */
+/* identical hash table. Copy items are straightforward:when the decompressor */
+/* receives a copy item, it simply looks up the hash table to translate the */
+/* index into a pointer into the data already decompressed. To update the */
+/* hash table, it replaces the same table entry with a pointer to the start */
+/* of the newly decoded phrase. The tricky part is with literal items, for at */
+/* the time that the decompressor receives a literal item the decompressor */
+/* does not have the three bytes in the Ziv (that the compressor has) to */
+/* perform the three-byte hash. To solve this problem, in LZRW3, both the */
+/* compressor and decompressor are wired up so that they "buffer" these */
+/* literals and update their hash tables only when three bytes are available. */
+/* This makes the maximum buffering 2 bytes. */
+/* */
+/* Replacement of offsets by hash table indexes yields a few percent extra */
+/* compression at the cost of some speed. LZRW3 is slower than LZRW1, LZRW1-A */
+/* and LZRW2, but yields better compression. */
+/* */
+/* Extra compression could be obtained by using a hash table of depth two. */
+/* However, increasing the depth above one incurs a significant decrease in */
+/* compression speed which was not considered worthwhile. Another reason for */
+/* keeping the depth down to one was to allow easy comparison with the */
+/* LZRW1-A and LZRW2 algorithms so as to demonstrate the exact effect of the */
+/* use of direct hash indexes. */
+/* */
+/* +---+ */
+/* |___|4095 */
+/* |___| */
+/* +---------------------*_|<---+ /----+---\ */
+/* | |___| +---|Hash | */
+/* | |___| |Function| */
+/* | |___| \--------/ */
+/* | |___|0 ^ */
+/* | +---+ | */
+/* | Hash +-----+ */
+/* | Table | */
+/* | --- */
+/* v ^^^ */
+/* +-------------------------------------|----------------+ */
+/* |||||||||||||||||||||||||||||||||||||||||||||||||||||||| */
+/* +-------------------------------------|----------------+ */
+/* | |1......18| | */
+/* |<------- Lempel=History ------------>|<--Ziv-->| | */
+/* | (=bytes already processed) |<-Still to go-->| */
+/* |<-------------------- INPUT BLOCK ------------------->| */
+/* */
+/* The diagram above for LZRW3 looks almost identical to the diagram for */
+/* LZRW1. The difference is that in LZRW3, the compressor transmits hash */
+/* table indices instead of Lempel offsets. For this to work, the */
+/* decompressor must maintain a hash table as well as the compressor and both */
+/* compressor and decompressor must "buffer" literals, as the decompressor */
+/* cannot hash phrases commencing with a literal until another two bytes have */
+/* arrived. */
+/* */
+/* LZRW3 Algorithm Execution Summary */
+/* --------------------------------- */
+/* 1. Hash the first three bytes of the Ziv to yield a hash table index h. */
+/* 2. Look up the hash table yielding history pointer p. */
+/* 3. Match where p points with the Ziv. If there is a match of three or */
+/* more bytes, code those bytes (in the Ziv) as a copy item, otherwise */
+/* code the next byte in the Ziv as a literal item. */
+/* 4. Update the hash table as possible subject to the constraint that only */
+/* phrases commencing three bytes back from the Ziv can be hashed and */
+/* entered into the hash table. (This enables the decompressor to keep */
+/* pace). See the description and code for more details. */
+/* */
+/******************************************************************************/
+/* */
+/* DEFINITION OF COMPRESSED FILE FORMAT */
+/* ==================================== */
+/* * A compressed file consists of a COPY FLAG followed by a REMAINDER. */
+/* * The copy flag CF uses up four bytes with the first byte being the */
+/* least significant. */
+/* * If CF=1, then the compressed file represents the remainder of the file */
+/* exactly. Otherwise CF=0 and the remainder of the file consists of zero */
+/* or more GROUPS, each of which represents one or more bytes. */
+/* * Each group consists of two bytes of CONTROL information followed by */
+/* sixteen ITEMs except for the last group which can contain from one */
+/* to sixteen items. */
+/* * An item can be either a LITERAL item or a COPY item. */
+/* * Each item corresponds to a bit in the control bytes. */
+/* * The first control byte corresponds to the first 8 items in the group */
+/* with bit 0 corresponding to the first item in the group and bit 7 to */
+/* the eighth item in the group. */
+/* * The second control byte corresponds to the second 8 items in the group */
+/* with bit 0 corresponding to the ninth item in the group and bit 7 to */
+/* the sixteenth item in the group. */
+/* * A zero bit in a control word means that the corresponding item is a */
+/* literal item. A one bit corresponds to a copy item. */
+/* * A literal item consists of a single byte which represents itself. */
+/* * A copy item consists of two bytes that represent from 3 to 18 bytes. */
+/* * The first byte in a copy item will be denoted C1. */
+/* * The second byte in a copy item will be denoted C2. */
+/* * Bits will be selected using square brackets. */
+/* For example: C1[0..3] is the low nibble of the first control byte. */
+/* of copy item C1. */
+/* * The LENGTH of a copy item is defined to be C1[0..3]+3 which is a number */
+/* in the range [3,18]. */
+/* * The INDEX of a copy item is defined to be C1[4..7]*256+C2[0..8] which */
+/* is a number in the range [0,4095]. */
+/* * A copy item represents the sequence of bytes */
+/* text[POS-OFFSET..POS-OFFSET+LENGTH-1] where */
+/* text is the entire text of the uncompressed string. */
+/* POS is the index in the text of the character following the */
+/* string represented by all the items preceeding the item */
+/* being defined. */
+/* OFFSET is obtained from INDEX by looking up the hash table. */
+/* */
+/******************************************************************************/
+
+/* The following #define defines the length of the copy flag that appears at */
+/* the start of the compressed file. The value of four bytes was chosen */
+/* because the fast_copy routine on my Macintosh runs faster if the source */
+/* and destination blocks are relatively longword aligned. */
+/* The actual flag data appears in the first byte. The rest are zeroed so as */
+/* to normalize the compressed representation (i.e. not non-deterministic). */
+#define FLAG_BYTES 4
+
+/* The following #defines define the meaning of the values of the copy */
+/* flag at the start of the compressed file. */
+#define FLAG_COMPRESS 0 /* Signals that output was result of compression. */
+#define FLAG_COPY 1 /* Signals that output was simply copied over. */
+
+/* The 68000 microprocessor (on which this algorithm was originally developed */
+/* is fussy about non-aligned arrays of words. To avoid these problems the */
+/* following macro can be used to "waste" from 0 to 3 bytes so as to align */
+/* the argument pointer. */
+#define ULONG_ALIGN_UP(X) ((((ULONG)X)+sizeof(ULONG)-1)&~(sizeof(ULONG)-1))
+
+
+/* The following constant defines the maximum length of an uncompressed item. */
+/* This definition must not be changed; its value is hardwired into the code. */
+/* The longest number of bytes that can be spanned by a single item is 18 */
+/* for the longest copy item. */
+#define MAX_RAW_ITEM (18)
+
+/* The following constant defines the maximum length of an uncompressed group.*/
+/* This definition must not be changed; its value is hardwired into the code. */
+/* A group contains at most 16 items which explains this definition. */
+#define MAX_RAW_GROUP (16*MAX_RAW_ITEM)
+
+/* The following constant defines the maximum length of a compressed group. */
+/* This definition must not be changed; its value is hardwired into the code. */
+/* A compressed group consists of two control bytes followed by up to 16 */
+/* compressed items each of which can have a maximum length of two bytes. */
+#define MAX_CMP_GROUP (2+16*2)
+
+/* The following constant defines the number of entries in the hash table. */
+/* This definition must not be changed; its value is hardwired into the code. */
+#define HASH_TABLE_LENGTH (4096)
+
+/* LZRW3, unlike LZRW1(-A), must initialize its hash table so as to enable */
+/* the compressor and decompressor to stay in step maintaining identical hash */
+/* tables. In an early version of the algorithm, the tables were simply */
+/* initialized to zero and a check for zero was included just before the */
+/* matching code. However, this test costs time. A better solution is to */
+/* initialize all the entries in the hash table to point to a constant */
+/* string. The decompressor does the same. This solution requires no extra */
+/* test. The contents of the string do not matter so long as the string is */
+/* the same for the compressor and decompressor and contains at least */
+/* MAX_RAW_ITEM bytes. I chose consecutive decimal digits because they do not */
+/* have white space problems (e.g. there is no chance that the compiler will */
+/* replace more than one space by a TAB) and because they make the length of */
+/* the string obvious by inspection. */
+#define START_STRING_18 ((UBYTE *) "123456789012345678")
+
+/* In this algorithm, hash values have to be calculated at more than one */
+/* point. The following macro neatens the code up for this. */
+#define HASH(PTR) \
+ (((40543*(((*(PTR))<<8)^((*((PTR)+1))<<4)^(*((PTR)+2))))>>4) & 0xFFF)
+
+/******************************************************************************/
+
+LOCAL void compress_compress
+ (p_wrk_mem,p_src_first,src_len,p_dst_first,p_dst_len)
+/* Input : Hand over the required amount of working memory in p_wrk_mem. */
+/* Input : Specify input block using p_src_first and src_len. */
+/* Input : Point p_dst_first to the start of the output zone (OZ). */
+/* Input : Point p_dst_len to a ULONG to receive the output length. */
+/* Input : Input block and output zone must not overlap. */
+/* Output : Length of output block written to *p_dst_len. */
+/* Output : Output block in Mem[p_dst_first..p_dst_first+*p_dst_len-1]. May */
+/* Output : write in OZ=Mem[p_dst_first..p_dst_first+src_len+MAX_CMP_GROUP-1].*/
+/* Output : Upon completion guaranteed *p_dst_len<=src_len+FLAG_BYTES. */
+UBYTE *p_wrk_mem;
+UBYTE *p_src_first;
+ULONG src_len;
+UBYTE *p_dst_first;
+LONG *p_dst_len;
+{
+ /* p_src and p_dst step through the source and destination blocks. */
+ register UBYTE *p_src = p_src_first;
+ register UBYTE *p_dst = p_dst_first;
+
+ /* The following variables are never modified and are used in the */
+ /* calculations that determine when the main loop terminates. */
+ UBYTE *p_src_post = p_src_first+src_len;
+ UBYTE *p_dst_post = p_dst_first+src_len;
+ UBYTE *p_src_max1 = p_src_first+src_len-MAX_RAW_ITEM;
+ UBYTE *p_src_max16 = p_src_first+src_len-MAX_RAW_ITEM*16;
+
+ /* The variables 'p_control' and 'control' are used to buffer control bits. */
+ /* Before each group is processed, the next two bytes of the output block */
+ /* are set aside for the control word for the group about to be processed. */
+ /* 'p_control' is set to point to the first byte of that word. Meanwhile, */
+ /* 'control' buffers the control bits being generated during the processing */
+ /* of the group. Instead of having a counter to keep track of how many items */
+ /* have been processed (=the number of bits in the control word), at the */
+ /* start of each group, the top word of 'control' is filled with 1 bits. */
+ /* As 'control' is shifted for each item, the 1 bits in the top word are */
+ /* absorbed or destroyed. When they all run out (i.e. when the top word is */
+ /* all zero bits, we know that we are at the end of a group. */
+# define TOPWORD 0xFFFF0000
+ UBYTE *p_control;
+ register ULONG control=TOPWORD;
+
+ /* THe variable 'hash' always points to the first element of the hash table. */
+ UBYTE **hash= (UBYTE **) ULONG_ALIGN_UP(p_wrk_mem);
+
+ /* The following two variables represent the literal buffer. p_h1 points to */
+ /* the hash table entry corresponding to the youngest literal. p_h2 points */
+ /* to the hash table entry corresponding to the second youngest literal. */
+ /* Note: p_h1=0=>p_h2=0 because zero values denote absence of a pending */
+ /* literal. The variables are initialized to zero meaning an empty "buffer". */
+ UBYTE **p_h1=0;
+ UBYTE **p_h2=0;
+
+ /* To start, we write the flag bytes. Being optimistic, we set the flag to */
+ /* FLAG_COMPRESS. The remaining flag bytes are zeroed so as to keep the */
+ /* algorithm deterministic. */
+ *p_dst++=FLAG_COMPRESS;
+ {UWORD i; for (i=2;i<=FLAG_BYTES;i++) *p_dst++=0;}
+
+ /* Reserve the first word of output as the control word for the first group. */
+ /* Note: This is undone at the end if the input block is empty. */
+ p_control=p_dst; p_dst+=2;
+
+ /* Initialize all elements of the hash table to point to a constant string. */
+ /* Use of an unrolled loop speeds this up considerably. */
+ {UWORD i; UBYTE **p_h=hash;
+# define ZH *p_h++=START_STRING_18
+ for (i=0;i<256;i++) /* 256=HASH_TABLE_LENGTH/16. */
+ {ZH;ZH;ZH;ZH;
+ ZH;ZH;ZH;ZH;
+ ZH;ZH;ZH;ZH;
+ ZH;ZH;ZH;ZH;}
+ }
+
+ /* The main loop processes either 1 or 16 items per iteration. As its */
+ /* termination logic is complicated, I have opted for an infinite loop */
+ /* structure containing 'break' and 'goto' statements. */
+ while (TRUE)
+ {/* Begin main processing loop. */
+
+ /* Note: All the variables here except unroll should be defined within */
+ /* the inner loop. Unfortunately the loop hasn't got a block. */
+ register UBYTE *p; /* Scans through targ phrase during matching. */
+ register UBYTE *p_ziv= NULL ; /* Points to first byte of current Ziv. */
+ register UWORD unroll; /* Loop counter for unrolled inner loop. */
+ register UWORD index; /* Index of current hash table entry. */
+ register UBYTE **p_h0 = NULL ; /* Pointer to current hash table entry. */
+
+ /* Test for overrun and jump to overrun code if necessary. */
+ if (p_dst>p_dst_post)
+ goto overrun;
+
+ /* The following cascade of if statements efficiently catches and deals */
+ /* with varying degrees of closeness to the end of the input block. */
+ /* When we get very close to the end, we stop updating the table and */
+ /* code the remaining bytes as literals. This makes the code simpler. */
+ unroll=16;
+ if (p_src>p_src_max16)
+ {
+ unroll=1;
+ if (p_src>p_src_max1)
+ {
+ if (p_src==p_src_post)
+ break;
+ else
+ goto literal;
+ }
+ }
+
+ /* This inner unrolled loop processes 'unroll' (whose value is either 1 */
+ /* or 16) items. I have chosen to implement this loop with labels and */
+ /* gotos to heighten the ease with which the loop may be implemented with */
+ /* a single decrement and branch instruction in assembly language and */
+ /* also because the labels act as highly readable place markers. */
+ /* (Also because we jump into the loop for endgame literals (see above)). */
+
+ begin_unrolled_loop:
+
+ /* To process the next phrase, we hash the next three bytes and use */
+ /* the resultant hash table index to look up the hash table. A pointer */
+ /* to the entry is stored in p_h0 so as to avoid an array lookup. The */
+ /* hash table entry *p_h0 is looked up yielding a pointer p to a */
+ /* potential match of the Ziv in the history. */
+ index=HASH(p_src);
+ p_h0=&hash[index];
+ p=*p_h0;
+
+ /* Having looked up the candidate position, we are in a position to */
+ /* attempt a match. The match loop has been unrolled using the PS */
+ /* macro so that failure within the first three bytes automatically */
+ /* results in the literal branch being taken. The coding is simple. */
+ /* p_ziv saves p_src so we can let p_src wander. */
+# define PS *p++!=*p_src++
+ p_ziv=p_src;
+ if (PS || PS || PS)
+ {
+ /* Literal. */
+
+ /* Code the literal byte as itself and a zero control bit. */
+ p_src=p_ziv; literal: *p_dst++=*p_src++; control&=0xFFFEFFFF;
+
+ /* We have just coded a literal. If we had two pending ones, that */
+ /* makes three and we can update the hash table. */
+ if (p_h2!=0)
+ {*p_h2=p_ziv-2;}
+
+ /* In any case, rotate the hash table pointers for next time. */
+ p_h2=p_h1; p_h1=p_h0;
+
+ }
+ else
+ {
+ /* Copy */
+
+ /* Match up to 15 remaining bytes using an unrolled loop and code. */
+#if 0
+ PS || PS || PS || PS || PS || PS || PS || PS ||
+ PS || PS || PS || PS || PS || PS || PS || p_src++;
+#else
+ if (
+ !( PS || PS || PS || PS || PS || PS || PS || PS ||
+ PS || PS || PS || PS || PS || PS || PS )
+ ) p_src++;
+#endif
+ *p_dst++=((index&0xF00)>>4)|(--p_src-p_ziv-3);
+ *p_dst++=index&0xFF;
+
+ /* As we have just coded three bytes, we are now in a position to */
+ /* update the hash table with the literal bytes that were pending */
+ /* upon the arrival of extra context bytes. */
+ if (p_h1!=0)
+ {
+ if (p_h2!=0)
+ {*p_h2=p_ziv-2; p_h2=0;}
+ *p_h1=p_ziv-1; p_h1=0;
+ }
+
+ /* In any case, we can update the hash table based on the current */
+ /* position as we just coded at least three bytes in a copy items. */
+ *p_h0=p_ziv;
+
+ }
+ control>>=1;
+
+ /* This loop is all set up for a decrement and jump instruction! */
+#ifndef linux
+` end_unrolled_loop: if (--unroll) goto begin_unrolled_loop;
+#else
+ /* end_unrolled_loop: */ if (--unroll) goto begin_unrolled_loop;
+#endif
+
+ /* At this point it will nearly always be the end of a group in which */
+ /* case, we have to do some control-word processing. However, near the */
+ /* end of the input block, the inner unrolled loop is only executed once. */
+ /* This necessitates the 'if' test. */
+ if ((control&TOPWORD)==0)
+ {
+ /* Write the control word to the place we saved for it in the output. */
+ *p_control++= control &0xFF;
+ *p_control = (control>>8) &0xFF;
+
+ /* Reserve the next word in the output block for the control word */
+ /* for the group about to be processed. */
+ p_control=p_dst; p_dst+=2;
+
+ /* Reset the control bits buffer. */
+ control=TOPWORD;
+ }
+
+ } /* End main processing loop. */
+
+ /* After the main processing loop has executed, all the input bytes have */
+ /* been processed. However, the control word has still to be written to the */
+ /* word reserved for it in the output at the start of the most recent group. */
+ /* Before writing, the control word has to be shifted so that all the bits */
+ /* are in the right place. The "empty" bit positions are filled with 1s */
+ /* which partially fill the top word. */
+ while(control&TOPWORD) control>>=1;
+ *p_control++= control &0xFF;
+ *p_control++=(control>>8) &0xFF;
+
+ /* If the last group contained no items, delete the control word too. */
+ if (p_control==p_dst) p_dst-=2;
+
+ /* Write the length of the output block to the dst_len parameter and return. */
+ *p_dst_len=p_dst-p_dst_first;
+ return;
+
+ /* Jump here as soon as an overrun is detected. An overrun is defined to */
+ /* have occurred if p_dst>p_dst_first+src_len. That is, the moment the */
+ /* length of the output written so far exceeds the length of the input block.*/
+ /* The algorithm checks for overruns at least at the end of each group */
+ /* which means that the maximum overrun is MAX_CMP_GROUP bytes. */
+ /* Once an overrun occurs, the only thing to do is to set the copy flag and */
+ /* copy the input over. */
+ overrun:
+#if 0
+ *p_dst_first=FLAG_COPY;
+ fast_copy(p_src_first,p_dst_first+FLAG_BYTES,src_len);
+ *p_dst_len=src_len+FLAG_BYTES;
+#else
+ fast_copy(p_src_first,p_dst_first,src_len);
+ *p_dst_len= -src_len; /* return a negative number to indicate uncompressed data */
+#endif
+}
+
+/******************************************************************************/
+
+LOCAL void compress_decompress
+ (p_wrk_mem,p_src_first,src_len,p_dst_first,p_dst_len)
+/* Input : Hand over the required amount of working memory in p_wrk_mem. */
+/* Input : Specify input block using p_src_first and src_len. */
+/* Input : Point p_dst_first to the start of the output zone. */
+/* Input : Point p_dst_len to a ULONG to receive the output length. */
+/* Input : Input block and output zone must not overlap. User knows */
+/* Input : upperbound on output block length from earlier compression. */
+/* Input : In any case, maximum expansion possible is nine times. */
+/* Output : Length of output block written to *p_dst_len. */
+/* Output : Output block in Mem[p_dst_first..p_dst_first+*p_dst_len-1]. */
+/* Output : Writes only in Mem[p_dst_first..p_dst_first+*p_dst_len-1]. */
+UBYTE *p_wrk_mem;
+UBYTE *p_src_first;
+LONG src_len;
+UBYTE *p_dst_first;
+ULONG *p_dst_len;
+{
+ /* Byte pointers p_src and p_dst scan through the input and output blocks. */
+ register UBYTE *p_src = p_src_first+FLAG_BYTES;
+ register UBYTE *p_dst = p_dst_first;
+ /* we need to avoid a SEGV when trying to uncompress corrupt data */
+ register UBYTE *p_dst_post = p_dst_first + *p_dst_len;
+
+ /* The following two variables are never modified and are used to control */
+ /* the main loop. */
+ UBYTE *p_src_post = p_src_first+src_len;
+ UBYTE *p_src_max16 = p_src_first+src_len-(MAX_CMP_GROUP-2);
+
+ /* The hash table is the only resident of the working memory. The hash table */
+ /* contains HASH_TABLE_LENGTH=4096 pointers to positions in the history. To */
+ /* keep Macintoshes happy, it is longword aligned. */
+ UBYTE **hash = (UBYTE **) ULONG_ALIGN_UP(p_wrk_mem);
+
+ /* The variable 'control' is used to buffer the control bits which appear in */
+ /* groups of 16 bits (control words) at the start of each compressed group. */
+ /* When each group is read, bit 16 of the register is set to one. Whenever */
+ /* a new bit is needed, the register is shifted right. When the value of the */
+ /* register becomes 1, we know that we have reached the end of a group. */
+ /* Initializing the register to 1 thus instructs the code to follow that it */
+ /* should read a new control word immediately. */
+ register ULONG control=1;
+
+ /* The value of 'literals' is always in the range 0..3. It is the number of */
+ /* consecutive literal items just seen. We have to record this number so as */
+ /* to know when to update the hash table. When literals gets to 3, there */
+ /* have been three consecutive literals and we can update at the position of */
+ /* the oldest of the three. */
+ register UWORD literals=0;
+
+ /* Check the leading copy flag to see if the compressor chose to use a copy */
+ /* operation instead of a compression operation. If a copy operation was */
+ /* used, then all we need to do is copy the data over, set the output length */
+ /* and return. */
+#if 0
+ if (*p_src_first==FLAG_COPY)
+ {
+ fast_copy(p_src_first+FLAG_BYTES,p_dst_first,src_len-FLAG_BYTES);
+ *p_dst_len=src_len-FLAG_BYTES;
+ return;
+ }
+#else
+ if ( src_len < 0 )
+ {
+ fast_copy(p_src_first,p_dst_first,-src_len );
+ *p_dst_len = (ULONG)-src_len;
+ return;
+ }
+#endif
+
+ /* Initialize all elements of the hash table to point to a constant string. */
+ /* Use of an unrolled loop speeds this up considerably. */
+ {UWORD i; UBYTE **p_h=hash;
+# define ZJ *p_h++=START_STRING_18
+ for (i=0;i<256;i++) /* 256=HASH_TABLE_LENGTH/16. */
+ {ZJ;ZJ;ZJ;ZJ;
+ ZJ;ZJ;ZJ;ZJ;
+ ZJ;ZJ;ZJ;ZJ;
+ ZJ;ZJ;ZJ;ZJ;}
+ }
+
+ /* The outer loop processes either 1 or 16 items per iteration depending on */
+ /* how close p_src is to the end of the input block. */
+ while (p_src!=p_src_post)
+ {/* Start of outer loop */
+
+ register UWORD unroll; /* Counts unrolled loop executions. */
+
+ /* When 'control' has the value 1, it means that the 16 buffered control */
+ /* bits that were read in at the start of the current group have all been */
+ /* shifted out and that all that is left is the 1 bit that was injected */
+ /* into bit 16 at the start of the current group. When we reach the end */
+ /* of a group, we have to load a new control word and inject a new 1 bit. */
+ if (control==1)
+ {
+ control=0x10000|*p_src++;
+ control|=(*p_src++)<<8;
+ }
+
+ /* If it is possible that we are within 16 groups from the end of the */
+ /* input, execute the unrolled loop only once, else process a whole group */
+ /* of 16 items by looping 16 times. */
+ unroll= p_src<=p_src_max16 ? 16 : 1;
+
+ /* This inner loop processes one phrase (item) per iteration. */
+ while (unroll--)
+ { /* Begin unrolled inner loop. */
+
+ /* Process a literal or copy item depending on the next control bit. */
+ if (control&1)
+ {
+ /* Copy item. */
+
+ register UBYTE *p; /* Points to place from which to copy. */
+ register UWORD lenmt; /* Length of copy item minus three. */
+ register UBYTE **p_hte; /* Pointer to current hash table entry.*/
+ register UBYTE *p_ziv=p_dst; /* Pointer to start of current Ziv. */
+
+ /* Read and dismantle the copy word. Work out from where to copy. */
+ lenmt=*p_src++;
+ p_hte=&hash[((lenmt&0xF0)<<4)|*p_src++];
+ p=*p_hte;
+ lenmt&=0xF;
+
+ /* Now perform the copy using a half unrolled loop. */
+ *p_dst++=*p++;
+ *p_dst++=*p++;
+ *p_dst++=*p++;
+ while (lenmt--)
+ *p_dst++=*p++;
+
+ /* Because we have just received 3 or more bytes in a copy item */
+ /* (whose bytes we have just installed in the output), we are now */
+ /* in a position to flush all the pending literal hashings that had */
+ /* been postponed for lack of bytes. */
+ if (literals>0)
+ {
+ register UBYTE *r=p_ziv-literals;;
+ hash[HASH(r)]=r;
+ if (literals==2)
+ {r++; hash[HASH(r)]=r;}
+ literals=0;
+ }
+
+ /* In any case, we can immediately update the hash table with the */
+ /* current position. We don't need to do a HASH(...) to work out */
+ /* where to put the pointer, as the compressor just told us!!! */
+ *p_hte=p_ziv;
+
+ }
+ else
+ {
+ /* Literal item. */
+
+ /* Copy over the literal byte. */
+ *p_dst++=*p_src++;
+
+ /* If we now have three literals waiting to be hashed into the hash */
+ /* table, we can do one of them now (because there are three). */
+ if (++literals == 3)
+ {register UBYTE *p=p_dst-3; hash[HASH(p)]=p; literals=2;}
+ }
+
+ /* Shift the control buffer so the next control bit is in bit 0. */
+ control>>=1;
+#if 1
+ if (p_dst > p_dst_post)
+ {
+ /* Shit: we tried to decompress corrupt data */
+ *p_dst_len = 0;
+ return;
+ }
+#endif
+ } /* End unrolled inner loop. */
+
+ } /* End of outer loop */
+
+ /* Write the length of the decompressed data before returning. */
+ *p_dst_len=p_dst-p_dst_first;
+}
+
+/******************************************************************************/
+/* End of LZRW3.C */
+/******************************************************************************/
diff --git a/drivers/char/ftape/compressor/lzrw3.h b/drivers/char/ftape/compressor/lzrw3.h
new file mode 100644
index 000000000..533feba47
--- /dev/null
+++ b/drivers/char/ftape/compressor/lzrw3.h
@@ -0,0 +1,253 @@
+#ifndef _LZRW3_H
+#define _LZRW3_H
+/*
+ * $Source: /homes/cvs/ftape-stacked/ftape/compressor/lzrw3.h,v $
+ * $Revision: 1.1 $
+ * $Date: 1997/10/05 19:12:30 $
+ *
+ * include files for lzrw3. Only slighty modified from the original
+ * version. Assembles the three include files compress.h, port.h and
+ * fastcopy.h from the original lzrw3 package.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/string.h>
+
+/******************************************************************************/
+/* */
+/* COMPRESS.H */
+/* */
+/******************************************************************************/
+/* */
+/* Author : Ross Williams. */
+/* Date : December 1989. */
+/* */
+/* This header file defines the interface to a set of functions called */
+/* 'compress', each member of which implements a particular data compression */
+/* algorithm. */
+/* */
+/* Normally in C programming, for each .H file, there is a corresponding .C */
+/* file that implements the functions promised in the .H file. */
+/* Here, there are many .C files corresponding to this header file. */
+/* Each comforming implementation file contains a single function */
+/* called 'compress' that implements a single data compression */
+/* algorithm that conforms with the interface specified in this header file. */
+/* Only one algorithm can be linked in at a time in this organization. */
+/* */
+/******************************************************************************/
+/* */
+/* DEFINITION OF FUNCTION COMPRESS */
+/* =============================== */
+/* */
+/* Summary of Function Compress */
+/* ---------------------------- */
+/* The action that 'compress' takes depends on its first argument called */
+/* 'action'. The function provides three actions: */
+/* */
+/* - Return information about the algorithm. */
+/* - Compress a block of memory. */
+/* - Decompress a block of memory. */
+/* */
+/* Parameters */
+/* ---------- */
+/* See the formal C definition later for a description of the parameters. */
+/* */
+/* Constants */
+/* --------- */
+/* COMPRESS_OVERRUN: The constant COMPRESS_OVERRUN defines by how many bytes */
+/* an algorithm is allowed to expand a block during a compression operation. */
+/* */
+/* Although compression algorithms usually compress data, there will always */
+/* be data that a given compressor will expand (this can be proven). */
+/* Fortunately, the degree of expansion can be limited to a single bit, by */
+/* copying over the input data if the data gets bigger during compression. */
+/* To allow for this possibility, the first bit of a compressed */
+/* representation can be used as a flag indicating whether the */
+/* input data was copied over, or truly compressed. In practice, the first */
+/* byte would be used to store this bit so as to maintain byte alignment. */
+/* */
+/* Unfortunately, in general, the only way to tell if an algorithm will */
+/* expand a particular block of data is to run the algorithm on the data. */
+/* If the algorithm does not continuously monitor how many output bytes it */
+/* has written, it might write an output block far larger than the input */
+/* block before realizing that it has done so. */
+/* On the other hand, continuous checks on output length are inefficient. */
+/* */
+/* To cater for all these problems, this interface definition: */
+/* > Allows a compression algorithm to return an output block that is up to */
+/* COMPRESS_OVERRUN bytes longer than the input block. */
+/* > Allows a compression algorithm to write up to COMPRESS_OVERRUN bytes */
+/* more than the length of the input block to the memory of the output */
+/* block regardless of the length of the output block eventually returned. */
+/* This allows an algorithm to overrun the length of the input block in the */
+/* output block by up to COMPRESS_OVERRUN bytes between expansion checks. */
+/* */
+/* The problem does not arise for decompression. */
+/* */
+/* Identity Action */
+/* --------------- */
+/* > action must be COMPRESS_ACTION_IDENTITY. */
+/* > p_dst_len must point to a longword to receive a longword address. */
+/* > The value of the other parameters does not matter. */
+/* > After execution, the longword that p_dst_len points to will be a pointer */
+/* to a structure of type compress_identity. */
+/* Thus, for example, after the call, (*p_dst_len)->memory will return the */
+/* number of bytes of working memory that the algorithm requires to run. */
+/* > The values of the identity structure returned are fixed constant */
+/* attributes of the algorithm and must not vary from call to call. */
+/* */
+/* Common Requirements for Compression and Decompression Actions */
+/* ------------------------------------------------------------- */
+/* > wrk_mem must point to an unused block of memory of a length specified in */
+/* the algorithm's identity block. The identity block can be obtained by */
+/* making a separate call to compress, specifying the identity action. */
+/* > The INPUT BLOCK is defined to be Memory[src_addr,src_addr+src_len-1]. */
+/* > dst_len will be used to denote *p_dst_len. */
+/* > dst_len is not read by compress, only written. */
+/* > The value of dst_len is defined only upon termination. */
+/* > The OUTPUT BLOCK is defined to be Memory[dst_addr,dst_addr+dst_len-1]. */
+/* */
+/* Compression Action */
+/* ------------------ */
+/* > action must be COMPRESS_ACTION_COMPRESS. */
+/* > src_len must be in the range [0,COMPRESS_MAX_ORG]. */
+/* > The OUTPUT ZONE is defined to be */
+/* Memory[dst_addr,dst_addr+src_len-1+COMPRESS_OVERRUN]. */
+/* > The function can modify any part of the output zone regardless of the */
+/* final length of the output block. */
+/* > The input block and the output zone must not overlap. */
+/* > dst_len will be in the range [0,src_len+COMPRESS_OVERRUN]. */
+/* > dst_len will be in the range [0,COMPRESS_MAX_COM] (from prev fact). */
+/* > The output block will consist of a representation of the input block. */
+/* */
+/* Decompression Action */
+/* -------------------- */
+/* > action must be COMPRESS_ACTION_DECOMPRESS. */
+/* > The input block must be the result of an earlier compression operation. */
+/* > If the previous fact is true, the following facts must also be true: */
+/* > src_len will be in the range [0,COMPRESS_MAX_COM]. */
+/* > dst_len will be in the range [0,COMPRESS_MAX_ORG]. */
+/* > The input and output blocks must not overlap. */
+/* > Only the output block is modified. */
+/* > Upon termination, the output block will consist of the bytes contained */
+/* in the input block passed to the earlier compression operation. */
+/* */
+/******************************************************************************/
+
+/******************************************************************************/
+/* */
+/* PORT.H */
+/* */
+/******************************************************************************/
+/* */
+/* This module contains macro definitions and types that are likely to */
+/* change between computers. */
+/* */
+/******************************************************************************/
+
+#ifndef DONE_PORT /* Only do this if not previously done. */
+
+ #ifdef THINK_C
+ #define UBYTE unsigned char /* Unsigned byte */
+ #define UWORD unsigned int /* Unsigned word (2 bytes) */
+ #define ULONG unsigned long /* Unsigned word (4 bytes) */
+ #define BOOL unsigned char /* Boolean */
+ #define FOPEN_BINARY_READ "rb" /* Mode string for binary reading. */
+ #define FOPEN_BINARY_WRITE "wb" /* Mode string for binary writing. */
+ #define FOPEN_TEXT_APPEND "a" /* Mode string for text appending. */
+ #define REAL double /* USed for floating point stuff. */
+ #endif
+ #if defined(LINUX) || defined(linux)
+ #define UBYTE __u8 /* Unsigned byte */
+ #define UWORD __u16 /* Unsigned word (2 bytes) */
+ #define ULONG __u32 /* Unsigned word (4 bytes) */
+ #define LONG __s32 /* Signed word (4 bytes) */
+ #define BOOL is not used here /* Boolean */
+ #define FOPEN_BINARY_READ not used /* Mode string for binary reading. */
+ #define FOPEN_BINARY_WRITE not used /* Mode string for binary writing. */
+ #define FOPEN_TEXT_APPEND not used /* Mode string for text appending. */
+ #define REAL not used /* USed for floating point stuff. */
+ #ifndef TRUE
+ #define TRUE 1
+ #endif
+ #endif
+
+ #define DONE_PORT /* Don't do all this again. */
+ #define MALLOC_FAIL NULL /* Failure status from malloc() */
+ #define LOCAL static /* For non-exported routines. */
+ #define EXPORT /* Signals exported function. */
+ #define then /* Useful for aligning ifs. */
+
+#endif
+
+/******************************************************************************/
+/* End of PORT.H */
+/******************************************************************************/
+
+#define COMPRESS_ACTION_IDENTITY 0
+#define COMPRESS_ACTION_COMPRESS 1
+#define COMPRESS_ACTION_DECOMPRESS 2
+
+#define COMPRESS_OVERRUN 1024
+#define COMPRESS_MAX_COM 0x70000000
+#define COMPRESS_MAX_ORG (COMPRESS_MAX_COM-COMPRESS_OVERRUN)
+
+#define COMPRESS_MAX_STRLEN 255
+
+/* The following structure provides information about the algorithm. */
+/* > The top bit of id must be zero. The remaining bits must be chosen by */
+/* the author of the algorithm by tossing a coin 31 times. */
+/* > The amount of memory requested by the algorithm is specified in bytes */
+/* and must be in the range [0,0x70000000]. */
+/* > All strings s must be such that strlen(s)<=COMPRESS_MAX_STRLEN. */
+struct compress_identity
+ {
+ ULONG id; /* Identifying number of algorithm. */
+ ULONG memory; /* Number of bytes of working memory required. */
+
+ char *name; /* Name of algorithm. */
+ char *version; /* Version number. */
+ char *date; /* Date of release of this version. */
+ char *copyright; /* Copyright message. */
+
+ char *author; /* Author of algorithm. */
+ char *affiliation; /* Affiliation of author. */
+ char *vendor; /* Where the algorithm can be obtained. */
+ };
+
+void lzrw3_compress( /* Single function interface to compression algorithm. */
+UWORD action, /* Action to be performed. */
+UBYTE *wrk_mem, /* Working memory temporarily given to routine to use. */
+UBYTE *src_adr, /* Address of input data. */
+LONG src_len, /* Length of input data. */
+UBYTE *dst_adr, /* Address of output data. */
+void *p_dst_len /* Pointer to a longword where routine will write: */
+ /* If action=..IDENTITY => Adr of id structure. */
+ /* If action=..COMPRESS => Length of output data. */
+ /* If action=..DECOMPRESS => Length of output data. */
+);
+
+/******************************************************************************/
+/* End of COMPRESS.H */
+/******************************************************************************/
+
+
+/******************************************************************************/
+/* fast_copy.h */
+/******************************************************************************/
+
+/* This function copies a block of memory very quickly. */
+/* The exact speed depends on the relative alignment of the blocks of memory. */
+/* PRE : 0<=src_len<=(2^32)-1 . */
+/* PRE : Source and destination blocks must not overlap. */
+/* POST : MEM[dst_adr,dst_adr+src_len-1]=MEM[src_adr,src_adr+src_len-1]. */
+/* POST : MEM[dst_adr,dst_adr+src_len-1] is the only memory changed. */
+
+#define fast_copy(src,dst,len) memcpy(dst,src,len)
+
+/******************************************************************************/
+/* End of fast_copy.h */
+/******************************************************************************/
+
+#endif
diff --git a/drivers/char/ftape/compressor/zftape-compress.c b/drivers/char/ftape/compressor/zftape-compress.c
new file mode 100644
index 000000000..382b58abe
--- /dev/null
+++ b/drivers/char/ftape/compressor/zftape-compress.c
@@ -0,0 +1,1317 @@
+/*
+ * Copyright (C) 1994-1997 Claus-Justus Heine
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139,
+ USA.
+
+ *
+ * This file implements a "generic" interface between the *
+ * zftape-driver and a compression-algorithm. The *
+ * compression-algorithm currently used is a LZ77. I use the *
+ * implementation lzrw3 by Ross N. Williams (Renaissance *
+ * Software). The compression program itself is in the file
+ * lzrw3.c * and lzrw3.h. To adopt another compression algorithm
+ * the functions * zft_compress() and zft_uncompress() must be
+ * changed * appropriately. See below.
+ */
+
+ char zftc_src[] ="$Source: /homes/cvs/ftape-stacked/ftape/compressor/zftape-compress.c,v $";
+ char zftc_rev[] = "$Revision: 1.1.6.1 $";
+ char zftc_dat[] = "$Date: 1997/11/16 15:15:56 $";
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+
+#include <linux/zftape.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,6)
+#include <asm/uaccess.h>
+#else
+#include <asm/segment.h>
+#endif
+
+#include "../zftape/zftape-init.h"
+#include "../zftape/zftape-eof.h"
+#include "../zftape/zftape-ctl.h"
+#include "../zftape/zftape-write.h"
+#include "../zftape/zftape-read.h"
+#include "../zftape/zftape-rw.h"
+#include "../compressor/zftape-compress.h"
+#include "../zftape/zftape-vtbl.h"
+#include "../compressor/lzrw3.h"
+
+/*
+ * global variables
+ */
+
+/* I handle the allocation of this buffer as a special case, because
+ * it's size varies depending on the tape length inserted.
+ */
+
+/* local variables
+ */
+static int keep_module_locked = 1;
+
+static void *zftc_wrk_mem = NULL;
+static __u8 *zftc_buf = NULL;
+static void *zftc_scratch_buf = NULL;
+
+/* compression statistics
+ */
+static unsigned int zftc_wr_uncompressed = 0;
+static unsigned int zftc_wr_compressed = 0;
+static unsigned int zftc_rd_uncompressed = 0;
+static unsigned int zftc_rd_compressed = 0;
+
+/* forward */
+static int zftc_write(int *write_cnt,
+ __u8 *dst_buf, const int seg_sz,
+ const __u8 *src_buf, const int req_len,
+ const zft_position *pos, const zft_volinfo *volume);
+static int zftc_read(int *read_cnt,
+ __u8 *dst_buf, const int to_do,
+ const __u8 *src_buf, const int seg_sz,
+ const zft_position *pos, const zft_volinfo *volume);
+static int zftc_seek(unsigned int new_block_pos,
+ zft_position *pos, const zft_volinfo *volume,
+ __u8 *buffer);
+static void zftc_lock (void);
+static void zftc_reset (void);
+static void zftc_cleanup(void);
+static void zftc_stats (void);
+
+/* compressed segment. This conforms to QIC-80-MC, Revision K.
+ *
+ * Rev. K applies to tapes with `fixed length format' which is
+ * indicated by format code 2,3 and 5. See below for format code 4 and 6
+ *
+ * 2 bytes: offset of compression segment structure
+ * 29k > offset >= 29k-18: data from previous segment ens in this
+ * segment and no compressed block starts
+ * in this segment
+ * offset == 0: data from previous segment occupies entire
+ * segment and continues in next segment
+ * n bytes: remainder from previous segment
+ *
+ * Rev. K:
+ * 4 bytes: 4 bytes: files set byte offset
+ * Post Rev. K and QIC-3020/3020:
+ * 8 bytes: 8 bytes: files set byte offset
+ * 2 bytes: byte count N (amount of data following)
+ * bit 15 is set if data is compressed, bit 15 is not
+ * set if data is uncompressed
+ * N bytes: data (as much as specified in the byte count)
+ * 2 bytes: byte count N_1 of next cluster
+ * N_1 bytes: data of next cluset
+ * 2 bytes: byte count N_2 of next cluster
+ * N_2 bytes: ...
+ *
+ * Note that the `N' byte count accounts only for the bytes that in the
+ * current segment if the cluster spans to the next segment.
+ */
+
+typedef struct
+{
+ int cmpr_pos; /* actual position in compression buffer */
+ int cmpr_sz; /* what is left in the compression buffer
+ * when copying the compressed data to the
+ * deblock buffer
+ */
+ unsigned int first_block; /* location of header information in
+ * this segment
+ */
+ unsigned int count; /* amount of data of current block
+ * contained in current segment
+ */
+ unsigned int offset; /* offset in current segment */
+ unsigned int spans:1; /* might continue in next segment */
+ unsigned int uncmpr; /* 0x8000 if this block contains
+ * uncompressed data
+ */
+ __s64 foffs; /* file set byte offset, same as in
+ * compression map segment
+ */
+} cmpr_info;
+
+static cmpr_info cseg; /* static data. Must be kept uptodate and shared by
+ * read, write and seek functions
+ */
+
+#define DUMP_CMPR_INFO(level, msg, info) \
+ TRACE(level, msg "\n" \
+ KERN_INFO "cmpr_pos : %d\n" \
+ KERN_INFO "cmpr_sz : %d\n" \
+ KERN_INFO "first_block: %d\n" \
+ KERN_INFO "count : %d\n" \
+ KERN_INFO "offset : %d\n" \
+ KERN_INFO "spans : %d\n" \
+ KERN_INFO "uncmpr : 0x%04x\n" \
+ KERN_INFO "foffs : " LL_X, \
+ (info)->cmpr_pos, (info)->cmpr_sz, (info)->first_block, \
+ (info)->count, (info)->offset, (info)->spans == 1, \
+ (info)->uncmpr, LL((info)->foffs))
+
+/* dispatch compression segment info, return error code
+ *
+ * afterwards, cseg->offset points to start of data of the NEXT
+ * compressed block, and cseg->count contains the amount of data
+ * left in the actual compressed block. cseg->spans is set to 1 if
+ * the block is continued in the following segment. Otherwise it is
+ * set to 0.
+ */
+static int get_cseg (cmpr_info *cinfo, const __u8 *buff,
+ const unsigned int seg_sz,
+ const zft_volinfo *volume)
+{
+ TRACE_FUN(ft_t_flow);
+
+ cinfo->first_block = GET2(buff, 0);
+ if (cinfo->first_block == 0) { /* data spans to next segment */
+ cinfo->count = seg_sz - sizeof(__u16);
+ cinfo->offset = seg_sz;
+ cinfo->spans = 1;
+ } else { /* cluster definetely ends in this segment */
+ if (cinfo->first_block > seg_sz) {
+ /* data corrupted */
+ TRACE_ABORT(-EIO, ft_t_err, "corrupted data:\n"
+ KERN_INFO "segment size: %d\n"
+ KERN_INFO "first block : %d",
+ seg_sz, cinfo->first_block);
+ }
+ cinfo->count = cinfo->first_block - sizeof(__u16);
+ cinfo->offset = cinfo->first_block;
+ cinfo->spans = 0;
+ }
+ /* now get the offset the first block should have in the
+ * uncompressed data stream.
+ *
+ * For this magic `18' refer to CRF-3 standard or QIC-80MC,
+ * Rev. K.
+ */
+ if ((seg_sz - cinfo->offset) > 18) {
+ if (volume->qic113) { /* > revision K */
+ TRACE(ft_t_data_flow, "New QIC-113 compliance");
+ cinfo->foffs = GET8(buff, cinfo->offset);
+ cinfo->offset += sizeof(__s64);
+ } else {
+ TRACE(/* ft_t_data_flow */ ft_t_noise, "pre QIC-113 version");
+ cinfo->foffs = (__s64)GET4(buff, cinfo->offset);
+ cinfo->offset += sizeof(__u32);
+ }
+ }
+ if (cinfo->foffs > volume->size) {
+ TRACE_ABORT(-EIO, ft_t_err, "Inconsistency:\n"
+ KERN_INFO "offset in current volume: %d\n"
+ KERN_INFO "size of current volume : %d",
+ (int)(cinfo->foffs>>10), (int)(volume->size>>10));
+ }
+ if (cinfo->cmpr_pos + cinfo->count > volume->blk_sz) {
+ TRACE_ABORT(-EIO, ft_t_err, "Inconsistency:\n"
+ KERN_INFO "block size : %d\n"
+ KERN_INFO "data record: %d",
+ volume->blk_sz, cinfo->cmpr_pos + cinfo->count);
+ }
+ DUMP_CMPR_INFO(ft_t_noise /* ft_t_any */, "", cinfo);
+ TRACE_EXIT 0;
+}
+
+/* This one is called, when a new cluster starts in same segment.
+ *
+ * Note: if this is the first cluster in the current segment, we must
+ * not check whether there are more than 18 bytes available because
+ * this have already been done in get_cseg() and there may be less
+ * than 18 bytes available due to header information.
+ *
+ */
+static void get_next_cluster(cmpr_info *cluster, const __u8 *buff,
+ const int seg_sz, const int finish)
+{
+ TRACE_FUN(ft_t_flow);
+
+ if (seg_sz - cluster->offset > 18 || cluster->foffs != 0) {
+ cluster->count = GET2(buff, cluster->offset);
+ cluster->uncmpr = cluster->count & 0x8000;
+ cluster->count -= cluster->uncmpr;
+ cluster->offset += sizeof(__u16);
+ cluster->foffs = 0;
+ if ((cluster->offset + cluster->count) < seg_sz) {
+ cluster->spans = 0;
+ } else if (cluster->offset + cluster->count == seg_sz) {
+ cluster->spans = !finish;
+ } else {
+ /* either an error or a volume written by an
+ * old version. If this is a data error, then we'll
+ * catch it later.
+ */
+ TRACE(ft_t_data_flow, "Either error or old volume");
+ cluster->spans = 1;
+ cluster->count = seg_sz - cluster->offset;
+ }
+ } else {
+ cluster->count = 0;
+ cluster->spans = 0;
+ cluster->foffs = 0;
+ }
+ DUMP_CMPR_INFO(ft_t_noise /* ft_t_any */ , "", cluster);
+ TRACE_EXIT;
+}
+
+static void zftc_lock(void)
+{
+#if LINUX_VERSION_CODE < KERNEL_VER(2,1,18)
+ if (!MOD_IN_USE) {
+ MOD_INC_USE_COUNT;
+ }
+#else
+ MOD_INC_USE_COUNT; /* sets MOD_VISITED and MOD_USED_ONCE,
+ * locking is done with can_unload()
+ */
+#endif
+ keep_module_locked = 1;
+}
+
+/* this function is needed for zftape_reset_position in zftape-io.c
+ */
+static void zftc_reset(void)
+{
+ TRACE_FUN(ft_t_flow);
+
+ memset((void *)&cseg, '\0', sizeof(cseg));
+ zftc_stats();
+#if LINUX_VERSION_CODE < KERNEL_VER(2,1,18)
+ if (MOD_IN_USE) {
+ MOD_DEC_USE_COUNT;
+ }
+#endif
+ keep_module_locked = 0;
+ TRACE_EXIT;
+}
+
+static int cmpr_mem_initialized = 0;
+static unsigned int alloc_blksz = 0;
+
+static int zft_allocate_cmpr_mem(unsigned int blksz)
+{
+ TRACE_FUN(ft_t_flow);
+
+ if (cmpr_mem_initialized && blksz == alloc_blksz) {
+ TRACE_EXIT 0;
+ }
+ TRACE_CATCH(zft_vmalloc_once(&zftc_wrk_mem, CMPR_WRK_MEM_SIZE),
+ zftc_cleanup());
+ TRACE_CATCH(zft_vmalloc_always(&zftc_buf, blksz + CMPR_OVERRUN),
+ zftc_cleanup());
+ alloc_blksz = blksz;
+ TRACE_CATCH(zft_vmalloc_always(&zftc_scratch_buf, blksz+CMPR_OVERRUN),
+ zftc_cleanup());
+ cmpr_mem_initialized = 1;
+ TRACE_EXIT 0;
+}
+
+static void zftc_cleanup(void)
+{
+ TRACE_FUN(ft_t_flow);
+
+ zft_vfree(&zftc_wrk_mem, CMPR_WRK_MEM_SIZE);
+ zft_vfree(&zftc_buf, alloc_blksz + CMPR_OVERRUN);
+ zft_vfree(&zftc_scratch_buf, alloc_blksz + CMPR_OVERRUN);
+ cmpr_mem_initialized = alloc_blksz = 0;
+ TRACE_EXIT;
+}
+
+/*****************************************************************************
+ * *
+ * The following two functions "ftape_compress()" and *
+ * "ftape_uncompress()" are the interface to the actual compression *
+ * algorithm (i.e. they are calling the "compress()" function from *
+ * the lzrw3 package for now). These routines could quite easily be *
+ * changed to adopt another compression algorithm instead of lzrw3, *
+ * which currently is used. *
+ * *
+ *****************************************************************************/
+
+/* called by zft_compress_write() to perform the compression. Must
+ * return the size of the compressed data.
+ *
+ * NOTE: The size of the compressed data should not exceed the size of
+ * the uncompressed data. Most compression algorithms have means
+ * to store data unchanged if the "compressed" data amount would
+ * exceed the original one. Mostly this is done by storing some
+ * flag-bytes in front of the compressed data to indicate if it
+ * is compressed or not. Thus the worst compression result
+ * length is the original length plus those flag-bytes.
+ *
+ * We don't want that, as the QIC-80 standard provides a means
+ * of marking uncompressed blocks by simply setting bit 15 of
+ * the compressed block's length. Thus a compessed block can
+ * have at most a length of 2^15-1 bytes. The QIC-80 standard
+ * restricts the block-length even further, allowing only 29k -
+ * 6 bytes.
+ *
+ * Currently, the maximum blocksize used by zftape is 28k.
+ *
+ * In short: don't exceed the length of the input-package, set
+ * bit 15 of the compressed size to 1 if you have copied data
+ * instead of compressing it.
+ */
+static int zft_compress(__u8 *in_buffer, unsigned int in_sz, __u8 *out_buffer)
+{
+ __s32 compressed_sz;
+ TRACE_FUN(ft_t_flow);
+
+
+ lzrw3_compress(COMPRESS_ACTION_COMPRESS, zftc_wrk_mem,
+ in_buffer, in_sz, out_buffer, &compressed_sz);
+ if (TRACE_LEVEL >= ft_t_info) {
+ /* the compiler will optimize this away when
+ * compiled with NO_TRACE_AT_ALL option
+ */
+ TRACE(ft_t_data_flow, "\n"
+ KERN_INFO "before compression: %d bytes\n"
+ KERN_INFO "after compresison : %d bytes",
+ in_sz,
+ (int)(compressed_sz < 0
+ ? -compressed_sz : compressed_sz));
+ /* for statistical purposes
+ */
+ zftc_wr_compressed += (compressed_sz < 0
+ ? -compressed_sz : compressed_sz);
+ zftc_wr_uncompressed += in_sz;
+ }
+ TRACE_EXIT (int)compressed_sz;
+}
+
+/* called by zft_compress_read() to decompress the data. Must
+ * return the size of the decompressed data for sanity checks
+ * (compared with zft_blk_sz)
+ *
+ * NOTE: Read the note for zft_compress() above! If bit 15 of the
+ * parameter in_sz is set, then the data in in_buffer isn't
+ * compressed, which must be handled by the un-compression
+ * algorithm. (I changed lzrw3 to handle this.)
+ *
+ * The parameter max_out_sz is needed to prevent buffer overruns when
+ * uncompressing corrupt data.
+ */
+static unsigned int zft_uncompress(__u8 *in_buffer,
+ int in_sz,
+ __u8 *out_buffer,
+ unsigned int max_out_sz)
+{
+ TRACE_FUN(ft_t_flow);
+
+ lzrw3_compress(COMPRESS_ACTION_DECOMPRESS, zftc_wrk_mem,
+ in_buffer, (__s32)in_sz,
+ out_buffer, (__u32 *)&max_out_sz);
+
+ if (TRACE_LEVEL >= ft_t_info) {
+ TRACE(ft_t_data_flow, "\n"
+ KERN_INFO "before decompression: %d bytes\n"
+ KERN_INFO "after decompression : %d bytes",
+ in_sz < 0 ? -in_sz : in_sz,(int)max_out_sz);
+ /* for statistical purposes
+ */
+ zftc_rd_compressed += in_sz < 0 ? -in_sz : in_sz;
+ zftc_rd_uncompressed += max_out_sz;
+ }
+ TRACE_EXIT (unsigned int)max_out_sz;
+}
+
+/* print some statistics about the efficiency of the compression to
+ * the kernel log
+ */
+static void zftc_stats(void)
+{
+ TRACE_FUN(ft_t_flow);
+
+ if (TRACE_LEVEL < ft_t_info) {
+ TRACE_EXIT;
+ }
+ if (zftc_wr_uncompressed != 0) {
+ if (zftc_wr_compressed > (1<<14)) {
+ TRACE(ft_t_info, "compression statistics (writing):\n"
+ KERN_INFO " compr./uncmpr. : %3d %%",
+ (((zftc_wr_compressed>>10) * 100)
+ / (zftc_wr_uncompressed>>10)));
+ } else {
+ TRACE(ft_t_info, "compression statistics (writing):\n"
+ KERN_INFO " compr./uncmpr. : %3d %%",
+ ((zftc_wr_compressed * 100)
+ / zftc_wr_uncompressed));
+ }
+ }
+ if (zftc_rd_uncompressed != 0) {
+ if (zftc_rd_compressed > (1<<14)) {
+ TRACE(ft_t_info, "compression statistics (reading):\n"
+ KERN_INFO " compr./uncmpr. : %3d %%",
+ (((zftc_rd_compressed>>10) * 100)
+ / (zftc_rd_uncompressed>>10)));
+ } else {
+ TRACE(ft_t_info, "compression statistics (reading):\n"
+ KERN_INFO " compr./uncmpr. : %3d %%",
+ ((zftc_rd_compressed * 100)
+ / zftc_rd_uncompressed));
+ }
+ }
+ /* only print it once: */
+ zftc_wr_uncompressed =
+ zftc_wr_compressed =
+ zftc_rd_uncompressed =
+ zftc_rd_compressed = 0;
+ TRACE_EXIT;
+}
+
+/* start new compressed block
+ */
+static int start_new_cseg(cmpr_info *cluster,
+ char *dst_buf,
+ const zft_position *pos,
+ const unsigned int blk_sz,
+ const char *src_buf,
+ const int this_segs_sz,
+ const int qic113)
+{
+ int size_left;
+ int cp_cnt;
+ int buf_pos;
+ TRACE_FUN(ft_t_flow);
+
+ size_left = this_segs_sz - sizeof(__u16) - cluster->cmpr_sz;
+ TRACE(ft_t_data_flow,"\n"
+ KERN_INFO "segment size : %d\n"
+ KERN_INFO "compressed_sz: %d\n"
+ KERN_INFO "size_left : %d",
+ this_segs_sz, cluster->cmpr_sz, size_left);
+ if (size_left > 18) { /* start a new cluseter */
+ cp_cnt = cluster->cmpr_sz;
+ cluster->cmpr_sz = 0;
+ buf_pos = cp_cnt + sizeof(__u16);
+ PUT2(dst_buf, 0, buf_pos);
+
+ if (qic113) {
+ __s64 foffs = pos->volume_pos;
+ if (cp_cnt) foffs += (__s64)blk_sz;
+
+ TRACE(ft_t_data_flow, "new style QIC-113 header");
+ PUT8(dst_buf, buf_pos, foffs);
+ buf_pos += sizeof(__s64);
+ } else {
+ __u32 foffs = (__u32)pos->volume_pos;
+ if (cp_cnt) foffs += (__u32)blk_sz;
+
+ TRACE(ft_t_data_flow, "old style QIC-80MC header");
+ PUT4(dst_buf, buf_pos, foffs);
+ buf_pos += sizeof(__u32);
+ }
+ } else if (size_left >= 0) {
+ cp_cnt = cluster->cmpr_sz;
+ cluster->cmpr_sz = 0;
+ buf_pos = cp_cnt + sizeof(__u16);
+ PUT2(dst_buf, 0, buf_pos);
+ /* zero unused part of segment. */
+ memset(dst_buf + buf_pos, '\0', size_left);
+ buf_pos = this_segs_sz;
+ } else { /* need entire segment and more space */
+ PUT2(dst_buf, 0, 0);
+ cp_cnt = this_segs_sz - sizeof(__u16);
+ cluster->cmpr_sz -= cp_cnt;
+ buf_pos = this_segs_sz;
+ }
+ memcpy(dst_buf + sizeof(__u16), src_buf + cluster->cmpr_pos, cp_cnt);
+ cluster->cmpr_pos += cp_cnt;
+ TRACE_EXIT buf_pos;
+}
+
+/* return-value: the number of bytes removed from the user-buffer
+ * `src_buf' or error code
+ *
+ * int *write_cnt : how much actually has been moved to the
+ * dst_buf. Need not be initialized when
+ * function returns with an error code
+ * (negativ return value)
+ * __u8 *dst_buf : kernel space buffer where the has to be
+ * copied to. The contents of this buffers
+ * goes to a specific segment.
+ * const int seg_sz : the size of the segment dst_buf will be
+ * copied to.
+ * const zft_position *pos : struct containing the coordinates in
+ * the current volume (byte position,
+ * segment id of current segment etc)
+ * const zft_volinfo *volume: information about the current volume,
+ * size etc.
+ * const __u8 *src_buf : user space buffer that contains the
+ * data the user wants to be written to
+ * tape.
+ * const int req_len : the amount of data the user wants to be
+ * written to tape.
+ */
+static int zftc_write(int *write_cnt,
+ __u8 *dst_buf, const int seg_sz,
+ const __u8 *src_buf, const int req_len,
+ const zft_position *pos, const zft_volinfo *volume)
+{
+ int req_len_left = req_len;
+ int result;
+ int len_left;
+ int buf_pos_write = pos->seg_byte_pos;
+ TRACE_FUN(ft_t_flow);
+
+ keep_module_locked = 1;
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,18)
+ MOD_INC_USE_COUNT; /* sets MOD_VISITED and MOD_USED_ONCE,
+ * locking is done with can_unload()
+ */
+#else
+ if (!MOD_IN_USE) {
+ MOD_INC_USE_COUNT;
+ }
+#endif
+ /* Note: we do not unlock the module because
+ * there are some values cached in that `cseg' variable. We
+ * don't don't want to use this information when being
+ * unloaded by kerneld even when the tape is full or when we
+ * cannot allocate enough memory.
+ */
+ if (pos->tape_pos > (volume->size-volume->blk_sz-ZFT_CMPR_OVERHEAD)) {
+ TRACE_EXIT -ENOSPC;
+ }
+ if (zft_allocate_cmpr_mem(volume->blk_sz) < 0) {
+ /* should we unlock the module? But it shouldn't
+ * be locked anyway ...
+ */
+ TRACE_EXIT -ENOMEM;
+ }
+ if (buf_pos_write == 0) { /* fill a new segment */
+ *write_cnt = buf_pos_write = start_new_cseg(&cseg,
+ dst_buf,
+ pos,
+ volume->blk_sz,
+ zftc_buf,
+ seg_sz,
+ volume->qic113);
+ if (cseg.cmpr_sz == 0 && cseg.cmpr_pos != 0) {
+ req_len_left -= result = volume->blk_sz;
+ cseg.cmpr_pos = 0;
+ } else {
+ result = 0;
+ }
+ } else {
+ *write_cnt = result = 0;
+ }
+
+ len_left = seg_sz - buf_pos_write;
+ while ((req_len_left > 0) && (len_left > 18)) {
+ /* now we have some size left for a new compressed
+ * block. We know, that the compression buffer is
+ * empty (else there wouldn't be any space left).
+ */
+#if LINUX_VERSION_CODE > KERNEL_VER(2,1,3)
+ if (copy_from_user(zftc_scratch_buf, src_buf + result,
+ volume->blk_sz) != 0) {
+ TRACE_EXIT -EFAULT;
+ }
+#else
+ TRACE_CATCH(verify_area(VERIFY_READ, src_buf + result,
+ volume->blk_sz),);
+ memcpy_fromfs(zftc_scratch_buf, src_buf + result,
+ volume->blk_sz);
+#endif
+ req_len_left -= volume->blk_sz;
+ cseg.cmpr_sz = zft_compress(zftc_scratch_buf, volume->blk_sz,
+ zftc_buf);
+ if (cseg.cmpr_sz < 0) {
+ cseg.uncmpr = 0x8000;
+ cseg.cmpr_sz = -cseg.cmpr_sz;
+ } else {
+ cseg.uncmpr = 0;
+ }
+ /* increment "result" iff we copied the entire
+ * compressed block to the zft_deblock_buf
+ */
+ len_left -= sizeof(__u16);
+ if (len_left >= cseg.cmpr_sz) {
+ len_left -= cseg.count = cseg.cmpr_sz;
+ cseg.cmpr_pos = cseg.cmpr_sz = 0;
+ result += volume->blk_sz;
+ } else {
+ cseg.cmpr_sz -=
+ cseg.cmpr_pos =
+ cseg.count = len_left;
+ len_left = 0;
+ }
+ PUT2(dst_buf, buf_pos_write, cseg.uncmpr | cseg.count);
+ buf_pos_write += sizeof(__u16);
+ memcpy(dst_buf + buf_pos_write, zftc_buf, cseg.count);
+ buf_pos_write += cseg.count;
+ *write_cnt += cseg.count + sizeof(__u16);
+ FT_SIGNAL_EXIT(_DONT_BLOCK);
+ }
+ /* erase the remainder of the segment if less than 18 bytes
+ * left (18 bytes is due to the QIC-80 standard)
+ */
+ if (len_left <= 18) {
+ memset(dst_buf + buf_pos_write, '\0', len_left);
+ (*write_cnt) += len_left;
+ }
+ TRACE(ft_t_data_flow, "returning %d", result);
+ TRACE_EXIT result;
+}
+
+/* out:
+ *
+ * int *read_cnt: the number of bytes we removed from the zft_deblock_buf
+ * (result)
+ * int *to_do : the remaining size of the read-request.
+ *
+ * in:
+ *
+ * char *buff : buff is the address of the upper part of the user
+ * buffer, that hasn't been filled with data yet.
+
+ * int buf_pos_read : copy of from _ftape_read()
+ * int buf_len_read : copy of buf_len_rd from _ftape_read()
+ * char *zft_deblock_buf: zft_deblock_buf
+ * unsigned short blk_sz: the block size valid for this volume, may differ
+ * from zft_blk_sz.
+ * int finish: if != 0 means that this is the last segment belonging
+ * to this volume
+ * returns the amount of data actually copied to the user-buffer
+ *
+ * to_do MUST NOT SHRINK except to indicate an EOF. In this case *to_do has to
+ * be set to 0
+ */
+static int zftc_read (int *read_cnt,
+ __u8 *dst_buf, const int to_do,
+ const __u8 *src_buf, const int seg_sz,
+ const zft_position *pos, const zft_volinfo *volume)
+{
+ int uncompressed_sz;
+ int result = 0;
+ int remaining = to_do;
+ TRACE_FUN(ft_t_flow);
+
+ keep_module_locked = 1;
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,18)
+ MOD_INC_USE_COUNT; /* sets MOD_VISITED and MOD_USED_ONCE,
+ * locking is done with can_unload()
+ */
+#else
+ if (!MOD_IN_USE) {
+ MOD_INC_USE_COUNT;
+ }
+#endif
+ TRACE_CATCH(zft_allocate_cmpr_mem(volume->blk_sz),);
+ if (pos->seg_byte_pos == 0) {
+ /* new segment just read
+ */
+ TRACE_CATCH(get_cseg(&cseg, src_buf, seg_sz, volume),
+ *read_cnt = 0);
+ memcpy(zftc_buf + cseg.cmpr_pos, src_buf + sizeof(__u16),
+ cseg.count);
+ cseg.cmpr_pos += cseg.count;
+ *read_cnt = cseg.offset;
+ DUMP_CMPR_INFO(ft_t_noise /* ft_t_any */, "", &cseg);
+ } else {
+ *read_cnt = 0;
+ }
+ /* loop and uncompress until user buffer full or
+ * deblock-buffer empty
+ */
+ TRACE(ft_t_data_flow, "compressed_sz: %d, compos : %d, *read_cnt: %d",
+ cseg.cmpr_sz, cseg.cmpr_pos, *read_cnt);
+ while ((cseg.spans == 0) && (remaining > 0)) {
+ if (cseg.cmpr_pos != 0) { /* cmpr buf is not empty */
+ uncompressed_sz =
+ zft_uncompress(zftc_buf,
+ cseg.uncmpr == 0x8000 ?
+ -cseg.cmpr_pos : cseg.cmpr_pos,
+ zftc_scratch_buf,
+ volume->blk_sz);
+ if (uncompressed_sz != volume->blk_sz) {
+ *read_cnt = 0;
+ TRACE_ABORT(-EIO, ft_t_warn,
+ "Uncompressed blk (%d) != blk size (%d)",
+ uncompressed_sz, volume->blk_sz);
+ }
+#if LINUX_VERSION_CODE > KERNEL_VER(2,1,3)
+ if (copy_to_user(dst_buf + result,
+ zftc_scratch_buf,
+ uncompressed_sz) != 0 ) {
+ TRACE_EXIT -EFAULT;
+ }
+#else
+ memcpy_tofs(dst_buf + result, zftc_scratch_buf,
+ uncompressed_sz);
+#endif
+ remaining -= uncompressed_sz;
+ result += uncompressed_sz;
+ cseg.cmpr_pos = 0;
+ }
+ if (remaining > 0) {
+ get_next_cluster(&cseg, src_buf, seg_sz,
+ volume->end_seg == pos->seg_pos);
+ if (cseg.count != 0) {
+ memcpy(zftc_buf, src_buf + cseg.offset,
+ cseg.count);
+ cseg.cmpr_pos = cseg.count;
+ cseg.offset += cseg.count;
+ *read_cnt += cseg.count + sizeof(__u16);
+ } else {
+ remaining = 0;
+ }
+ }
+ TRACE(ft_t_data_flow, "\n"
+ KERN_INFO "compressed_sz: %d\n"
+ KERN_INFO "compos : %d\n"
+ KERN_INFO "*read_cnt : %d",
+ cseg.cmpr_sz, cseg.cmpr_pos, *read_cnt);
+ }
+ if (seg_sz - cseg.offset <= 18) {
+ *read_cnt += seg_sz - cseg.offset;
+ TRACE(ft_t_data_flow, "expanding read cnt to: %d", *read_cnt);
+ }
+ TRACE(ft_t_data_flow, "\n"
+ KERN_INFO "segment size : %d\n"
+ KERN_INFO "read count : %d\n"
+ KERN_INFO "buf_pos_read : %d\n"
+ KERN_INFO "remaining : %d",
+ seg_sz, *read_cnt, pos->seg_byte_pos,
+ seg_sz - *read_cnt - pos->seg_byte_pos);
+ TRACE(ft_t_data_flow, "returning: %d", result);
+ TRACE_EXIT result;
+}
+
+/* seeks to the new data-position. Reads sometimes a segment.
+ *
+ * start_seg and end_seg give the boundaries of the current volume
+ * blk_sz is the blk_sz of the current volume as stored in the
+ * volume label
+ *
+ * We don't allow blocksizes less than 1024 bytes, therefore we don't need
+ * a 64 bit argument for new_block_pos.
+ */
+
+static int seek_in_segment(const unsigned int to_do, cmpr_info *c_info,
+ const char *src_buf, const int seg_sz,
+ const int seg_pos, const zft_volinfo *volume);
+static int slow_seek_forward_until_error(const unsigned int distance,
+ cmpr_info *c_info, zft_position *pos,
+ const zft_volinfo *volume, __u8 *buf);
+static int search_valid_segment(unsigned int segment,
+ const unsigned int end_seg,
+ const unsigned int max_foffs,
+ zft_position *pos, cmpr_info *c_info,
+ const zft_volinfo *volume, __u8 *buf);
+static int slow_seek_forward(unsigned int dest, cmpr_info *c_info,
+ zft_position *pos, const zft_volinfo *volume,
+ __u8 *buf);
+static int compute_seg_pos(unsigned int dest, zft_position *pos,
+ const zft_volinfo *volume);
+
+#define ZFT_SLOW_SEEK_THRESHOLD 10 /* segments */
+#define ZFT_FAST_SEEK_MAX_TRIALS 10 /* times */
+#define ZFT_FAST_SEEK_BACKUP 10 /* segments */
+
+static int zftc_seek(unsigned int new_block_pos,
+ zft_position *pos, const zft_volinfo *volume, __u8 *buf)
+{
+ unsigned int dest;
+ int limit;
+ int distance;
+ int result = 0;
+ int seg_dist;
+ int new_seg;
+ int old_seg = 0;
+ int fast_seek_trials = 0;
+ TRACE_FUN(ft_t_flow);
+
+ keep_module_locked = 1;
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,18)
+ MOD_INC_USE_COUNT; /* sets MOD_VISITED and MOD_USED_ONCE,
+ * locking is done with can_unload()
+ */
+#else
+ if (!MOD_IN_USE) {
+ MOD_INC_USE_COUNT;
+ }
+#endif
+ if (new_block_pos == 0) {
+ pos->seg_pos = volume->start_seg;
+ pos->seg_byte_pos = 0;
+ pos->volume_pos = 0;
+ zftc_reset();
+ TRACE_EXIT 0;
+ }
+ dest = new_block_pos * (volume->blk_sz >> 10);
+ distance = dest - (pos->volume_pos >> 10);
+ while (distance != 0) {
+ seg_dist = compute_seg_pos(dest, pos, volume);
+ TRACE(ft_t_noise, "\n"
+ KERN_INFO "seg_dist: %d\n"
+ KERN_INFO "distance: %d\n"
+ KERN_INFO "dest : %d\n"
+ KERN_INFO "vpos : %d\n"
+ KERN_INFO "seg_pos : %d\n"
+ KERN_INFO "trials : %d",
+ seg_dist, distance, dest,
+ (unsigned int)(pos->volume_pos>>10), pos->seg_pos,
+ fast_seek_trials);
+ if (distance > 0) {
+ if (seg_dist < 0) {
+ TRACE(ft_t_bug, "BUG: distance %d > 0, "
+ "segment difference %d < 0",
+ distance, seg_dist);
+ result = -EIO;
+ break;
+ }
+ new_seg = pos->seg_pos + seg_dist;
+ if (new_seg > volume->end_seg) {
+ new_seg = volume->end_seg;
+ }
+ if (old_seg == new_seg || /* loop */
+ seg_dist <= ZFT_SLOW_SEEK_THRESHOLD ||
+ fast_seek_trials >= ZFT_FAST_SEEK_MAX_TRIALS) {
+ TRACE(ft_t_noise, "starting slow seek:\n"
+ KERN_INFO "fast seek failed too often: %s\n"
+ KERN_INFO "near target position : %s\n"
+ KERN_INFO "looping between two segs : %s",
+ (fast_seek_trials >=
+ ZFT_FAST_SEEK_MAX_TRIALS)
+ ? "yes" : "no",
+ (seg_dist <= ZFT_SLOW_SEEK_THRESHOLD)
+ ? "yes" : "no",
+ (old_seg == new_seg)
+ ? "yes" : "no");
+ result = slow_seek_forward(dest, &cseg,
+ pos, volume, buf);
+ break;
+ }
+ old_seg = new_seg;
+ limit = volume->end_seg;
+ fast_seek_trials ++;
+ for (;;) {
+ result = search_valid_segment(new_seg, limit,
+ volume->size,
+ pos, &cseg,
+ volume, buf);
+ if (result == 0 || result == -EINTR) {
+ break;
+ }
+ if (new_seg == volume->start_seg) {
+ result = -EIO; /* set errror
+ * condition
+ */
+ break;
+ }
+ limit = new_seg;
+ new_seg -= ZFT_FAST_SEEK_BACKUP;
+ if (new_seg < volume->start_seg) {
+ new_seg = volume->start_seg;
+ }
+ }
+ if (result < 0) {
+ TRACE(ft_t_warn,
+ "Couldn't find a readable segment");
+ break;
+ }
+ } else /* if (distance < 0) */ {
+ if (seg_dist > 0) {
+ TRACE(ft_t_bug, "BUG: distance %d < 0, "
+ "segment difference %d >0",
+ distance, seg_dist);
+ result = -EIO;
+ break;
+ }
+ new_seg = pos->seg_pos + seg_dist;
+ if (fast_seek_trials > 0 && seg_dist == 0) {
+ /* this avoids sticking to the same
+ * segment all the time. On the other hand:
+ * if we got here for the first time, and the
+ * deblock_buffer still contains a valid
+ * segment, then there is no need to skip to
+ * the previous segment if the desired position
+ * is inside this segment.
+ */
+ new_seg --;
+ }
+ if (new_seg < volume->start_seg) {
+ new_seg = volume->start_seg;
+ }
+ limit = pos->seg_pos;
+ fast_seek_trials ++;
+ for (;;) {
+ result = search_valid_segment(new_seg, limit,
+ pos->volume_pos,
+ pos, &cseg,
+ volume, buf);
+ if (result == 0 || result == -EINTR) {
+ break;
+ }
+ if (new_seg == volume->start_seg) {
+ result = -EIO; /* set errror
+ * condition
+ */
+ break;
+ }
+ limit = new_seg;
+ new_seg -= ZFT_FAST_SEEK_BACKUP;
+ if (new_seg < volume->start_seg) {
+ new_seg = volume->start_seg;
+ }
+ }
+ if (result < 0) {
+ TRACE(ft_t_warn,
+ "Couldn't find a readable segment");
+ break;
+ }
+ }
+ distance = dest - (pos->volume_pos >> 10);
+ }
+ TRACE_EXIT result;
+}
+
+
+/* advance inside the given segment at most to_do bytes.
+ * of kilobytes moved
+ */
+
+static int seek_in_segment(const unsigned int to_do,
+ cmpr_info *c_info,
+ const char *src_buf,
+ const int seg_sz,
+ const int seg_pos,
+ const zft_volinfo *volume)
+{
+ int result = 0;
+ int blk_sz = volume->blk_sz >> 10;
+ int remaining = to_do;
+ TRACE_FUN(ft_t_flow);
+
+ if (c_info->offset == 0) {
+ /* new segment just read
+ */
+ TRACE_CATCH(get_cseg(c_info, src_buf, seg_sz, volume),);
+ c_info->cmpr_pos += c_info->count;
+ DUMP_CMPR_INFO(ft_t_noise, "", c_info);
+ }
+ /* loop and uncompress until user buffer full or
+ * deblock-buffer empty
+ */
+ TRACE(ft_t_noise, "compressed_sz: %d, compos : %d",
+ c_info->cmpr_sz, c_info->cmpr_pos);
+ while (c_info->spans == 0 && remaining > 0) {
+ if (c_info->cmpr_pos != 0) { /* cmpr buf is not empty */
+ result += blk_sz;
+ remaining -= blk_sz;
+ c_info->cmpr_pos = 0;
+ }
+ if (remaining > 0) {
+ get_next_cluster(c_info, src_buf, seg_sz,
+ volume->end_seg == seg_pos);
+ if (c_info->count != 0) {
+ c_info->cmpr_pos = c_info->count;
+ c_info->offset += c_info->count;
+ } else {
+ break;
+ }
+ }
+ /* Allow escape from this loop on signal!
+ */
+ FT_SIGNAL_EXIT(_DONT_BLOCK);
+ DUMP_CMPR_INFO(ft_t_noise, "", c_info);
+ TRACE(ft_t_noise, "to_do: %d", remaining);
+ }
+ if (seg_sz - c_info->offset <= 18) {
+ c_info->offset = seg_sz;
+ }
+ TRACE(ft_t_noise, "\n"
+ KERN_INFO "segment size : %d\n"
+ KERN_INFO "buf_pos_read : %d\n"
+ KERN_INFO "remaining : %d",
+ seg_sz, c_info->offset,
+ seg_sz - c_info->offset);
+ TRACE_EXIT result;
+}
+
+static int slow_seek_forward_until_error(const unsigned int distance,
+ cmpr_info *c_info,
+ zft_position *pos,
+ const zft_volinfo *volume,
+ __u8 *buf)
+{
+ unsigned int remaining = distance;
+ int seg_sz;
+ int seg_pos;
+ int result;
+ TRACE_FUN(ft_t_flow);
+
+ seg_pos = pos->seg_pos;
+ do {
+ TRACE_CATCH(seg_sz = zft_fetch_segment(seg_pos, buf,
+ FT_RD_AHEAD),);
+ /* now we have the contents of the actual segment in
+ * the deblock buffer
+ */
+ TRACE_CATCH(result = seek_in_segment(remaining, c_info, buf,
+ seg_sz, seg_pos,volume),);
+ remaining -= result;
+ pos->volume_pos += result<<10;
+ pos->seg_pos = seg_pos;
+ pos->seg_byte_pos = c_info->offset;
+ seg_pos ++;
+ if (seg_pos <= volume->end_seg && c_info->offset == seg_sz) {
+ pos->seg_pos ++;
+ pos->seg_byte_pos = 0;
+ c_info->offset = 0;
+ }
+ /* Allow escape from this loop on signal!
+ */
+ FT_SIGNAL_EXIT(_DONT_BLOCK);
+ TRACE(ft_t_noise, "\n"
+ KERN_INFO "remaining: %d\n"
+ KERN_INFO "seg_pos: %d\n"
+ KERN_INFO "end_seg: %d\n"
+ KERN_INFO "result: %d",
+ remaining, seg_pos, volume->end_seg, result);
+ } while (remaining > 0 && seg_pos <= volume->end_seg);
+ TRACE_EXIT 0;
+}
+
+/* return segment id of next segment containing valid data, -EIO otherwise
+ */
+static int search_valid_segment(unsigned int segment,
+ const unsigned int end_seg,
+ const unsigned int max_foffs,
+ zft_position *pos,
+ cmpr_info *c_info,
+ const zft_volinfo *volume,
+ __u8 *buf)
+{
+ cmpr_info tmp_info;
+ int seg_sz;
+ TRACE_FUN(ft_t_flow);
+
+ memset(&tmp_info, 0, sizeof(cmpr_info));
+ while (segment <= end_seg) {
+ FT_SIGNAL_EXIT(_DONT_BLOCK);
+ TRACE(ft_t_noise,
+ "Searching readable segment between %d and %d",
+ segment, end_seg);
+ seg_sz = zft_fetch_segment(segment, buf, FT_RD_AHEAD);
+ if ((seg_sz > 0) &&
+ (get_cseg (&tmp_info, buf, seg_sz, volume) >= 0) &&
+ (tmp_info.foffs != 0 || segment == volume->start_seg)) {
+ if ((tmp_info.foffs>>10) > max_foffs) {
+ TRACE_ABORT(-EIO, ft_t_noise, "\n"
+ KERN_INFO "cseg.foff: %d\n"
+ KERN_INFO "dest : %d",
+ (int)(tmp_info.foffs >> 10),
+ max_foffs);
+ }
+ DUMP_CMPR_INFO(ft_t_noise, "", &tmp_info);
+ *c_info = tmp_info;
+ pos->seg_pos = segment;
+ pos->volume_pos = c_info->foffs;
+ pos->seg_byte_pos = c_info->offset;
+ TRACE(ft_t_noise, "found segment at %d", segment);
+ TRACE_EXIT 0;
+ }
+ segment++;
+ }
+ TRACE_EXIT -EIO;
+}
+
+static int slow_seek_forward(unsigned int dest,
+ cmpr_info *c_info,
+ zft_position *pos,
+ const zft_volinfo *volume,
+ __u8 *buf)
+{
+ unsigned int distance;
+ int result = 0;
+ TRACE_FUN(ft_t_flow);
+
+ distance = dest - (pos->volume_pos >> 10);
+ while ((distance > 0) &&
+ (result = slow_seek_forward_until_error(distance,
+ c_info,
+ pos,
+ volume,
+ buf)) < 0) {
+ if (result == -EINTR) {
+ break;
+ }
+ TRACE(ft_t_noise, "seg_pos: %d", pos->seg_pos);
+ /* the failing segment is either pos->seg_pos or
+ * pos->seg_pos + 1. There is no need to further try
+ * that segment, because ftape_read_segment() already
+ * has tried very much to read it. So we start with
+ * following segment, which is pos->seg_pos + 1
+ */
+ if(search_valid_segment(pos->seg_pos+1, volume->end_seg, dest,
+ pos, c_info,
+ volume, buf) < 0) {
+ TRACE(ft_t_noise, "search_valid_segment() failed");
+ result = -EIO;
+ break;
+ }
+ distance = dest - (pos->volume_pos >> 10);
+ result = 0;
+ TRACE(ft_t_noise, "segment: %d", pos->seg_pos);
+ /* found valid segment, retry the seek */
+ }
+ TRACE_EXIT result;
+}
+
+static int compute_seg_pos(const unsigned int dest,
+ zft_position *pos,
+ const zft_volinfo *volume)
+{
+ int segment;
+ int distance = dest - (pos->volume_pos >> 10);
+ unsigned int raw_size;
+ unsigned int virt_size;
+ unsigned int factor;
+ TRACE_FUN(ft_t_flow);
+
+ if (distance >= 0) {
+ raw_size = volume->end_seg - pos->seg_pos + 1;
+ virt_size = ((unsigned int)(volume->size>>10)
+ - (unsigned int)(pos->volume_pos>>10)
+ + FT_SECTORS_PER_SEGMENT - FT_ECC_SECTORS - 1);
+ virt_size /= FT_SECTORS_PER_SEGMENT - FT_ECC_SECTORS;
+ if (virt_size == 0 || raw_size == 0) {
+ TRACE_EXIT 0;
+ }
+ if (raw_size >= (1<<25)) {
+ factor = raw_size/(virt_size>>7);
+ } else {
+ factor = (raw_size<<7)/virt_size;
+ }
+ segment = distance/(FT_SECTORS_PER_SEGMENT-FT_ECC_SECTORS);
+ segment = (segment * factor)>>7;
+ } else {
+ raw_size = pos->seg_pos - volume->start_seg + 1;
+ virt_size = ((unsigned int)(pos->volume_pos>>10)
+ + FT_SECTORS_PER_SEGMENT - FT_ECC_SECTORS - 1);
+ virt_size /= FT_SECTORS_PER_SEGMENT - FT_ECC_SECTORS;
+ if (virt_size == 0 || raw_size == 0) {
+ TRACE_EXIT 0;
+ }
+ if (raw_size >= (1<<25)) {
+ factor = raw_size/(virt_size>>7);
+ } else {
+ factor = (raw_size<<7)/virt_size;
+ }
+ segment = distance/(FT_SECTORS_PER_SEGMENT-FT_ECC_SECTORS);
+ }
+ TRACE(ft_t_noise, "factor: %d/%d", factor, 1<<7);
+ TRACE_EXIT segment;
+}
+
+static struct zft_cmpr_ops cmpr_ops = {
+ zftc_write,
+ zftc_read,
+ zftc_seek,
+ zftc_lock,
+ zftc_reset,
+ zftc_cleanup
+};
+
+int zft_compressor_init(void)
+{
+ TRACE_FUN(ft_t_flow);
+
+#ifdef MODULE
+ printk(KERN_INFO "zftape compressor v1.00a 970514 for " FTAPE_VERSION "\n");
+ if (TRACE_LEVEL >= ft_t_info) {
+ printk(
+KERN_INFO "(c) 1997 Claus-Justus Heine (claus@momo.math.rwth-aachen.de)\n"
+KERN_INFO "Compressor for zftape (lzrw3 algorithm)\n"
+KERN_INFO "Compiled for kernel version %s"
+#ifdef MODVERSIONS
+ " with versioned symbols"
+#endif
+ "\n", UTS_RELEASE);
+ }
+#else /* !MODULE */
+ /* print a short no-nonsense boot message */
+ printk("zftape compressor v1.00a 970514 for Linux " UTS_RELEASE "\n");
+ printk("For use with " FTAPE_VERSION "\n");
+#endif /* MODULE */
+ TRACE(ft_t_info, "zft_compressor_init @ 0x%p", zft_compressor_init);
+ TRACE(ft_t_info, "installing compressor for zftape ...");
+ TRACE_CATCH(zft_cmpr_register(&cmpr_ops),);
+ TRACE_EXIT 0;
+}
+
+
+#ifdef MODULE
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,18)
+MODULE_AUTHOR(
+ "(c) 1996, 1997 Claus-Justus Heine (claus@momo.math.rwth-aachen.de");
+MODULE_DESCRIPTION(
+"Compression routines for zftape. Uses the lzrw3 algorithm by Ross Williams");
+#endif
+
+#if LINUX_VERSION_CODE <= KERNEL_VER(1,2,13)
+char kernel_version[] = UTS_RELEASE;
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,18)
+static int can_unload(void)
+{
+ return keep_module_locked ? -EBUSY : 0;
+}
+#endif
+
+/* Called by modules package when installing the driver
+ */
+int init_module(void)
+{
+ int result;
+
+#if LINUX_VERSION_CODE >= KERNEL_VER(1,1,85)
+# if LINUX_VERSION_CODE < KERNEL_VER(2,1,18)
+ register_symtab(0); /* remove global ftape symbols */
+# else
+ if (!mod_member_present(&__this_module, can_unload))
+ return -EBUSY;
+ __this_module.can_unload = can_unload;
+ EXPORT_NO_SYMBOLS;
+# endif
+#endif
+ result = zft_compressor_init();
+ keep_module_locked = 0;
+ return result;
+}
+
+/* Called by modules package when removing the driver
+ */
+void cleanup_module(void)
+{
+ TRACE_FUN(ft_t_flow);
+
+ if (zft_cmpr_unregister() != &cmpr_ops) {
+ TRACE(ft_t_info, "failed");
+ } else {
+ TRACE(ft_t_info, "successful");
+ }
+ zftc_cleanup();
+ printk(KERN_INFO "zft-compressor successfully unloaded.\n");
+ TRACE_EXIT;
+}
+#endif /* MODULE */
diff --git a/drivers/char/ftape/compressor/zftape-compress.h b/drivers/char/ftape/compressor/zftape-compress.h
new file mode 100644
index 000000000..f200741e3
--- /dev/null
+++ b/drivers/char/ftape/compressor/zftape-compress.h
@@ -0,0 +1,83 @@
+#ifndef _ZFTAPE_COMPRESS_H
+#define _ZFTAPE_COMPRESS_H
+/*
+ * Copyright (c) 1994-1997 Claus-Justus Heine
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139,
+ USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/compressor/zftape-compress.h,v $
+ * $Revision: 1.1 $
+ * $Date: 1997/10/05 19:12:32 $
+ *
+ * This file contains macros and definitions for zftape's
+ * builtin compression code.
+ *
+ */
+
+#include "../zftape/zftape-buffers.h"
+#include "../zftape/zftape-vtbl.h"
+#include "../compressor/lzrw3.h"
+
+/* CMPR_WRK_MEM_SIZE gives the size of the compression wrk_mem */
+/* I got these out of lzrw3.c */
+#define U(X) ((__u32) X)
+#define SIZE_P_BYTE (U(sizeof(__u8 *)))
+#define ALIGNMENT_FUDGE (U(16))
+
+#define CMPR_WRK_MEM_SIZE (U(4096)*(SIZE_P_BYTE) + ALIGNMENT_FUDGE)
+
+/* the maximum number of bytes the size of the "compressed" data can
+ * exceed the uncompressed data. As it is quite useless to compress
+ * data twice it is sometimes the case that it is more efficient to
+ * copy a block of data but to feed it to the "compression"
+ * algorithm. In this case there are some flag bytes or the like
+ * proceding the "compressed" data. THAT MUST NOT BE THE CASE for the
+ * algorithm we use for this driver. Instead, the high bit 15 of
+ * compressed_size:
+ *
+ * compressed_size = ftape_compress()
+ *
+ * must be set in such a case.
+ *
+ * Nevertheless, it might also be as for lzrw3 that there is an
+ * "intermediate" overrun that exceeds the amount of the compressed
+ * data that is actually produced. During the algorithm we need in the
+ * worst case MAX_CMP_GROUP bytes more than the input-size.
+ */
+#define MAX_CMP_GROUP (2+16*2) /* from lzrw3.c */
+
+#define CMPR_OVERRUN MAX_CMP_GROUP /* during compression */
+
+/****************************************************/
+
+#define CMPR_BUFFER_SIZE (MAX_BLOCK_SIZE + CMPR_OVERRUN)
+
+/* the compression map stores the byte offset compressed blocks within
+ * the current volume for catridges with format code 2,3 and 5
+ * (and old versions of zftape) and the offset measured in kilobytes for
+ * format code 4 and 6. This gives us a possible max. size of a
+ * compressed volume of 1024*4GIG which should be enough.
+ */
+typedef __u32 CmprMap;
+
+/* globals
+ */
+
+/* exported functions
+ */
+
+#endif /* _ZFTAPE_COMPRESS_H */
diff --git a/drivers/char/ftape/fdc-io.c b/drivers/char/ftape/fdc-io.c
deleted file mode 100644
index 51fc128d8..000000000
--- a/drivers/char/ftape/fdc-io.c
+++ /dev/null
@@ -1,1300 +0,0 @@
-/* Yo, Emacs! we're -*- Linux-C -*-
- *
- * Copyright (C) 1993-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING. If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- *
- * This file contains the low-level floppy disk interface code
- * for the QIC-40/80 tape streamer device driver.
- */
-
-#include <linux/errno.h>
-#include <linux/sched.h>
-#include <linux/ioport.h>
-#include <linux/ftape.h>
-#include <asm/system.h>
-#include <asm/io.h>
-#include <asm/dma.h>
-#include <asm/irq.h>
-
-#include "tracing.h"
-#include "fdc-io.h"
-#include "fdc-isr.h"
-#include "ftape-io.h"
-#include "ftape-rw.h"
-#include "calibr.h"
-#include "fc-10.h"
-#include "qic117.h"
-
-
-/* Global vars.
- */
-int ftape_unit = -1;
-int ftape_motor = 0;
-int current_cylinder = -1;
-fdc_mode_enum fdc_mode = fdc_idle;
-fdc_config_info fdc = {0};
-
-/* Local vars.
- */
-static int fdc_calibr_count;
-static int fdc_calibr_time;
-static int fdc_confused = 0;
-static int fdc_status;
-volatile byte fdc_head; /* FDC head */
-volatile byte fdc_cyl; /* FDC track */
-volatile byte fdc_sect; /* FDC sector */
-static int fdc_data_rate = 0; /* default rate = 500 Kbps */
-static int fdc_seek_rate = 14; /* default rate = 2 msec @ 500 Kbps */
-static void (*do_ftape) (void);
-static int fdc_fifo_state; /* original fifo setting - fifo enabled */
-static int fdc_fifo_thr; /* original fifo setting - threshold */
-static int fdc_lock_state; /* original lock setting - locked */
-static int fdc_fifo_locked = 0; /* has fifo && lock set ? */
-static byte fdc_precomp = 0; /* sets fdc to default precomp. value */
-static byte fdc_drv_spec[4]; /* drive specification bytes for i82078 */
-static int perpend_mode; /* true if fdc is in perpendicular mode */
-
-static char ftape_id[] = "ftape"; /* used by request irq and free irq */
-
-void fdc_catch_stray_interrupts(unsigned count)
-{
- unsigned long flags;
-
- save_flags(flags);
- cli();
- if (count == 0) {
- expected_stray_interrupts = 0;
- } else {
- expected_stray_interrupts += count;
- }
- restore_flags(flags);
-}
-
-/* Wait during a timeout period for a given FDC status.
- * If usecs == 0 then just test status, else wait at least for usecs.
- * Returns -ETIME on timeout. Function must be calibrated first !
- */
-int fdc_wait(int usecs, byte mask, byte state)
-{
- int count_1 = (fdc_calibr_count * usecs - 1) / fdc_calibr_time;
-
- do {
- fdc_status = inb_p(fdc.msr);
- if ((fdc_status & mask) == state) {
- return 0;
- }
- } while (count_1-- >= 0);
- return -ETIME;
-}
-
-int fdc_ready_wait(int usecs)
-{
- return fdc_wait(usecs, FDC_DATA_READY, FDC_DATA_READY);
-}
-
-static void fdc_usec_wait(int usecs)
-{
- fdc_wait(usecs, 0, 1); /* will always timeout ! */
-}
-
-int fdc_ready_out_wait(int usecs)
-{
- fdc_usec_wait(RQM_DELAY); /* wait for valid RQM status */
- return fdc_wait(usecs, FDC_DATA_OUT_READY, FDC_DATA_OUT_READY);
-}
-
-int fdc_ready_in_wait(int usecs)
-{
- fdc_usec_wait(RQM_DELAY); /* wait for valid RQM status */
- return fdc_wait(usecs, FDC_DATA_OUT_READY, FDC_DATA_IN_READY);
-}
-
-int fdc_wait_calibrate(void)
-{
- return calibrate("fdc_wait",
- fdc_usec_wait, &fdc_calibr_count, &fdc_calibr_time);
-}
-
-/* Wait for a (short) while for the FDC to become ready
- * and transfer the next command byte.
- * Return -ETIME on timeout on getting ready (depends on hardware!).
- */
-int fdc_write(byte data)
-{
- fdc_usec_wait(RQM_DELAY); /* wait for valid RQM status */
- if (fdc_wait(150, FDC_DATA_READY_MASK, FDC_DATA_IN_READY) < 0) {
- return -ETIME;
- } else {
- outb(data, fdc.fifo);
- return 0;
- }
-}
-
-/* Wait for a (short) while for the FDC to become ready
- * and transfer the next result byte.
- * Return -ETIME if timeout on getting ready (depends on hardware!).
- */
-int fdc_read(byte * data)
-{
- fdc_usec_wait(RQM_DELAY); /* wait for valid RQM status */
- if (fdc_wait(150, FDC_DATA_READY_MASK, FDC_DATA_OUT_READY) < 0) {
- return -ETIME;
- } else {
- *data = inb(fdc.fifo);
- return 0;
- }
-}
-
-/* Output a cmd_len long command string to the FDC.
- * The FDC should be ready to receive a new command or
- * an error (EBUSY) will occur.
- */
-int fdc_command(byte * cmd_data, int cmd_len)
-{
- TRACE_FUN(8, "fdc_command");
- int result = 0;
- unsigned long flags;
- int count = cmd_len;
-
- fdc_usec_wait(RQM_DELAY); /* wait for valid RQM status */
- save_flags(flags);
- cli();
- fdc_status = inb(fdc.msr);
- if ((fdc_status & FDC_DATA_READY_MASK) == FDC_DATA_IN_READY) {
- int retry = 0;
- fdc_mode = *cmd_data; /* used by isr */
- interrupt_seen = 0;
- while (count) {
- result = fdc_write(*cmd_data);
- if (result < 0) {
- TRACEx3(6, "fdc_mode = %02x, status = %02x at index %d",
- (int) fdc_mode, (int) fdc_status, cmd_len - count);
- if (++retry <= 3) {
- TRACE(2, "fdc_write timeout, retry");
- } else {
- TRACE(1, "fdc_write timeout, fatal");
- fdc_confused = 1;
- /* recover ??? */
- break;
- }
- } else {
- --count;
- ++cmd_data;
- }
- }
- } else {
- TRACE(1, "fdc not ready");
- result = -EBUSY;
- }
- restore_flags(flags);
- TRACE_EXIT;
- return result;
-}
-
-/* Input a res_len long result string from the FDC.
- * The FDC should be ready to send the result or an error
- * (EBUSY) will occur.
- */
-int fdc_result(byte * res_data, int res_len)
-{
- TRACE_FUN(8, "fdc_result");
- int result = 0;
- unsigned long flags;
- int count = res_len;
-
- save_flags(flags);
- cli();
- fdc_status = inb(fdc.msr);
- if ((fdc_status & FDC_DATA_READY_MASK) == FDC_DATA_OUT_READY) {
- int retry = 0;
- while (count) {
- if (!(fdc_status & FDC_BUSY)) {
- TRACE(1, "premature end of result phase");
- }
- result = fdc_read(res_data);
- if (result < 0) {
- TRACEx3(6, "fdc_mode = %02x, status = %02x at index %d",
- (int) fdc_mode, (int) fdc_status, res_len - count);
- if (++retry <= 3) {
- TRACE(2, "fdc_read timeout, retry");
- } else {
- TRACE(1, "fdc_read timeout, fatal");
- fdc_confused = 1;
- /* recover ??? */
- break;
- }
- } else {
- --count;
- ++res_data;
- }
- }
- } else {
- TRACE(1, "fdc not ready");
- result = -EBUSY;
- }
- restore_flags(flags);
- fdc_usec_wait(RQM_DELAY); /* allow FDC to negate BSY */
- TRACE_EXIT;
- return result;
-}
-
-/* Handle command and result phases for
- * commands without data phase.
- */
-int fdc_issue_command(byte * out_data, int out_count,
- byte * in_data, int in_count)
-{
- TRACE_FUN(8, "fdc_issue_command");
- int result;
- int t0, t1;
-
- if (out_count > 0) {
- result = fdc_command(out_data, out_count);
- if (result < 0) {
- TRACE(1, "fdc_command failed");
- TRACE_EXIT;
- return result;
- }
- }
- /* will take 24 - 30 usec for fdc_sense_drive_status and
- * fdc_sense_interrupt_status commands.
- * 35 fails sometimes (5/9/93 SJL)
- * On a loaded system it incidentally takes longer than
- * this for the fdc to get ready ! ?????? WHY ??????
- * So until we know what's going on use a very long timeout.
- */
- t0 = timestamp();
- result = fdc_ready_out_wait(500 /* usec */ );
- t1 = timestamp();
- if (result < 0) {
- TRACEi(1, "fdc_ready_out_wait failed after:", timediff(t0, t1));
- TRACE_EXIT;
- return result;
- }
- if (in_count > 0) {
- result = fdc_result(in_data, in_count);
- if (result < 0) {
- TRACE(1, "result phase aborted");
- TRACE_EXIT;
- return result;
- }
- }
- TRACE_EXIT;
- return 0;
-}
-
-/* Wait for FDC interrupt with timeout.
- * Signals are blocked so the wait will not be aborted.
- * Note: interrupts must be enabled ! (23/05/93 SJL)
- */
-int fdc_interrupt_wait(int time)
-{
- TRACE_FUN(8, "fdc_interrupt_wait");
- struct wait_queue wait =
- {current, NULL};
- int result = -ETIME;
- int need_cleanup = 0;
- int current_blocked = current->blocked;
- static int resetting = 0;
-
- if (waitqueue_active(&wait_intr)) {
- TRACE(1, "error: nested call");
- return -EIO; /* return error... */
- }
- if (interrupt_seen == 0) {
- /* timeout time will be between 0 and MSPT milliseconds too long !
- */
- current->timeout = jiffies + 1 + (time + MSPT - 1) / MSPT;
- current->state = TASK_INTERRUPTIBLE;
- current->blocked = _BLOCK_ALL;
- add_wait_queue(&wait_intr, &wait);
- do {
- schedule(); /* sets TASK_RUNNING on timeout */
- } while (!interrupt_seen && current->state != TASK_RUNNING);
- current->blocked = current_blocked; /* restore */
- remove_wait_queue(&wait_intr, &wait);
- if (interrupt_seen) {
- current->timeout = 0; /* interrupt hasn't cleared this */
- result = 0;
- } else {
-#if 1
-/*** remove me when sure this doesn't happen ***/
- if (current->timeout > 0) {
- TRACE(-1, "*** BUG: unexpected schedule exit ***");
- if (current->signal & ~current->blocked) {
- TRACE(4, "caused by signal ?");
- }
- }
-#endif
- if (current->signal & ~current->blocked) {
- result = -EINTR;
- } else {
- result = -ETIME;
- }
- need_cleanup = 1; /* missing interrupt, reset fdc. */
- }
- } else {
- result = 0;
- }
- /* In first instance, next statement seems unnecessary since
- * it will be cleared in fdc_command. However, a small part of
- * the software seems to rely on this being cleared here
- * (ftape_close might fail) so stick to it until things get fixed !
- */
- interrupt_seen = 0; /* clear for next call */
-
- if (need_cleanup & !resetting) {
- resetting = 1; /* break infinite recursion if reset fails */
- TRACE(8, "cleanup reset");
- fdc_reset();
- resetting = 0;
- }
- TRACE_EXIT;
- return result;
-}
-
-/* Start/stop drive motor. Enable DMA mode.
- */
-void fdc_motor(int motor)
-{
- TRACE_FUN(8, "fdc_motor");
- int unit = FTAPE_UNIT;
- int data = unit | FDC_RESET_NOT | FDC_DMA_MODE;
-
- ftape_motor = motor;
- if (ftape_motor) {
- data |= FDC_MOTOR_0 << unit;
- TRACEx1(4, "turning motor %d on", unit);
- } else {
- TRACEx1(4, "turning motor %d off", unit);
- }
-#ifdef MACH2
- outb_p(data, fdc.dor2);
-#else
- outb_p(data, fdc.dor);
-#endif
- ftape_sleep(10 * MILLISECOND);
- TRACE_EXIT;
-}
-
-static void fdc_update_dsr(void)
-{
- TRACE_FUN(8, "fdc_update_dsr");
-
- TRACEx2(5, "rate = %d, precomp = %d", fdc_data_rate, fdc_precomp);
- if (fdc.type >= i82077) {
- outb_p((fdc_data_rate & 0x03) | fdc_precomp, fdc.dsr);
- } else {
- outb_p(fdc_data_rate, fdc.ccr);
- }
- TRACE_EXIT;
-}
-
-void fdc_set_write_precomp(int precomp)
-{
- /* write precompensation can be set in multiples of 41.67 nsec.
- * round the parameter to the nearest multiple and convert it
- * into a fdc setting. Note that 0 means default to the fdc,
- * 7 is used instead of that.
- */
- fdc_precomp = ((precomp + 21) / 42) << 2;
- if (fdc_precomp == 0) {
- fdc_precomp = 7 << 2;
- }
- fdc_update_dsr();
-}
-
-/* Read back the Drive Specification regs on an i82078, so that we
- * are able to restore them later
- */
-void fdc_save_drive_specs(void)
-{
- byte cmd1[] =
- {FDC_DRIVE_SPEC, 0x80};
- byte cmd2[] =
- {FDC_DRIVE_SPEC, 0x00, 0x00, 0x00, 0x00, 0xc0};
- int result;
-
- TRACE_FUN(8, "fdc_save_drive_specs");
- if (fdc.type >= i82078_1) {
- result = fdc_issue_command(cmd1, NR_ITEMS(cmd1), fdc_drv_spec, 4);
- if (result >= 0) {
- cmd2[1] = (fdc_drv_spec[0] & 0x03) | 0x04;
- cmd2[2] = (fdc_drv_spec[1] & 0x03) | 0x24;
- cmd2[3] = (fdc_drv_spec[2] & 0x03) | 0x44;
- cmd2[4] = (fdc_drv_spec[3] & 0x03) | 0x64;
- fdc_command(cmd2, NR_ITEMS(cmd2));
- if (result < 0) {
- TRACE(1, "Setting of drive specs failed");
- return;
- }
- } else {
- TRACE(2, "Save of drive specs failed");
- }
- }
- TRACE_EXIT;
-}
-
-/* Restore the previously saved Drive Specification values */
-void fdc_restore_drive_specs(void)
-{
- byte cmd[] =
- {FDC_DRIVE_SPEC, 0x00, 0x00, 0x00, 0x00, 0xc0};
- int result;
-
- TRACE_FUN(8, "fdc_restore_drive_specs");
- if (fdc.type > i82078_1) {
- cmd[1] = (fdc_drv_spec[0] & 0x1f) | 0x00;
- cmd[2] = (fdc_drv_spec[1] & 0x1f) | 0x20;
- cmd[3] = (fdc_drv_spec[2] & 0x1f) | 0x40;
- cmd[4] = (fdc_drv_spec[3] & 0x1f) | 0x60;
- result = fdc_command(cmd, NR_ITEMS(cmd));
- if (result < 0) {
- TRACE(2, "Restoration of drive specs failed");
- }
- }
- TRACE_EXIT;
-}
-
-/* Select clock for fdc, must correspond with tape drive setting !
- * This also influences the fdc timing so we must adjust some values.
- */
-void fdc_set_data_rate(int rate)
-{
- /* Select clock for fdc, must correspond with tape drive setting !
- * This also influences the fdc timing so we must adjust some values.
- */
- fdc_data_rate = rate;
- fdc_update_dsr();
- fdc_set_seek_rate(fdc_seek_rate); /* re-adjust for changed clock */
-}
-
-/* Reset the floppy disk controller. Leave the ftape_unit selected.
- */
-void fdc_reset(void)
-{
- TRACE_FUN(8, "fdc_reset");
- int unit = FTAPE_UNIT;
- byte fdc_ctl = unit | FDC_DMA_MODE;
- int st0;
- int i;
- int result;
- int dummy;
-
- if (ftape_motor) {
- fdc_ctl |= FDC_MOTOR_0 << unit;
- }
-#ifdef MACH2
- outb_p(fdc_ctl & 0x0f, fdc.dor);
- outb_p(fdc_ctl, fdc.dor2);
-#else
- outb_p(fdc_ctl, fdc.dor); /* assert reset, keep unit selected */
-#endif
- fdc_usec_wait(10 /* usec */ ); /* delay >= 14 fdc clocks */
- fdc_ctl |= FDC_RESET_NOT;
- fdc_mode = fdc_idle;
-#ifdef MACH2
- outb_p(fdc_ctl & 0x0f, fdc.dor);
- outb_p(fdc_ctl, fdc.dor2);
-#else
- outb_p(fdc_ctl, fdc.dor); /* release reset */
-#endif
- result = fdc_interrupt_wait(1 * SECOND);
- if (result < 0) {
- TRACE(1, "missing interrupt after reset");
- }
- fdc_set_data_rate(fdc_data_rate); /* keep original setting */
- fdc_usec_wait(1000 /* usec */ ); /* don't know why, but needed */
- for (i = 0; i < 4; ++i) { /* clear disk-change status */
- fdc_sense_interrupt_status(&st0, &dummy);
- if (i == unit) {
- current_cylinder = dummy;
- }
- }
- fdc_set_seek_rate(2);
- TRACE_EXIT;
-}
-
-/* When we're done, put the fdc into reset mode so that the regular
- floppy disk driver will figure out that something is wrong and
- initialize the controller the way it wants. */
-void fdc_disable(void)
-{
- TRACE_FUN(8, "fdc_disable");
- int result;
- byte cmd1[] = {FDC_CONFIGURE, 0x00, 0x00, 0x00};
- byte cmd2[] = {FDC_LOCK};
- byte cmd3[] = {FDC_UNLOCK};
- byte stat[1];
-
- if (CLK_48MHZ && fdc.type >= i82078)
- cmd1[0] |= FDC_CLK48_BIT;
- if (fdc_fifo_locked) {
- result = fdc_issue_command(cmd3, 1, stat, 1);
- if (result < 0 || stat[0] != 0x00) {
- TRACE(-1, "couldn't unlock fifo, configuration remains changed");
- } else {
- cmd1[2] = ((fdc_fifo_state) ? 0 : 0x20) + (fdc_fifo_thr - 1);
- result = fdc_command(cmd1, NR_ITEMS(cmd1));
- if (result < 0) {
- TRACE(-1, "couldn't reconfigure fifo to old state");
- } else if (fdc_lock_state) {
- result = fdc_issue_command(cmd2, 1, stat, 1);
- if (result < 0) {
- TRACE(-1, "couldn't lock old state again");
- }
- }
- TRACEx3(5, "fifo restored: %sabled, thr. %d, %slocked",
- fdc_fifo_state ? "en" : "dis",
- fdc_fifo_thr, (fdc_lock_state) ? "" : "not ");
- }
- fdc_fifo_locked = 0;
- }
-#ifdef MACH2
- outb_p(FTAPE_UNIT & 0x0f, fdc.dor);
- outb_p(FTAPE_UNIT, fdc.dor2);
- udelay(10);
- outb_p(FDC_RESET_NOT & 0x0f, fdc.dor);
- outb_p(FDC_RESET_NOT, fdc.dor2);
-#else
- outb_p(FTAPE_UNIT, fdc.dor);
- udelay(10);
- outb_p(FDC_RESET_NOT, fdc.dor);
-#endif
- TRACE_EXIT;
-}
-
-/* Specify FDC seek-rate
- */
-int fdc_set_seek_rate(int seek_rate)
-{
- byte in[3];
- const int hut = 1; /* minimize head unload time */
- const int hlt = 1; /* minimize head load time */
- const int rates[] = {250, 2000, 500, 1000};
-
- in[0] = FDC_SPECIFY;
- in[1] = (((16 - (rates[fdc_data_rate & 0x03] * seek_rate) / 500) << 4) |
- hut);
- in[2] = (hlt << 1) | 0;
- fdc_seek_rate = seek_rate;
-
- return fdc_command(in, 3);
-}
-
-/* Sense drive status: get unit's drive status (ST3)
- */
-int fdc_sense_drive_status(int *st3)
-{
- TRACE_FUN(8, "fdc_sense_drive_status");
- int result;
- byte out[2];
- byte in[1];
-
- out[0] = FDC_SENSED;
- out[1] = FTAPE_UNIT;
- result = fdc_issue_command(out, 2, in, 1);
- if (result < 0) {
- TRACE(1, "issue_command failed");
- } else {
- *st3 = in[0];
- result = 0;
- }
- TRACE_EXIT;
- return result;
-}
-
-/* Sense Interrupt Status command:
- * should be issued at the end of each seek.
- * get ST0 and current cylinder.
- */
-int fdc_sense_interrupt_status(int *st0, int *current_cylinder)
-{
- TRACE_FUN(8, "fdc_sense_interrupt_status");
- int result;
- byte out[1];
- byte in[2];
-
- out[0] = FDC_SENSEI;
- result = fdc_issue_command(out, 1, in, 2);
- if (result) {
- TRACE(1, "issue_command failed");
- } else {
- *st0 = in[0];
- *current_cylinder = in[1];
- result = 0;
- }
- TRACE_EXIT;
- return result;
-}
-
-/* step to track
- */
-int fdc_seek(int track)
-{
- TRACE_FUN(8, "fdc_seek");
- int result;
- byte out[3];
- int st0, pcn;
-
- out[0] = FDC_SEEK;
- out[1] = FTAPE_UNIT;
- out[2] = track;
- seek_completed = 0;
- result = fdc_command(out, 3);
- if (result != 0) {
- TRACEi(1, "failed, status =", result);
- TRACEx1(4, "destination was: %d, resetting FDC...", track);
- /* We really need this command to work !
- */
- fdc_reset();
- TRACE_EXIT;
- return result;
- }
- /* Handle interrupts until seek_completed or timeout.
- */
- for (;;) {
- result = fdc_interrupt_wait(2 * SECOND);
- if (result < 0) {
- TRACEi(2, "fdc_interrupt_wait timeout, status =", result);
- TRACE_EXIT;
- return result;
- } else if (seek_completed) {
- result = fdc_sense_interrupt_status(&st0, &pcn);
- if (result != 0) {
- TRACEi(1, "fdc_sense_interrupt_status failed, status =", result);
- TRACE_EXIT;
- return result;
- }
- if ((st0 & ST0_SEEK_END) == 0) {
- TRACE(1, "no seek-end after seek completion !??");
- TRACE_EXIT;
- return -EIO;
- }
- break;
- }
- }
- /* Verify whether we issued the right tape command.
- */
- /* Verify that we seek to the proper track. */
- if (pcn != track) {
- TRACE(1, "bad seek..");
- TRACE_EXIT;
- return -EIO;
- }
- current_cylinder = pcn;
- TRACE_EXIT;
- return 0;
-}
-
-/* Recalibrate and wait until home.
- */
-int fdc_recalibrate(void)
-{
- TRACE_FUN(8, "fdc_recalibrate");
- int result;
- byte out[2];
- int st0;
- int pcn;
- int retry;
-
- result = fdc_set_seek_rate(6);
- if (result) {
- TRACEi(1, "fdc_set_seek_rate failed, status =", result);
- TRACE_EXIT;
- return result;
- }
- out[0] = FDC_RECAL;
- out[1] = FTAPE_UNIT;
- seek_completed = 0;
- result = fdc_command(out, 2);
- if (result) {
- TRACEi(1, "fdc_command failed, status =", result);
- TRACE_EXIT;
- return result;
- }
- /* Handle interrupts until seek_completed or timeout.
- */
- for (retry = 0;; ++retry) {
- result = fdc_interrupt_wait(2 * SECOND);
- if (result < 0) {
- TRACE(1, "fdc_interrupt_wait failed");
- TRACE_EXIT;
- return result;
- } else if (result == 0 && seek_completed) {
- result = fdc_sense_interrupt_status(&st0, &pcn);
- if (result != 0) {
- TRACEi(1, "fdc_sense_interrupt_status failed, status =", result);
- TRACE_EXIT;
- return result;
- }
- if ((st0 & ST0_SEEK_END) == 0) {
- if (retry < 1) {
- continue; /* some drives/fdc's give an extra interrupt */
- } else {
- TRACE(1, "no seek-end after seek completion !??");
- TRACE_EXIT;
- return -EIO;
- }
- }
- break;
- }
- }
- current_cylinder = pcn;
- if (pcn != 0) {
- TRACEi(1, "failed: resulting track =", pcn);
- }
- result = fdc_set_seek_rate(2);
- if (result != 0) {
- TRACEi(1, "fdc_set_seek_rate failed, status =", result);
- TRACE_EXIT;
- return result;
- }
- TRACE_EXIT;
- return 0;
-}
-
-/* Setup Floppy Disk Controller and DMA to read or write the next cluster
- * of good sectors from or to the current segment.
- */
-int setup_fdc_and_dma(buffer_struct * buff, unsigned char operation)
-{
- TRACE_FUN(8, "setup_fdc_and_dma");
- unsigned long flags;
- byte perpend[] = {FDC_PERPEND, 0x00};
- unsigned char out[9];
- int result;
- int dma_mode;
-
- if (operation == FDC_READ || operation == FDC_READ_DELETED) {
- dma_mode = DMA_MODE_READ;
- if (qic_std == QIC_TAPE_QIC3020) {
- if (fdc.type < i82077AA) {
- /* fdc does not support perpendicular mode. complain */
- TRACE(0, "Your FDC does not support QIC-3020.");
- return -EIO;
- }
- /* enable perpendicular mode */
- perpend[1] = 0x83 + (0x04 << FTAPE_UNIT);
- result = fdc_command(perpend, 2);
- if (result < 0) {
- TRACE(1, "Perpendicular mode entry failed!");
- } else {
- TRACE(4, "Perpendicular mode entered");
- perpend_mode = 1;
- }
- } else if (perpend_mode) {
- /* Turn off perpendicular mode */
- perpend[1] = 0x80;
- result = fdc_command(perpend, 2);
- if (result < 0) {
- TRACE(1, "Perpendicular mode exit failed!");
- } else {
- TRACE(4, "Perpendicular mode exited");
- perpend_mode = 0;
- }
- }
- TRACEx2(5, "xfer %d sectors to 0x%p", buff->sector_count, buff->ptr);
- } else if (operation == FDC_WRITE || operation == FDC_WRITE_DELETED) {
- dma_mode = DMA_MODE_WRITE;
- /* When writing QIC-3020 tapes, turn on perpendicular mode.
- */
- if (qic_std == QIC_TAPE_QIC3020) {
- if (fdc.type < i82077AA) {
- /* fdc does not support perpendicular mode: complain */
- TRACE(0, "Your FDC does not support QIC-3020.");
- return -EIO;
- }
- perpend[1] = 0x83 + (0x4 << FTAPE_UNIT);
- result = fdc_command(perpend, 2);
- if (result < 0) {
- TRACE(1, "Perpendicular mode entry failed!");
- } else {
- TRACE(4, "Perpendicular mode entered");
- perpend_mode = 1;
- }
- } else if (perpend_mode) {
- perpend[1] = 0x80;
- result = fdc_command(perpend, 2);
- if (result < 0) {
- TRACE(1, "Perpendicular mode exit failed!");
- } else {
- TRACE(4, "Perpendicular mode exited");
- perpend_mode = 0;
- }
- }
- TRACEx2(5, "xfer %d sectors from 0x%p", buff->sector_count, buff->ptr);
- } else {
- TRACE(-1, "bug: illegal operation parameter");
- TRACE_EXIT;
- return -EIO;
- }
- /* Program the DMA controller.
- */
- save_flags(flags);
- cli(); /* could be called from ISR ! */
- disable_dma(fdc.dma);
- clear_dma_ff(fdc.dma);
- set_dma_mode(fdc.dma, dma_mode);
- set_dma_addr(fdc.dma, (unsigned) buff->ptr);
- set_dma_count(fdc.dma, SECTOR_SIZE * buff->sector_count);
-#ifdef GCC_2_4_5_BUG
- /* This seemingly stupid construction confuses the gcc-2.4.5
- * code generator enough to create correct code.
- */
- if (1) {
- int i;
-
- for (i = 0; i < 1; ++i) {
- udelay(1);
- }
- }
-#endif
- enable_dma(fdc.dma);
- /* Issue FDC command to start reading/writing.
- */
- out[0] = operation;
- out[1] = FTAPE_UNIT;
- out[2] = buff->cyl;
- out[3] = buff->head;
- out[4] = buff->sect + buff->sector_offset;
- out[5] = 3; /* Sector size of 1K. */
- out[6] = out[4] + buff->sector_count - 1; /* last sector */
- out[7] = 109; /* Gap length. */
- out[8] = 0xff; /* No limit to transfer size. */
- restore_flags(flags);
- TRACEx4(6, "C: 0x%02x, H: 0x%02x, R: 0x%02x, cnt: 0x%02x",
- out[2], out[3], out[4], out[6] - out[4] + 1);
- result = fdc_command(out, 9);
- if (result != 0) {
- fdc_mode = fdc_idle;
- TRACE(1, "fdc_command failed");
- }
- fdc_setup_error = result;
- TRACE_EXIT;
- return result;
-}
-
-int fdc_fifo_enable(void)
-{
- TRACE_FUN(8, "fdc_fifo_enable");
- int result = 0;
- byte cmd0[] = {FDC_DUMPREGS};
- byte cmd1[] = {FDC_CONFIGURE, 0, 0x07, 0}; /* enable fifo, thr = 8 */
- byte cmd2[] = {FDC_LOCK};
- byte cmd3[] = {FDC_UNLOCK};
- byte stat;
- byte reg[10];
- int i;
-
- if (CLK_48MHZ && fdc.type >= i82078)
- cmd1[0] |= FDC_CLK48_BIT;
- if (!fdc_fifo_locked) {
- /* Dump fdc internal registers for examination
- */
- result = fdc_command(cmd0, NR_ITEMS(cmd0));
- if (result < 0) {
- TRACE(2, "FDC dumpreg command failed, fifo unchanged");
- result = -EIO;
- } else {
- /* Now read fdc internal registers from fifo
- */
- for (i = 0; i < NR_ITEMS(reg); ++i) {
- fdc_read(&reg[i]);
- TRACEx2(6, "Register %d = 0x%02x", i, reg[i]);
- }
- fdc_fifo_state = (reg[8] & 0x20) == 0;
- fdc_lock_state = reg[7] & 0x80;
- fdc_fifo_thr = 1 + (reg[8] & 0x0f);
- TRACEx3(5, "original fifo state: %sabled, threshold %d, %slocked",
- (fdc_fifo_state) ? "en" : "dis",
- fdc_fifo_thr, (fdc_lock_state) ? "" : "not ");
- /* If fdc is already locked, unlock it first !
- */
- if (fdc_lock_state) {
- fdc_ready_wait(100);
- result = fdc_command(cmd3, NR_ITEMS(cmd3));
- if (result < 0) {
- TRACE(-1, "FDC unlock command failed, configuration unchanged");
- result = -EIO;
- }
- }
- /* Enable fifo and set threshold at xx bytes to allow a
- * reasonably large latency and reduce number of dma bursts.
- */
- fdc_ready_wait(100);
- result = fdc_command(cmd1, NR_ITEMS(cmd1));
- if (result < 0) {
- TRACE(-1, "FDC configure command failed, fifo unchanged");
- result = -EIO;
- } else {
- /* Now lock configuration so reset will not change it
- */
- result = fdc_issue_command(cmd2, NR_ITEMS(cmd2), &stat, 1);
- if (result < 0 || stat != 0x10) {
- TRACEx1(-1, "FDC lock command failed, stat = 0x%02x", stat);
- result = -EIO;
- } else {
- fdc_fifo_locked = 1;
- result = 0;
- }
- }
- }
- } else {
- TRACE(2, "Fifo not enabled because locked");
- }
- TRACE_EXIT;
- return result;
-}
-
-/* Determine fd controller type
- */
-static byte fdc_save_state[2] = {0, 0};
-
-int fdc_probe(void)
-{
- TRACE_FUN(8, "fdc_probe");
- byte cmd[1];
- byte stat[16]; /* must be able to hold dumpregs & save results */
- int result;
-
- /* Try to find out what kind of fd controller we have to deal with
- * Scheme borrowed from floppy driver:
- * first try if FDC_DUMPREGS command works
- * (this indicates that we have a 82072 or better)
- * then try the FDC_VERSION command (82072 doesn't support this)
- * then try the FDC_UNLOCK command (some older 82077's don't support this)
- * then try the FDC_PARTID command (82078's support this)
- */
- cmd[0] = FDC_DUMPREGS;
- result = fdc_issue_command(cmd, 1, stat, 1);
- if (result == 0) {
- if (stat[0] == 0x80) {
- /* invalid command: must be pre 82072
- */
- TRACE(2, "Type 8272A/765A compatible FDC found");
- result = i8272;
- } else {
- fdc_result(&stat[1], 9);
- fdc_save_state[0] = stat[7];
- fdc_save_state[1] = stat[8];
- cmd[0] = FDC_VERSION;
- result = fdc_issue_command(cmd, 1, stat, 1);
- if (result < 0 || stat[0] == 0x80) {
- TRACE(2, "Type 82072 FDC found");
- result = i8272;
- } else if (*stat == 0x90) {
- cmd[0] = FDC_UNLOCK;
- result = fdc_issue_command(cmd, 1, stat, 1);
- if (result < 0 || stat[0] != 0x00) {
- TRACE(2, "Type pre-1991 82077 FDC found, treating it like a 82072");
- result = i8272;
- } else {
- int i;
-
- if (fdc_save_state[0] & 0x80) { /* was locked */
- cmd[0] = FDC_LOCK; /* restore lock */
- result = fdc_issue_command(cmd, 1, stat, 1);
- TRACE(2, "FDC is already locked");
- }
- /* Test for an i82078 FDC */
- cmd[0] = FDC_PARTID;
- result = fdc_issue_command(cmd, 1, stat, 1);
- if (result < 0 || stat[0] == 0x80) {
- /* invalid command: not an i82078xx type FDC */
- result = no_fdc;
- for (i = 0; i < 4; ++i) {
- outb_p(i, fdc.tdr);
- if ((inb_p(fdc.tdr) & 0x03) != i) {
- result = i82077;
- break;
- }
- }
- if (result == no_fdc) {
- result = i82077AA;
- TRACE(2, "Type 82077AA FDC found");
- } else {
- TRACE(2, "Type 82077 FDC found");
- }
- } else {
- /* FDC_PARTID cmd succeeded */
- switch (stat[0] >> 5) {
- case 0x0:
- /* i82078SL or i82078-1. The SL part cannot run at 2Mbps (the
- * SL and -1 dies are identical; they are speed graded after
- * production, according to Intel). Some SL's can be detected
- * by doing a SAVE cmd and look at bit 7 of the first byte (the
- * SEL3V# bit). If it is 0, the part runs off 3Volts, and hence
- * it is a SL.
- */
- cmd[0] = FDC_SAVE;
- result = fdc_issue_command(cmd, 1, stat, 16);
- if (result < 0) {
- TRACE(1, "FDC_SAVE failed. Dunno why");
- /* guess we better claim the fdc to be an i82078 */
- result = i82078;
- TRACE(2, "Type i82078 FDC (i suppose) found");
- } else {
- if ((stat[0] & FDC_SEL3V_BIT)) {
- /* fdc running off 5Volts; Pray that it's an i82078-1
- */
- TRACE(2, "Type i82078-1 or 5Volt i82078SL FDC found");
- TRACE(2, "Treating it as an i82078-1 (2Mbps) FDC");
- result = i82078_1;
- } else {
- TRACE(2, "Type 3Volt i82078SL FDC (1Mbps) found");
- result = i82078;
- }
- }
- break;
- case 0x1:
- case 0x2: /* S82078B (?!) */
- /* 44pin i82078 found */
- result = i82078;
- TRACE(2, "Type i82078 FDC found");
- break;
- case 0x3: /* NSC PC8744 core; used in several super-IO chips */
- result = i82077AA;
- TRACE(2, "Type 82077AA compatible FDC found");
- break;
- default:
- TRACE(2, "A previously undetected FDC found");
- TRACEi(2, "Treating it as a 82077AA. Please report partid=",
- stat[0]);
- result = i82077AA;
- } /* switch(stat[ 0] >> 5) */
- } /* if (result < 0 || stat[ 0] == 0x80) */
- }
- } else {
- TRACE(2, "Unknown FDC found");
- result = i8272;
- }
- }
- } else {
- TRACE(-1, "No FDC found");
- result = no_fdc;
- }
- TRACE_EXIT;
- return result;
-}
-
-void fdc_config_regs(unsigned fdc_base, unsigned fdc_irq, unsigned fdc_dma)
-{
- fdc.irq = fdc_irq;
- fdc.dma = fdc_dma;
- fdc.sra = fdc_base;
- fdc.srb = fdc_base + 1;
- fdc.dor = fdc_base + 2;
- fdc.tdr = fdc_base + 3;
- fdc.msr = fdc.dsr = fdc_base + 4;
- fdc.fifo = fdc_base + 5;
-#if defined MACH2 || defined PROBE_FC10
- fdc.dor2 = fdc_base + 6;
-#endif
- fdc.dir = fdc.ccr = fdc_base + 7;
-}
-
-/* If probing for a FC-10/20 controller the fdc base address, interrupt
- * and dma channel must be specified.
- * If using an alternate fdc controller, base address, interrupt and
- * dma channel must be specified.
- */
-#if defined PROBE_FC10 && !defined FDC_BASE
-#error No FDC base address (FDC_BASE) specified in Makefile!
-#endif
-#if defined FDC_BASE && !defined FDC_IRQ
-#error No interrupt (FDC_IRQ) specified in Makefile!
-#endif
-#if defined FDC_BASE && !defined FDC_DMA
-#error No dma channel (FDC_DMA) specified in Makefile!
-#endif
-
-void fdc_config(void)
-{
- TRACE_FUN(8, "fdc_config");
- static int already_done = 0;
-
- if (!already_done) {
-#ifdef PROBE_FC10
- int fc_type;
-
- fdc_config_regs(FDC_BASE, FDC_IRQ, FDC_DMA);
- fc_type = fc10_enable();
- if (fc_type != 0) {
- TRACEx1(2, "FC-%c0 controller found", '0' + fc_type);
- fdc.type = fc10;
- fdc.hook = &do_ftape;
- } else {
- TRACE(2, "FC-10/20 controller not found");
- fdc.type = no_fdc;
- fdc.dor2 = 0; /* not used with std fdc */
- fdc_config_regs(0x3f0, 6, 2); /* back to std fdc again */
- fdc.hook = &do_ftape;
- }
-#else
-#ifdef FDC_BASE
- TRACE(2, "Using fdc controller at alternate address");
- fdc_config_regs(FDC_BASE, FDC_IRQ, FDC_DMA);
- fdc.hook = &do_ftape;
-#else
- TRACE(2, "Using the standard fdc controller");
- fdc_config_regs(0x3f0, 6, 2); /* std fdc */
- fdc.hook = &do_ftape;
-#endif /* !FDC_BASE */
-#endif /* !PROBE_FC10 */
- }
- *(fdc.hook) = fdc_isr; /* hook our handler in */
- already_done = 1;
- TRACE_EXIT;
-}
-
-static void ftape_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
- TRACE_FUN(8, "ftape_interrupt");
- void (*handler) (void) = *fdc.hook;
-
- *fdc.hook = NULL;
- if (handler) {
- handler();
- } else {
- TRACE(-1, "Unexpected ftape interrupt");
- }
- TRACE_EXIT;
-}
-
-int fdc_grab_irq_and_dma(void)
-{
- TRACE_FUN(8, "fdc_grab_irq_and_dma");
- int result = 0;
-
- if (fdc.hook == &do_ftape) {
- /* Get fast interrupt handler.
- */
- result = request_irq(fdc.irq, ftape_interrupt, SA_INTERRUPT,
- "ftape", ftape_id);
- if (result) {
- TRACEx1(-1, "Unable to grab IRQ%d for ftape driver", fdc.irq);
- result = -EIO;
- } else {
- result = request_dma(fdc.dma, ftape_id);
- if (result) {
- TRACEx1(-1, "Unable to grab DMA%d for ftape driver", fdc.dma);
- free_irq(fdc.irq, ftape_id);
- result = -EIO;
- } else {
- enable_irq(fdc.irq);
- }
- }
- }
-#ifdef FDC_DMA
- if (result == 0 && FDC_DMA == 2) {
- /* Using same dma channel as standard fdc, need to disable the
- * dma-gate on the std fdc. This couldn't be done in the floppy
- * driver as some laptops are using the dma-gate to enter a
- * low power or even suspended state :-(
- */
- outb_p(FDC_RESET_NOT, 0x3f2);
- TRACE(2, "DMA-gate on standard fdc disabled");
- }
-#endif
- TRACE_EXIT;
- return result;
-}
-
-int fdc_release_irq_and_dma(void)
-{
- TRACE_FUN(8, "fdc_grab_irq_and_dma");
- int result = 0;
-
- if (fdc.hook == &do_ftape) {
- disable_dma(fdc.dma); /* just in case... */
- free_dma(fdc.dma);
- disable_irq(fdc.irq);
- free_irq(fdc.irq, ftape_id);
- }
-#ifdef FDC_DMA
- if (result == 0 && FDC_DMA == 2) {
- /* Using same dma channel as standard fdc, need to disable the
- * dma-gate on the std fdc. This couldn't be done in the floppy
- * driver as some laptops are using the dma-gate to enter a
- * low power or even suspended state :-(
- */
- outb_p(FDC_RESET_NOT | FDC_DMA_MODE, 0x3f2);
- TRACE(2, "DMA-gate on standard fdc enabled again");
- }
-#endif
- TRACE_EXIT;
- return result;
-}
-
-int fdc_uninit(void)
-{
- TRACE_FUN(8, "fdc_uninit");
- int result = 0;
-
- if (fdc.sra != 0) {
- if (fdc.dor2 == 0) {
- release_region(fdc.sra, 6);
- release_region(fdc.sra + 7, 1);
- } else {
- release_region(fdc.sra, 8);
- }
- }
- TRACE_EXIT;
- return result;
-}
-
-int fdc_init(void)
-{
- TRACE_FUN(8, "fdc_init");
- int result = 0;
-
- fdc_config();
- if (fdc_grab_irq_and_dma() < 0) {
- result = -EBUSY;
- } else {
- ftape_motor = 0;
- fdc_catch_stray_interrupts(1); /* one always comes */
- TRACE(5, "resetting fdc");
- fdc_reset(); /* init fdc & clear track counters */
- if (fdc.type == no_fdc) { /* default, means no FC-10 or 20 found */
- fdc.type = fdc_probe();
- }
- if (fdc.type != no_fdc) {
- if (fdc.type >= i82077) {
- if (fdc_fifo_enable() < 0) {
- TRACE(2, "couldn't enable fdc fifo !");
- } else {
- TRACE(5, "fdc fifo enabled and locked");
- }
- }
- } else {
- fdc_release_irq_and_dma();
- result = -EIO;
- }
- }
- if (result >= 0) {
- if (fdc.dor2 == 0) {
- request_region(fdc.sra, 6, "fdc (ftape)");
- request_region(fdc.sra + 7, 1, "fdc (ftape)");
- } else {
- request_region(fdc.sra, 8, "fdc (ftape)");
- }
- }
- TRACE_EXIT;
- return result;
-}
diff --git a/drivers/char/ftape/fdc-io.h b/drivers/char/ftape/fdc-io.h
deleted file mode 100644
index 083eab7be..000000000
--- a/drivers/char/ftape/fdc-io.h
+++ /dev/null
@@ -1,182 +0,0 @@
-#ifndef _FDC_IO_H
-#define _FDC_IO_H
-
-/*
- * Copyright (C) 1993-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING. If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- *
- $Source: /home/bas/distr/ftape-2.03b/RCS/fdc-io.h,v $
- $Author: bas $
- *
- $Revision: 1.38 $
- $Date: 1995/05/10 16:09:36 $
- $State: Beta $
- *
- * This file contains the low level functions
- * that communicate with the floppy disk controller,
- * for the QIC-40/80 floppy-tape driver for Linux.
- */
-
-#include <linux/fdreg.h>
-
-#define FDC_SK_BIT (0x20)
-#define FDC_MT_BIT (0x80)
-
-#define FDC_READ (FD_READ & ~(FDC_SK_BIT | FDC_MT_BIT))
-#define FDC_WRITE (FD_WRITE & ~FDC_MT_BIT)
-#define FDC_READ_DELETED (0x4c)
-#define FDC_WRITE_DELETED (0x49)
-#define FDC_READID (0x4a)
-#define FDC_SENSED (0x04)
-#define FDC_SENSEI (FD_SENSEI)
-#define FDC_RECAL (FD_RECALIBRATE)
-#define FDC_SEEK (FD_SEEK)
-#define FDC_SPECIFY (FD_SPECIFY)
-#define FDC_RECALIBR (FD_RECALIBRATE)
-#define FDC_VERSION (FD_VERSION)
-#define FDC_PERPEND (FD_PERPENDICULAR)
-#define FDC_DUMPREGS (FD_DUMPREGS)
-#define FDC_LOCK (FD_LOCK)
-#define FDC_UNLOCK (FD_UNLOCK)
-#define FDC_CONFIGURE (FD_CONFIGURE)
-#define FDC_DRIVE_SPEC (0x8e) /* i82078 has this (any others?) */
-#define FDC_PARTID (0x18) /* i82078 has this */
-#define FDC_SAVE (0x2e) /* i82078 has this (any others?) */
-#define FDC_RESTORE (0x4e) /* i82078 has this (any others?) */
-
-#define FDC_STATUS_MASK (STATUS_BUSY | STATUS_DMA | STATUS_DIR | STATUS_READY)
-#define FDC_DATA_READY (STATUS_READY)
-#define FDC_DATA_OUTPUT (STATUS_DIR)
-#define FDC_DATA_READY_MASK (STATUS_READY | STATUS_DIR)
-#define FDC_DATA_OUT_READY (STATUS_READY | STATUS_DIR)
-#define FDC_DATA_IN_READY (STATUS_READY)
-#define FDC_BUSY (STATUS_BUSY)
-#define FDC_CLK48_BIT (0x80)
-#define FDC_SEL3V_BIT (0x40)
-
-#define ST0_INT_MASK (ST0_INTR)
-#define FDC_INT_NORMAL (ST0_INTR & 0x00)
-#define FDC_INT_ABNORMAL (ST0_INTR & 0x40)
-#define FDC_INT_INVALID (ST0_INTR & 0x80)
-#define FDC_INT_READYCH (ST0_INTR & 0xC0)
-#define ST0_SEEK_END (ST0_SE)
-#define ST3_TRACK_0 (ST3_TZ)
-
-#define FDC_RESET_NOT (0x04)
-#define FDC_DMA_MODE (0x08)
-#define FDC_MOTOR_0 (0x10)
-#define FDC_MOTOR_1 (0x20)
-
-typedef struct {
- void (**hook) (void); /* our wedge into the isr */
- enum {
- no_fdc, i8272, i82077, i82077AA, fc10,
- i82078, i82078_1
- } type; /* FDC type */
- unsigned char irq; /* FDC irq nr */
- unsigned char dma; /* FDC dma channel nr */
- unsigned short sra; /* Status register A (PS/2 only) */
- unsigned short srb; /* Status register B (PS/2 only) */
- unsigned short dor; /* Digital output register */
- unsigned short tdr; /* Tape Drive Register (82077SL-1 &
- 82078 only) */
- unsigned short msr; /* Main Status Register */
- unsigned short dsr; /* Datarate Select Register (8207x only) */
- unsigned short fifo; /* Data register / Fifo on 8207x */
- unsigned short dir; /* Digital Input Register */
- unsigned short ccr; /* Configuration Control Register */
- unsigned short dor2; /* Alternate dor on MACH-2 controller,
- also used with FC-10, meaning unknown */
-} fdc_config_info;
-
-typedef enum {
- fdc_data_rate_250 = 2,
- fdc_data_rate_500 = 0,
- fdc_data_rate_1000 = 3,
- fdc_data_rate_2000 = 1, /* i82078-1: remember to use Data Rate Table #2 */
-} fdc_data_rate_type;
-
-typedef enum {
- waiting = 0,
- reading,
- writing,
- done,
- error,
-} buffer_state_enum;
-
-typedef volatile enum {
- fdc_idle = 0,
- fdc_reading_data = FDC_READ,
- fdc_seeking = FDC_SEEK,
- fdc_writing_data = FDC_WRITE,
- fdc_reading_id = FDC_READID,
- fdc_recalibrating = FDC_RECAL,
-} fdc_mode_enum;
-
-/*
- * fdc-io.c defined public variables
- */
-extern fdc_mode_enum fdc_mode;
-extern volatile enum runner_status_enum runner_status;
-extern int old_vfo;
-extern volatile int head;
-extern volatile int tail;
-extern int fdc_setup_error; /* outdated ??? */
-extern struct wait_queue *wait_intr;
-extern volatile unsigned int next_segment; /* next segment for read ahead */
-extern int ftape_unit; /* fdc unit specified at ftape_open() */
-extern int ftape_motor; /* fdc motor line state */
-extern int current_cylinder; /* track nr the FDC thinks we're on */
-extern volatile byte fdc_head; /* FDC head */
-extern volatile byte fdc_cyl; /* FDC track */
-extern volatile byte fdc_sect; /* FDC sector */
-extern fdc_config_info fdc; /* FDC hardware configuration */
-
-/*
- * fdc-io.c defined public functions
- */
-extern void fdc_catch_stray_interrupts(unsigned count);
-extern int fdc_ready_wait(int timeout);
-extern int fdc_write(byte data);
-extern int fdc_read(byte * data);
-extern int fdc_command(byte * cmd_data, int cmd_len);
-extern int fdc_result(byte * res_data, int res_len);
-extern int fdc_issue_command(byte * out_data, int out_count, \
- byte * in_data, int in_count);
-extern void fdc_isr(void);
-extern int fdc_interrupt_wait(int time);
-extern void fdt_sleep(unsigned int time);
-extern int fdc_specify(int head_unload_time, int seek_rate,
- int head_load_time, int non_dma);
-extern int fdc_set_seek_rate(int seek_rate);
-extern int fdc_seek(int track);
-extern int fdc_sense_drive_status(int *st3);
-extern void fdc_motor(int motor);
-extern void fdc_reset(void);
-extern int fdc_recalibrate(void);
-extern void fdc_disable(void);
-extern int fdc_wait_calibrate(void);
-extern int fdc_sense_interrupt_status(int *st0, int *current_cylinder);
-extern void fdc_save_drive_specs(void);
-extern void fdc_restore_drive_specs(void);
-extern void fdc_set_data_rate(int rate);
-extern int fdc_release_irq_and_dma(void);
-extern int fdc_init(void);
-extern int fdc_uninit(void);
-extern void fdc_set_write_precomp(int precomp);
-
-#endif
diff --git a/drivers/char/ftape/fdc-isr.c b/drivers/char/ftape/fdc-isr.c
deleted file mode 100644
index 0fda85914..000000000
--- a/drivers/char/ftape/fdc-isr.c
+++ /dev/null
@@ -1,813 +0,0 @@
-/*
- * Copyright (C) 1994-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING. If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- $Source: /home/bas/distr/ftape-2.03b/RCS/fdc-isr.c,v $
- $Author: bas $
- *
- $Revision: 1.36 $
- $Date: 1995/05/27 08:54:21 $
- $State: Beta $
- *
- * This file contains the interrupt service routine and associated
- * code for the QIC-40/80 tape streamer device driver.
- */
-
-#include <linux/ftape.h>
-#include <asm/io.h>
-#include <asm/dma.h>
-
-#define volatile /* */
-
-#include "tracing.h"
-#include "fdc-isr.h"
-#include "qic117.h"
-#include "fdc-io.h"
-#include "ftape-ctl.h"
-#include "ftape-rw.h"
-#include "ftape-io.h"
-#include "calibr.h"
-#include "ftape-bsm.h"
-
-/* Global vars.
- */
-volatile int expected_stray_interrupts = 0;
-volatile int seek_completed = 0;
-volatile int interrupt_seen = 0;
-volatile int expect_stray_interrupt = 0;
-int random_rw = 0;
-
-/* Local vars.
- */
-typedef enum {
- no_error = 0, id_am_error = 0x01, id_crc_error = 0x02,
- data_am_error = 0x04, data_crc_error = 0x08,
- no_data_error = 0x10, overrun_error = 0x20,
-} error_cause;
-static int hide_interrupt;
-static int stop_read_ahead = 0;
-
-
-static void print_error_cause(int cause)
-{
- TRACE_FUN(8, "print_error_cause");
-
- switch (cause) {
- case no_data_error:
- TRACE(4, "no data error");
- break;
- case id_am_error:
- TRACE(4, "id am error");
- break;
- case id_crc_error:
- TRACE(4, "id crc error");
- break;
- case data_am_error:
- TRACE(4, "data am error");
- break;
- case data_crc_error:
- TRACE(4, "data crc error");
- break;
- case overrun_error:
- TRACE(4, "overrun error");
- break;
- default:
- }
- TRACE_EXIT;
-}
-
-static char *
-get_fdc_mode_text(fdc_mode_enum fdc_mode)
-{
- switch (fdc_mode) {
- case fdc_idle:
- return "fdc_idle";
- case fdc_reading_data:
- return "fdc_reading_data";
- case fdc_seeking:
- return "fdc_seeking";
- case fdc_writing_data:
- return "fdc_writing_data";
- case fdc_reading_id:
- return "fdc_reading_id";
- case fdc_recalibrating:
- return "fdc_recalibrating";
- default:
- return "unknown";
- }
-}
-
-static void
-decode_irq_cause(fdc_mode_enum fdc_mode, byte st[],
- char **fdc_mode_txt, error_cause * cause)
-{
- TRACE_FUN(8, "decode_irq_cause");
-
- /* Valid st[], decode cause of interrupt.
- */
- *fdc_mode_txt = get_fdc_mode_text(fdc_mode);
- switch (st[0] & ST0_INT_MASK) {
- case FDC_INT_NORMAL:
- TRACEx1(fdc_mode == fdc_reading_id ? 6 : 5,
- "normal completion: %s", *fdc_mode_txt);
- *cause = no_error;
- break;
- case FDC_INT_ABNORMAL:
- TRACEx1(5, "abnormal completion %s", *fdc_mode_txt);
- TRACEx3(6, "ST0: 0x%02x, ST1: 0x%02x, ST2: 0x%02x",
- st[0], st[1], st[2]);
- TRACEx4(6, "C: 0x%02x, H: 0x%02x, R: 0x%02x, N: 0x%02x",
- st[3], st[4], st[5], st[6]);
- if (st[1] & 0x01) {
- if (st[2] & 0x01) {
- *cause = data_am_error;
- } else {
- *cause = id_am_error;
- }
- } else if (st[1] & 0x20) {
- if (st[2] & 0x20) {
- *cause = data_crc_error;
- } else {
- *cause = id_crc_error;
- }
- } else if (st[1] & 0x04) {
- *cause = no_data_error;
- } else if (st[1] & 0x10) {
- *cause = overrun_error;
- }
- print_error_cause(*cause);
- break;
- case FDC_INT_INVALID:
- TRACEx1(5, "invalid completion %s", *fdc_mode_txt);
- *cause = no_error;
- break;
- case FDC_INT_READYCH:
- TRACEx1(5, "ready change %s", *fdc_mode_txt);
- *cause = no_error;
- break;
- default:
- }
- TRACE_EXIT;
-}
-
-static void update_history(error_cause cause)
-{
- switch (cause) {
- case id_am_error:
- history.id_am_errors++;
- break;
- case id_crc_error:
- history.id_crc_errors++;
- break;
- case data_am_error:
- history.data_am_errors++;
- break;
- case data_crc_error:
- history.data_crc_errors++;
- break;
- case overrun_error:
- history.overrun_errors++;
- break;
- case no_data_error:
- history.no_data_errors++;
- break;
- default:
- }
-}
-
-static void skip_bad_sector(buffer_struct * buff)
-{
- TRACE_FUN(8, "skip_bad_sector");
-
- /* Mark sector as soft error and skip it
- */
- if (buff->remaining > 0) {
- ++buff->sector_offset;
- ++buff->data_offset;
- --buff->remaining;
- buff->ptr += SECTOR_SIZE;
- buff->bad_sector_map >>= 1;
- } else {
- ++buff->sector_offset; /* hack for error maps */
- TRACE(1, "skipping last sector in segment");
- }
- TRACE_EXIT;
-}
-
-static void update_error_maps(buffer_struct * buff, unsigned error_offset)
-{
- TRACE_FUN(8, "update_error_maps");
- int hard = 0;
-
- /* error_offset is a sector offset !
- */
- if (buff->retry < SOFT_RETRIES) {
- buff->soft_error_map |= (1 << error_offset);
- } else {
- buff->hard_error_map |= (1 << error_offset);
- buff->soft_error_map &= ~buff->hard_error_map;
- buff->retry = -1; /* will be set to 0 in setup_segment */
- hard = 1;
- }
- TRACEx2(4, "sector %d : %s error", SECTOR(error_offset),
- hard ? "hard" : "soft");
- TRACEx2(5, "hard map: 0x%08lx, soft map: 0x%08lx",
- buff->hard_error_map, buff->soft_error_map);
- TRACE_EXIT;
-}
-
-/*
- * Error cause: Amount xferred: Action:
- *
- * id_am_error 0 mark bad and skip
- * id_crc_error 0 mark bad and skip
- * data_am_error 0 mark bad and skip
- * data_crc_error % 1024 mark bad and skip
- * no_data_error 0 retry on write
- * mark bad and skip on read
- * overrun_error [ 0..all-1 ] mark bad and skip
- * no_error all continue
- */
-static void determine_progress(buffer_struct * buff, error_cause cause, int mode)
-{
- TRACE_FUN(8, "determine_progress");
- unsigned nr_not_xferred;
- unsigned nr_xferred;
- unsigned dma_residue;
-
- /* Using less preferred order of disable_dma and get_dma_residue
- * because this seems to fail on at least one system if reversed!
- */
- dma_residue = get_dma_residue(fdc.dma);
- disable_dma(fdc.dma);
- nr_xferred = buff->sector_count * SECTOR_SIZE - dma_residue;
- if (cause == no_error && dma_residue == 0) {
- nr_not_xferred = 0;
- } else {
- if (cause == no_error) {
- TRACEx1(4, "unexpected DMA residue: 0x%04x", dma_residue);
- } else {
- TRACEx1(6, "DMA residue = 0x%04x", dma_residue);
- }
- nr_not_xferred = ((dma_residue + (SECTOR_SIZE - 1)) / SECTOR_SIZE);
- buff->sector_count -= nr_not_xferred; /* adjust to actual value */
- }
- /* Update var's influenced by the DMA operation.
- */
- if (buff->sector_count > 0) {
- buff->sector_offset += buff->sector_count;
- buff->data_offset += buff->sector_count;
- buff->ptr += buff->sector_count * SECTOR_SIZE;
- buff->remaining -= buff->sector_count;
- buff->bad_sector_map >>= buff->sector_count;
- }
- if (cause == no_error) {
- TRACEx1(5, "%d Sector(s) transferred", buff->sector_count);
- } else if (cause == no_data_error) {
- TRACEx1(5, "Sector %d not found", SECTOR(buff->sector_offset));
- } else if (nr_xferred > 0 || cause == id_crc_error ||
- cause == id_am_error || cause == data_am_error) {
- TRACEx1(5, "Error in sector %d", SECTOR(buff->sector_offset));
- } else if (cause == overrun_error) {
- /* got an overrun error on the first byte, must be a hardware problem
- */
- TRACE(-1, "Unexpected error: failing DMA controller ?");
- } else {
- TRACEx1(4, "Unexpected error at sector %d", SECTOR(buff->sector_offset));
- }
- /* Sector_offset points to the problem area, except if we got
- * a data_crc_error. In that case it points one past the failing
- * sector.
- * Now adjust sector_offset so it always points one past he
- * failing sector. I.e. skip the bad sector.
- */
- if (cause != no_error) {
- if (cause != data_crc_error) {
- skip_bad_sector(buff);
- }
- update_error_maps(buff, buff->sector_offset - 1);
- }
- TRACE_EXIT;
-}
-
-static int calc_steps(int cmd)
-{
- if (current_cylinder > cmd) {
- return current_cylinder - cmd;
- } else {
- return current_cylinder + cmd;
- }
-}
-
-static void pause_tape(unsigned segment, int retry, int fdc_mode)
-{
- TRACE_FUN(8, "pause_tape");
- int result;
- /* The 3rd initializer needs to be explicit or else gcc will
- * generate a reference to memset :-(
- */
- byte out[3] =
- {FDC_SEEK, FTAPE_UNIT, 0};
-
- /* We'll use a raw seek command to get the tape to rewind
- * and stop for a retry.
- */
- ++history.rewinds;
- if (qic117_cmds[current_command].non_intr) {
- TRACE(2, "motion command may be issued too soon");
- }
- if (retry && (fdc_mode == fdc_reading_data || fdc_mode == fdc_reading_id)) {
- current_command = QIC_MICRO_STEP_PAUSE;
- might_be_off_track = 1;
- } else {
- current_command = QIC_PAUSE;
- }
- out[2] = calc_steps(current_command);
- result = fdc_command(out, 3); /* issue QIC_117 command */
- if (result < 0) {
- TRACEx1(4, "qic-pause failed, status = %d", result);
- } else {
- location.known = 0;
- runner_status = idle;
- hide_interrupt = 1;
- tape_running = 0;
- }
- TRACE_EXIT;
-}
-
-static void stop_tape(unsigned segment)
-{
- TRACE_FUN(8, "stop_tape");
- int result;
- byte out[3] =
- {FDC_SEEK, FTAPE_UNIT, calc_steps(QIC_STOP_TAPE)};
-
- if (qic117_cmds[current_command].non_intr) {
- TRACE(2, "motion command may be issued too soon");
- }
- current_command = QIC_STOP_TAPE;
- /* We'll use a raw seek command to get the tape to stop
- */
- result = fdc_command(out, 3); /* issue QIC_117 command */
- if (result < 0) {
- TRACEx1(4, "qic-stop failed, status = %d", result);
- } else {
- runner_status = idle;
- hide_interrupt = 1;
- tape_running = 0;
- }
- TRACE_EXIT;
-}
-
-static void continue_xfer(buffer_struct ** p_buff, error_cause cause,
- int fdc_mode, unsigned skip)
-{
- TRACE_FUN(8, "continue_xfer");
- buffer_struct *buff = *p_buff;
- int write = (fdc_mode == fdc_writing_data);
- byte fdc_op = (write) ? FDC_WRITE : FDC_READ;
-
- if (skip > 0) {
- /* This part can be removed if it never happens
- */
- if (runner_status != running ||
- (buff->status != (write ? writing : reading))) {
- TRACEx2(1, "unexpected runner/buffer state %d/%d",
- runner_status, buff->status);
- buff->status = error;
- *p_buff = next_buffer(&head); /* finish this buffer */
- runner_status = aborting;
- fdc_mode = fdc_idle;
- }
- }
- if (buff->remaining > 0 && calc_next_cluster(&buffer[head]) > 0) {
- /* still sectors left in current segment, continue with this segment
- */
- if (setup_fdc_and_dma(&buffer[head], fdc_op) < 0) {
- /* failed, abort operation
- */
- buff->bytes = buff->ptr - buff->address;
- buff->status = error;
- buff = *p_buff = next_buffer(&head); /* finish this buffer */
- runner_status = aborting;
- fdc_mode = fdc_idle;
- }
- } else {
- /* current segment completed
- */
- unsigned last_segment = buff->segment_id;
- int eot = ((last_segment + 1) % segments_per_track) == 0;
- int next = buff->next_segment; /* 0 means stop ! */
-
- buff->bytes = buff->ptr - buff->address;
- buff->status = done;
- buff = *p_buff = next_buffer(&head);
- if (eot) {
- /* finished last segment on current track, can't continue
- */
- runner_status = logical_eot;
- fdc_mode = fdc_idle;
- } else if (next > 0) {
- /* continue with next segment
- */
- if (buff->status == waiting) {
- if (write && next != buff->segment_id) {
- TRACE(5, "segments out of order, aborting write");
- runner_status = do_abort;
- fdc_mode = fdc_idle;
- } else {
- setup_new_segment(&buffer[head], next, 0);
- if (stop_read_ahead) {
- buff->next_segment = 0;
- stop_read_ahead = 0;
- }
- if (calc_next_cluster(&buffer[head]) == 0 ||
- setup_fdc_and_dma(&buffer[head], fdc_op) != 0) {
- TRACEx1(1, "couldn't start %s-ahead", (write) ? "write" : "read");
- runner_status = do_abort;
- fdc_mode = fdc_idle;
- } else {
- buff->status = (write) ? writing : reading; /* keep on going */
- }
- }
- } else {
- TRACEx1(5, "all input buffers %s, pausing tape",
- (write) ? "empty" : "full");
- pause_tape(last_segment, 0, fdc_mode);
- runner_status = idle; /* not quite true until next irq */
- }
- } else {
- /* don't continue with next segment
- */
- TRACEx1(5, "no %s allowed, stopping tape",
- (write) ? "write next" : "read ahead");
- if (random_rw) {
- stop_tape(last_segment);
- } else {
- pause_tape(last_segment, 0, fdc_mode);
- }
- runner_status = idle; /* not quite true until next irq */
- }
- }
- TRACE_EXIT;
- return;
-}
-
-static void
-retry_sector(buffer_struct ** p_buff, error_cause cause, int fdc_mode,
- unsigned skip)
-{
- TRACE_FUN(8, "retry_sector");
- buffer_struct *buff = *p_buff;
-
- TRACEx1(4, "%s error, will retry",
- (fdc_mode == fdc_writing_data) ? "write" : "read");
- pause_tape(buff->segment_id, 1, fdc_mode);
- runner_status = aborting;
- buff->status = error;
- buff->skip = skip;
- TRACE_EXIT;
-}
-
-static unsigned
-find_resume_point(buffer_struct * buff)
-{
- TRACE_FUN(8, "find_resume_point");
- int i = 0;
- unsigned long mask;
- unsigned long map;
-
- /* This function is to be called after all variables have been
- * updated to point past the failing sector.
- * If there are any soft errors before the failing sector,
- * find the first soft error and return the sector offset.
- * Otherwise find the last hard error.
- * Note: there should always be at least one hard or soft error !
- */
- if (buff->sector_offset < 1 || buff->sector_offset > 32) {
- TRACEx1(1, "bug: sector_offset = %d", buff->sector_offset);
- } else {
- if (buff->sector_offset >= 32) { /* C-limitation on shift ! */
- mask = 0xffffffff;
- } else {
- mask = (1 << buff->sector_offset) - 1;
- }
- map = buff->soft_error_map & mask;
- if (map) {
- while ((map & (1 << i)) == 0) {
- ++i;
- }
- TRACEx1(4, "at sector %d", SECTOR(i));
- } else {
- map = buff->hard_error_map & mask;
- i = buff->sector_offset - 1;
- if (map) {
- while ((map & (1 << i)) == 0) {
- --i;
- }
- TRACEx1(4, "after sector %d", SECTOR(i));
- ++i; /* first sector after last hard error */
- } else {
- TRACE(1, "bug: no soft or hard errors");
- }
- }
- }
- TRACE_EXIT;
- return i;
-}
-
-/* FDC interrupt service routine.
- */
-void
-fdc_isr(void)
-{
- TRACE_FUN(8, "fdc_isr");
- int result;
- int status;
- error_cause cause = no_error;
- byte in[7];
- static int isr_active = 0;
- int t0;
- buffer_struct *buff = &buffer[head];
- int skip;
-
- t0 = timestamp();
- if (isr_active) {
- TRACE(-1, "nested interrupt, not good !");
- *fdc.hook = fdc_isr; /* hook our handler into the fdc code again */
- TRACE_EXIT;
- return;
- }
- ++isr_active;
- sti(); /* enables interrupts again */
- status = inb_p(fdc.msr);
- if (status & FDC_BUSY) { /* Entering Result Phase */
- hide_interrupt = 0;
- result = fdc_result(in, 7); /* better get it fast ! */
- if (result < 0) {
- /* Entered unknown state...
- */
- TRACE(1, "probably fatal error during FDC Result Phase");
- TRACE(1, "drive may hang until (power) reset :-(");
- /* what to do next ????
- */
- } else {
- int i;
- char *fdc_mode_txt;
-
- decode_irq_cause(fdc_mode, in, &fdc_mode_txt, &cause);
- for (i = 0; i < NR_BUFFERS; ++i) {
- TRACEx3(8, "buffer[%d] status: %d, segment_id: %d",
- i, buffer[i].status, buffer[i].segment_id);
- }
- switch (fdc_mode) {
-
- case fdc_reading_data:{
-
- if (cause == no_error) {
- TRACEi(5, "reading segment", buff->segment_id);
- } else {
- TRACEi(4, "error reading segment", buff->segment_id);
- }
- if (runner_status == aborting || runner_status == do_abort) {
- TRACEx1(4, "aborting %s", fdc_mode_txt);
- break;
- }
- if (buff->retry > 0) {
- TRACEx1(5, "this is retry nr %d", buff->retry);
- }
- if (buff->bad_sector_map == FAKE_SEGMENT) {
- /* This condition occurs when reading a `fake' sector that's
- * not accessible. Doesn't really matter as we would have
- * ignored it anyway !
- * Chance is that we're past the next segment now, so the
- * next operation may fail and result in a retry.
- */
- TRACE(4, "skipping empty segment (read)");
- buff->remaining = 0; /* skip failing sector */
- continue_xfer(&buff, no_error, fdc_mode, 1); /* fake success */
- } else {
- switch (cause) {
- case no_error:{
- determine_progress(buff, cause, fdc_reading_data);
- if (in[2] & 0x40) {
- /* Handle deleted data in header segments.
- * Skip segment and force read-ahead.
- */
- TRACEx1(2, "deleted data in sector %d",
- SECTOR(buff->sector_offset - 1));
- buff->deleted = 1;
- buff->remaining = 0; /* abort transfer */
- buff->soft_error_map |= (-1L << buff->sector_offset);
- if (buff->segment_id == 0) {
- stop_read_ahead = 1; /* stop on next segment */
- }
- buff->next_segment = buff->segment_id + 1; /* force read-ahead */
- skip = (SECTORS_PER_SEGMENT - buff->sector_offset);
- } else {
- skip = 0;
- }
- continue_xfer(&buff, cause, fdc_mode, skip);
- break;
- }
- case no_data_error:
- /* Tape started too far ahead of or behind the right sector.
- * This may also happen in the middle of a segment !
- * Handle no-data as soft error. If next sector fails too,
- * a retry (with needed reposition) will follow.
- */
- case id_am_error:
- case id_crc_error:
- case data_am_error:
- case data_crc_error:
- case overrun_error:{
- int first_error = (buff->soft_error_map == 0 &&
- buff->hard_error_map == 0);
-
- update_history(cause);
- determine_progress(buff, cause, fdc_reading_data);
- if (first_error) {
- skip = buff->sector_offset;
- } else {
- skip = find_resume_point(buff);
- }
- /* Try to resume with next sector on single errors (let ecc
- * correct it), but retry on no_data (we'll be past the
- * target when we get here so we cannot retry) or on multiple
- * errors (reduce chance on ecc failure).
- */
- if (first_error && cause != no_data_error) {
- continue_xfer(&buff, cause, fdc_mode, skip);
- } else {
- retry_sector(&buff, cause, fdc_mode, skip);
- }
- break;
- }
- default:{
- /* Don't know why this could happen but find out.
- */
- TRACE(1, "unexpected error");
- determine_progress(buff, cause, fdc_reading_data);
- retry_sector(&buff, cause, fdc_mode, 0);
- break;
- }
- }
- }
- break;
- }
-
- case fdc_reading_id:{
-
- if (cause == no_error) {
- fdc_cyl = in[3];
- fdc_head = in[4];
- fdc_sect = in[5];
- TRACEx3(6, "id read: C: 0x%02x, H: 0x%02x, R: 0x%02x",
- fdc_cyl, fdc_head, fdc_sect);
- } else { /* no valid information, use invalid sector */
- fdc_cyl =
- fdc_head =
- fdc_sect = 0;
- TRACE(5, "Didn't find valid sector Id");
- }
- fdc_mode = fdc_idle;
- break;
- }
-
- case fdc_writing_data:{
-
- if (cause == no_error) {
- TRACEi(5, "writing segment", buff->segment_id);
- } else {
- TRACEi(4, "error writing segment", buff->segment_id);
- }
- if (runner_status == aborting || runner_status == do_abort) {
- TRACEx1(5, "aborting %s", fdc_mode_txt);
- break;
- }
- if (buff->retry > 0) {
- TRACEx1(5, "this is retry nr %d", buff->retry);
- }
- if (buff->bad_sector_map == FAKE_SEGMENT) {
- /* This condition occurs when trying to write to a `fake'
- * sector that's not accessible. Doesn't really matter as
- * it isn't used anyway ! Might be located at wrong segment,
- * then we'll fail on the next segment.
- */
- TRACE(4, "skipping empty segment (write)");
- buff->remaining = 0; /* skip failing sector */
- continue_xfer(&buff, no_error, fdc_mode, 1); /* fake success */
- } else {
- switch (cause) {
- case no_error:{
- determine_progress(buff, cause, fdc_writing_data);
- continue_xfer(&buff, cause, fdc_mode, 0);
- break;
- }
- case no_data_error:
- case id_am_error:
- case id_crc_error:
- case data_am_error:
- case overrun_error:{
- update_history(cause);
- determine_progress(buff, cause, fdc_writing_data);
- skip = find_resume_point(buff);
- retry_sector(&buff, cause, fdc_mode, skip);
- break;
- }
- default:{
- if (in[1] & 0x02) {
- TRACE(1, "media not writable");
- } else {
- TRACE(-1, "unforeseen write error");
- }
- fdc_mode = fdc_idle;
- break;
- }
- }
- }
- break;
- }
- default:
-
- TRACEx1(1, "Warning: unexpected irq during: %s",
- fdc_mode_txt);
- fdc_mode = fdc_idle;
- break;
- }
- }
- if (runner_status == do_abort) {
- /* cease operation, remember tape position
- */
- TRACE(5, "runner aborting");
- runner_status = aborting;
- ++expected_stray_interrupts;
- }
- } else { /* !FDC_BUSY */
- /* clear interrupt, cause should be gotten by issuing
- * a Sense Interrupt Status command.
- */
- if (fdc_mode == fdc_recalibrating || fdc_mode == fdc_seeking) {
- if (hide_interrupt) {
- int st0;
- int pcn;
-
- result = fdc_sense_interrupt_status(&st0, &pcn);
- current_cylinder = pcn;
- TRACE(5, "handled hidden interrupt");
- }
- seek_completed = 1;
- fdc_mode = fdc_idle;
- } else if (!waitqueue_active(&wait_intr)) {
- if (expected_stray_interrupts == 0) {
- TRACE(2, "unexpected stray interrupt");
- } else {
- TRACE(5, "expected stray interrupt");
- --expected_stray_interrupts;
- }
- } else {
- if (fdc_mode == fdc_reading_data || fdc_mode == fdc_writing_data ||
- fdc_mode == fdc_reading_id) {
- byte status = inb_p(fdc.msr);
- if (status & FDC_BUSY) {
- TRACE(-1, "***** FDC failure, busy too late");
- } else {
- TRACE(-1, "***** FDC failure, no busy");
- }
- } else {
- TRACE(6, "awaited stray interrupt");
- }
- }
- hide_interrupt = 0;
- }
- /* Handle sleep code.
- */
- if (!hide_interrupt) {
- ++interrupt_seen;
- if (wait_intr) {
- wake_up_interruptible(&wait_intr);
- }
- } else {
- TRACEx1(5, "hiding interrupt while %s", wait_intr ? "waiting" : "active");
- }
- t0 = timediff(t0, timestamp());
- if (t0 >= 1000) { /* only tell us about long calls */
- TRACEx1(7, "isr() duration: %5d usec", t0);
- }
- *fdc.hook = fdc_isr; /* hook our handler into the fdc code again */
- TRACE_EXIT;
- --isr_active;
-}
diff --git a/drivers/char/ftape/ftape-bsm.c b/drivers/char/ftape/ftape-bsm.c
deleted file mode 100644
index 6e86b065e..000000000
--- a/drivers/char/ftape/ftape-bsm.c
+++ /dev/null
@@ -1,428 +0,0 @@
-/*
- * Copyright (C) 1994-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING. If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-bsm.c,v $
- $Author: bas $
- *
- $Revision: 1.7 $
- $Date: 1995/04/30 13:15:14 $
- $State: Beta $
- *
- * This file contains the bad-sector map handling code for
- * the QIC-117 floppy tape driver for Linux.
- * QIC-40, QIC-80, QIC-3010 and QIC-3020 maps are implemented.
- */
-
-#include <linux/ftape.h>
-#include <linux/string.h>
-
-#include "tracing.h"
-#include "ftape-bsm.h"
-#include "ftape-ctl.h"
-#include "ftape-rw.h"
-
-
-/* Global vars.
- */
-int bad_sector_map_changed = 0;
-
-/* Local vars.
- */
-static byte bad_sector_map[BAD_SECTOR_MAP_SIZE];
-typedef enum {
- forward, backward
-} mode_type;
-
-#if 0
-/* fix_tape converts a normal QIC-80 tape into a 'wide' tape.
- * For testing purposes only !
- */
-void fix_tape(byte * buffer)
-{
- static byte list[BAD_SECTOR_MAP_SIZE];
- unsigned long *src_ptr = (unsigned long *) list;
- byte *dst_ptr = bad_sector_map;
- unsigned long map;
- unsigned sector = 1;
- int i;
-
- memcpy(list, bad_sector_map, sizeof(list));
- memset(bad_sector_map, 0, sizeof(bad_sector_map));
- while ((byte *) src_ptr - list < sizeof(list)) {
- map = *src_ptr++;
- if (map == EMPTY_SEGMENT) {
- *(unsigned long *) dst_ptr = 0x800000 + sector;
- dst_ptr += 3;
- sector += SECTORS_PER_SEGMENT;
- } else {
- for (i = 0; i < SECTORS_PER_SEGMENT; ++i) {
- if (map & 1) {
- *(unsigned long *) dst_ptr = sector;
- dst_ptr += 3;
- }
- map >>= 1;
- ++sector;
- }
- }
- }
- bad_sector_map_changed = 1;
- *(buffer + 4) = 4; /* put new format code */
- format_code = 4;
-}
-
-#endif
-
-byte *
- find_end_of_bsm_list(byte * ptr, byte * limit)
-{
- while (ptr + 2 < limit) {
- if (ptr[0] || ptr[1] || ptr[2]) {
- ptr += 3;
- } else {
- return ptr;
- }
- }
- return NULL;
-}
-
-void store_bad_sector_map(byte * buffer)
-{
- TRACE_FUN(8, "store_bad_sector_map");
- size_t count;
- size_t offset;
-
- /* Store the bad sector map in buffer.
- */
- if (format_code == 4) {
- offset = 256;
- count = sizeof(bad_sector_map);
- } else {
- offset = 2 * SECTOR_SIZE; /* skip failed sector log */
- count = sizeof(bad_sector_map) - (offset - 256);
- }
- memcpy(buffer + offset, bad_sector_map, count);
- TRACE_EXIT;
-}
-
-void put_sector(byte ** ptr, unsigned long sector)
-{
- *(*ptr)++ = sector & 0xff;
- sector >>= 8;
- *(*ptr)++ = sector & 0xff;
- sector >>= 8;
- *(*ptr)++ = sector & 0xff;
-}
-
-unsigned long get_sector(byte ** ptr, mode_type mode)
-{
- unsigned long sector;
-
- if (mode == forward) {
- sector = *(*ptr)++;
- sector += *(*ptr)++ << 8;
- sector += *(*ptr)++ << 16;
- } else {
- sector = *--(*ptr) << 16;
- sector += *--(*ptr) << 8;
- sector += *--(*ptr);
- }
- return sector;
-}
-
-void extract_bad_sector_map(byte * buffer)
-{
- TRACE_FUN(8, "extract_bad_sector_map");
-
- /* Fill the bad sector map with the contents of buffer.
- */
- if (format_code == 4) {
- /* QIC-3010/3020 and wide QIC-80 tapes no longer have a failed
- * sector log but use this area to extend the bad sector map.
- */
- memcpy(bad_sector_map, buffer + 256, sizeof(bad_sector_map));
- } else {
- /* non-wide QIC-80 tapes have a failed sector log area that
- * mustn't be included in the bad sector map.
- */
- memcpy(bad_sector_map, buffer + 256 + FAILED_SECTOR_LOG_SIZE,
- sizeof(bad_sector_map) - FAILED_SECTOR_LOG_SIZE);
- }
-#if 0
- /* for testing of bad sector handling at end of tape
- */
- ((unsigned long *) bad_sector_map)[segments_per_track * tracks_per_tape - 3] = 0x000003e0;
- ((unsigned long *) bad_sector_map)[segments_per_track * tracks_per_tape - 2] = 0xff3fffff;
- ((unsigned long *) bad_sector_map)[segments_per_track * tracks_per_tape - 1] = 0xffffe000;
-#endif
-#if 0
- /* Enable to test bad sector handling
- */
- ((unsigned long *) bad_sector_map)[30] = 0xfffffffe;
- ((unsigned long *) bad_sector_map)[32] = 0x7fffffff;
- ((unsigned long *) bad_sector_map)[34] = 0xfffeffff;
- ((unsigned long *) bad_sector_map)[36] = 0x55555555;
- ((unsigned long *) bad_sector_map)[38] = 0xffffffff;
- ((unsigned long *) bad_sector_map)[50] = 0xffff0000;
- ((unsigned long *) bad_sector_map)[51] = 0xffffffff;
- ((unsigned long *) bad_sector_map)[52] = 0xffffffff;
- ((unsigned long *) bad_sector_map)[53] = 0x0000ffff;
-#endif
-#if 0
- /* Enable when testing multiple volume tar dumps.
- */
- for (i = first_data_segment; i <= ftape_last_segment.id - 7; ++i) {
- ((unsigned long *) bad_sector_map)[i] = EMPTY_SEGMENT;
- }
-#endif
-#if 0
- /* Enable when testing bit positions in *_error_map
- */
- for (i = first_data_segment; i <= ftape_last_segment.id; ++i) {
- ((unsigned long *) bad_sector_map)[i] |= 0x00ff00ff;
- }
-#endif
- if (tracing > 2) {
- unsigned int map;
- int good_sectors = 0;
- int bad_sectors;
- unsigned int total_bad = 0;
- int i;
-
- if (format_code == 4 || format_code == 3) {
- byte *ptr = bad_sector_map;
- unsigned sector;
-
- do {
- sector = get_sector(&ptr, forward);
- if (sector != 0) {
- if (format_code == 4 && sector & 0x800000) {
- total_bad += SECTORS_PER_SEGMENT - 3;
- TRACEx1(6, "bad segment at sector: %6d", sector & 0x7fffff);
- } else {
- ++total_bad;
- TRACEx1(6, "bad sector: %6d", sector);
- }
- }
- } while (sector != 0);
- /* Display end-of-file marks
- */
- do {
- sector = *((unsigned short *) ptr)++;
- if (sector) {
- TRACEx2(4, "eof mark: %4d/%2d", sector,
- *((unsigned short *) ptr)++);
- }
- } while (sector);
- } else {
- for (i = first_data_segment;
- i < segments_per_track * tracks_per_tape; ++i) {
- map = ((unsigned long *) bad_sector_map)[i];
- bad_sectors = count_ones(map);
- if (bad_sectors > 0) {
- TRACEx2(6, "bsm for segment %4d: 0x%08x", i, map);
- if (bad_sectors > SECTORS_PER_SEGMENT - 3) {
- bad_sectors = SECTORS_PER_SEGMENT - 3;
- }
- total_bad += bad_sectors;
- }
- }
- }
- good_sectors = ((segments_per_track * tracks_per_tape - first_data_segment)
- * (SECTORS_PER_SEGMENT - 3)) - total_bad;
- TRACEx1(3, "%d Kb usable on this tape",
- good_sectors - ftape_last_segment.free);
- if (total_bad == 0) {
- TRACE(1, "WARNING: this tape has no bad blocks registered !");
- } else {
- TRACEx1(2, "%d bad sectors", total_bad);
- }
- }
- TRACE_EXIT;
-}
-
-unsigned long cvt2map(int sector)
-{
- return 1 << (((sector & 0x7fffff) - 1) % SECTORS_PER_SEGMENT);
-}
-
-int cvt2segment(int sector)
-{
- return ((sector & 0x7fffff) - 1) / SECTORS_PER_SEGMENT;
-}
-
-int forward_seek_entry(int segment_id, byte ** ptr, unsigned long *map)
-{
- byte *tmp_ptr;
- unsigned long sector;
- int segment;
- int count;
-
- do {
- sector = get_sector(ptr, forward);
- segment = cvt2segment(sector);
- } while (sector != 0 && segment < segment_id);
- tmp_ptr = *ptr - 3; /* point to first sector >= segment_id */
- /* Get all sectors in segment_id
- */
- if (format_code == 4 && (sector & 0x800000) && segment == segment_id) {
- *map = EMPTY_SEGMENT;
- count = 32;
- } else {
- *map = 0;
- count = 0;
- while (sector != 0 && segment == segment_id) {
- *map |= cvt2map(sector);
- sector = get_sector(ptr, forward);
- segment = cvt2segment(sector);
- ++count;
- }
- }
- *ptr = tmp_ptr;
- return count;
-}
-
-int backwards_seek_entry(int segment_id, byte ** ptr, unsigned long *map)
-{
- unsigned long sector;
- int segment;
- int count;
-
- *map = 0;
- if (*ptr > bad_sector_map) {
- do {
- sector = get_sector(ptr, backward);
- segment = cvt2segment(sector);
- } while (*ptr > bad_sector_map && segment > segment_id);
- count = 0;
- if (segment > segment_id) {
- /* at start of list, no entry found */
- } else if (segment < segment_id) {
- /* before smaller entry, adjust for overshoot */
- *ptr += 3;
- } else {
- /* get all sectors in segment_id */
- if (format_code == 4 && (sector & 0x800000)) {
- *map = EMPTY_SEGMENT;
- count = 32;
- } else {
- do {
- *map |= cvt2map(sector);
- ++count;
- if (*ptr <= bad_sector_map) {
- break;
- }
- sector = get_sector(ptr, backward);
- segment = cvt2segment(sector);
- } while (segment == segment_id);
- if (segment < segment_id) {
- *ptr += 3;
- }
- }
- }
- } else {
- count = 0;
- }
- return count;
-}
-
-void put_bad_sector_entry(int segment_id, unsigned long new_map)
-{
- byte *ptr = bad_sector_map;
- int count;
- int new_count;
- unsigned long map;
-
- if (format_code == 3 || format_code == 4) {
- count = forward_seek_entry(segment_id, &ptr, &map);
- new_count = count_ones(new_map);
- /* If format code == 4 put empty segment instead of 32 bad sectors.
- */
- if (new_count == 32 && format_code == 4) {
- new_count = 1;
- }
- if (count != new_count) {
- /* insert (or delete if < 0) new_count - count entries.
- * Move trailing part of list including terminating 0.
- */
- byte *hi_ptr = ptr;
-
- do {
- } while (get_sector(&hi_ptr, forward) != 0);
- memmove(ptr + new_count, ptr + count, hi_ptr - (ptr + count));
- }
- if (new_count == 1 && new_map == EMPTY_SEGMENT) {
- put_sector(&ptr, 0x800001 + segment_id * SECTORS_PER_SEGMENT);
- } else {
- int i = 0;
-
- while (new_map) {
- if (new_map & 1) {
- put_sector(&ptr, 1 + segment_id * SECTORS_PER_SEGMENT + i);
- }
- ++i;
- new_map >>= 1;
- }
- }
- } else {
- ((unsigned long *) bad_sector_map)[segment_id] = new_map;
- }
- bad_sector_map_changed = 1;
-}
-
-unsigned long get_bad_sector_entry(int segment_id)
-{
- TRACE_FUN(8, "get_bad_sector_entry");
- static unsigned long map = 0;
-
- if (used_header_segment == -1) {
- /* When reading header segment we'll need a blank map.
- */
- map = 0;
- } else if (format_code == 3 || format_code == 4) {
- /* Invariants:
- * map - mask value returned on last call.
- * ptr - points to first sector greater or equal to
- * first sector in last_referenced segment.
- * last_referenced - segment id used in the last call,
- * sector and map belong to this id.
- * This code is designed for sequential access and retries.
- * For true random access it may have to be redesigned.
- */
- static int last_reference = -1;
- static byte *ptr = bad_sector_map;
-
- if (segment_id > last_reference) {
- /* Skip all sectors before segment_id
- */
- forward_seek_entry(segment_id, &ptr, &map);
- } else if (segment_id < last_reference) {
- /* Skip backwards until begin of buffer or first sector in segment_id
- */
- backwards_seek_entry(segment_id, &ptr, &map);
- } /* segment_id == last_reference : keep map */
- last_reference = segment_id;
- } else {
- map = ((unsigned long *) bad_sector_map)[segment_id];
- }
- TRACE_EXIT;
- return map;
-}
-
-void ftape_init_bsm(void)
-{
- memset(bad_sector_map, 0, sizeof(bad_sector_map));
-}
diff --git a/drivers/char/ftape/ftape-ctl.c b/drivers/char/ftape/ftape-ctl.c
deleted file mode 100644
index e8c3ae1a1..000000000
--- a/drivers/char/ftape/ftape-ctl.c
+++ /dev/null
@@ -1,883 +0,0 @@
-/*
- * Copyright (C) 1993-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING. If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- *
- * This file contains the non-read/write ftape functions
- * for the QIC-40/80 floppy-tape driver for Linux.
- */
-
-#include <linux/errno.h>
-#include <linux/mm.h>
-#include <linux/ftape.h>
-#include <asm/uaccess.h>
-
-#include "tracing.h"
-#include "ftape-eof.h"
-#include "ftape-io.h"
-#include "ftape-ctl.h"
-#include "ftape-write.h"
-#include "ftape-read.h"
-#include "ftape-rw.h"
-#include "qic117.h"
-#include "ftape-bsm.h"
-
-
-/* Global vars.
- */
-int segments_per_track = 102;
-int segments_per_head = 1020;
-int segments_per_cylinder = 4;
-int tracks_per_tape = 20;
-int ftape_failure = 1;
-int ftape_seg_pos = 0;
-int first_data_segment = -1;
-int ftape_state = idle; /* use buffer_state_enum */
-history_record history;
-int write_protected;
-int ftape_offline = 0;
-int no_tape = 1;
-int formatted = 0;
-int ftape_data_rate = 0;
-int going_offline = 0;
-int read_only = 0;
-
-/* Local vars.
- */
-static int ftape_last_error = 0;
-static const vendor_struct vendors[] = QIC117_VENDORS;
-static const wakeup_method methods[] = WAKEUP_METHODS;
-static int init_drive_needed = 1;
-
-
-static int ftape_not_operational(int status)
-{
- /* return true if status indicates tape can not be used.
- */
- return ((status ^ QIC_STATUS_CARTRIDGE_PRESENT) &
- (QIC_STATUS_ERROR |
- QIC_STATUS_CARTRIDGE_PRESENT |
- QIC_STATUS_NEW_CARTRIDGE));
-}
-
-int ftape_seek_to_eot(void)
-{
- TRACE_FUN(8, "ftape_seek_to_eot");
- int result;
- int status;
-
- result = ftape_ready_wait(timeout.pause, &status);
- while ((status & QIC_STATUS_AT_EOT) == 0) {
- if (result < 0) {
- TRACE(1, "failed");
- TRACE_EXIT;
- return result;
- }
- if (ftape_not_operational(status)) {
- TRACE_EXIT;
- return -EIO;
- }
- result = ftape_command_wait(QIC_PHYSICAL_FORWARD,
- timeout.rewind, &status);
- }
- TRACE_EXIT;
- return 0;
-}
-
-int ftape_seek_to_bot(void)
-{
- TRACE_FUN(8, "ftape_seek_to_bot");
- int result;
- int status;
-
- result = ftape_ready_wait(timeout.pause, &status);
- while ((status & QIC_STATUS_AT_BOT) == 0) {
- if (result < 0) {
- TRACE(1, "failed");
- TRACE_EXIT;
- return result;
- }
- if (ftape_not_operational(status)) {
- TRACE_EXIT;
- return -EIO;
- }
- result = ftape_command_wait(QIC_PHYSICAL_REVERSE,
- timeout.rewind, &status);
- }
- TRACE_EXIT;
- return 0;
-}
-
-void ftape_reset_position(void)
-{
- ftape_seg_pos = first_data_segment;
- reset_eof_list();
-}
-
-int ftape_new_cartridge(void)
-{
- location.track = -1; /* force seek on first access */
- first_data_segment = -1; /* unknown */
- ftape_zap_read_buffers();
- ftape_zap_write_buffers();
- ftape_reset_position();
- return 0;
-}
-
-int ftape_abort_operation(void)
-{
- TRACE_FUN(5, "ftape_abort_operation");
- int result = 0;
- int i;
- int status;
-
- if (runner_status == running) {
- TRACE(5, "aborting runner, waiting");
- runner_status = do_abort;
- /* set timeout so that the tape will run to logical EOT
- * if we missed the last sector and there are no queue pulses.
- */
- result = ftape_dumb_stop();
- if (result == 0) {
- runner_status = idle;
- }
- }
- if (runner_status != idle) {
- if (runner_status == do_abort) {
- TRACE(5, "forcing runner abort");
- }
- TRACE(5, "stopping tape");
- result = ftape_command_wait(QIC_STOP_TAPE, timeout.stop, &status);
- location.known = 0;
- runner_status = idle;
- }
- for (i = 0; i < NR_BUFFERS; ++i) {
- buffer[i].status = waiting;
- }
- head = tail = 0;
- TRACE_EXIT;
- return result;
-}
-
-int lookup_vendor_id(int vendor_id)
-{
- int i = 0;
-
- while (vendors[i].vendor_id != vendor_id) {
- if (++i >= NR_ITEMS(vendors)) {
- return -1;
- }
- }
- return i;
-}
-
-void ftape_detach_drive(void)
-{
- TRACE_FUN(8, "ftape_detach_drive");
-
- TRACE(5, "disabling tape drive and fdc");
- ftape_put_drive_to_sleep(drive_type);
- fdc_catch_stray_interrupts(1); /* one always comes */
- fdc_disable();
- fdc_release_irq_and_dma();
- TRACE_EXIT;
-}
-
-static void clear_history(void)
-{
- history.used = 0;
- history.id_am_errors =
- history.id_crc_errors =
- history.data_am_errors =
- history.data_crc_errors =
- history.overrun_errors =
- history.no_data_errors =
- history.retries =
- history.crc_errors =
- history.crc_failures =
- history.ecc_failures =
- history.corrected =
- history.defects =
- history.rewinds = 0;
-}
-
-int ftape_activate_drive(vendor_struct * drive_type)
-{
- TRACE_FUN(5, "ftape_activate_drive");
- int result = 0;
-
- /* If we already know the drive type, wake it up.
- * Else try to find out what kind of drive is attached.
- */
- if (drive_type->wake_up != unknown_wake_up) {
- TRACE(5, "enabling tape drive and fdc");
- result = ftape_wakeup_drive(drive_type->wake_up);
- if (result < 0) {
- TRACE(1, "known wakeup method failed");
- }
- } else {
- int old_tracing = tracing;
- wake_up_types method;
-
- /* Try to awaken the drive using all known methods.
- * Lower tracing for a while.
- */
- if (tracing <= 4) {
- tracing = 0;
- }
- for (method = no_wake_up; method < NR_ITEMS(methods); ++method) {
- drive_type->wake_up = method;
-#if 0
- /* Test setup for dual drive configuration in dodo.
- * /dev/rft2 uses mountain wakeup only -> Archive QIC-80
- * /dev/rft3 uses colorado wakeup only -> Jumbo QIC-40
- * Other systems will use the normal scheme.
- */
- if ((FTAPE_UNIT < 2) ||
- (FTAPE_UNIT == 2 && method == wake_up_mountain) ||
- (FTAPE_UNIT == 3 && method == wake_up_colorado)) {
- result = ftape_wakeup_drive(drive_type->wake_up);
- } else {
- result = -EIO;
- }
-#else
- result = ftape_wakeup_drive(drive_type->wake_up);
-#endif
- if (result >= 0) {
- int tracing = old_tracing; /* fool TRACE */
- TRACEx1(2, "drive wakeup method: %s",
- methods[drive_type->wake_up].name);
- break;
- }
- }
- tracing = old_tracing;
- if (method >= NR_ITEMS(methods)) {
- /* no response at all, cannot open this drive */
- drive_type->wake_up = unknown_wake_up;
- TRACE(1, "no tape drive found !");
- tracing = old_tracing;
- result = -ENODEV;
- }
- }
- TRACE_EXIT;
- return result;
-}
-
-int ftape_get_drive_status(int *new_tape, int *no_tape, int *wp_tape)
-{
- TRACE_FUN(5, "ftape_get_drive_status");
- int result;
- int status;
-
- *no_tape =
- *wp_tape = 0;
- /* Tape drive is activated now.
- * First clear error status if present.
- */
- do {
- result = ftape_ready_wait(timeout.reset, &status);
- if (result < 0) {
- if (result == -ETIME) {
- TRACE(1, "ftape_ready_wait timeout");
- } else if (result == -EINTR) {
- TRACE(1, "ftape_ready_wait aborted");
- } else {
- TRACE(1, "ftape_ready_wait failed");
- }
- result = -EIO;
- break;
- }
- /* Clear error condition (drive is ready !)
- */
- if (status & QIC_STATUS_ERROR) {
- int error;
- int command;
-
- TRACE(1, "error status set");
- result = ftape_report_error(&error, &command, 1);
- if (result < 0) {
- TRACEi(1, "report_error_code failed:", result);
- ftape_reset_drive(); /* hope it's working next time */
- init_drive_needed = 1;
- result = -EIO;
- break;
- } else if (error != 0) {
- TRACEi(4, "error code :", error);
- TRACEi(4, "error command:", command);
- }
- }
- if (status & QIC_STATUS_NEW_CARTRIDGE) {
- int error;
- int command;
- int old_tracing = tracing;
-
- /* Undocumented feature: Must clear (not present!) error
- * here or we'll fail later.
- */
- tracing = 0;
- ftape_report_error(&error, &command, 1);
- tracing = old_tracing;
- TRACE(3, "status: new cartridge");
- *new_tape = 1;
- }
- } while (status & QIC_STATUS_ERROR);
-
- *no_tape = !(status & QIC_STATUS_CARTRIDGE_PRESENT);
- *wp_tape = (status & QIC_STATUS_WRITE_PROTECT);
- if (*no_tape) {
- TRACE(1, "no cartridge present");
- } else {
- if (*wp_tape) {
- TRACE(2, "Write protected cartridge");
- }
- }
- TRACE_EXIT;
- return result;
-}
-
-void ftape_log_vendor_id(void)
-{
- TRACE_FUN(5, "ftape_log_vendor_id");
- int vendor_index;
-
- ftape_report_vendor_id(&drive_type.vendor_id);
- vendor_index = lookup_vendor_id(drive_type.vendor_id);
- if (drive_type.vendor_id == UNKNOWN_VENDOR &&
- drive_type.wake_up == wake_up_colorado) {
- vendor_index = 0;
- drive_type.vendor_id = 0; /* hack to get rid of all this mail */
- }
- if (vendor_index < 0) {
- /* Unknown vendor id, first time opening device.
- * The drive_type remains set to type found at wakeup time, this
- * will probably keep the driver operating for this new vendor.
- */
- TRACE(-1, "============ unknown vendor id ===========");
- TRACE(-1, "A new, yet unsupported tape drive is found");
- TRACE(-1, "Please report the following values:");
- TRACEx1(-1, " Vendor id : 0x%04x", drive_type.vendor_id);
- TRACEx1(-1, " Wakeup method : %s", methods[drive_type.wake_up].name);
- TRACE(-1, "And a description of your tape drive to:");
- TRACE(-1, "Kai Harrekilde-Petersen <khp@dolphinics.no>");
- TRACE(-1, "==========================================");
- drive_type.speed = 500; /* deci-ips: very safe value */
- } else {
- drive_type.name = vendors[vendor_index].name;
- drive_type.speed = vendors[vendor_index].speed;
- TRACEx1(3, "tape drive type: %s", drive_type.name);
- /* scan all methods for this vendor_id in table */
- while (drive_type.wake_up != vendors[vendor_index].wake_up) {
- if (vendor_index < NR_ITEMS(vendors) - 1 &&
- vendors[vendor_index + 1].vendor_id == drive_type.vendor_id) {
- ++vendor_index;
- } else {
- break;
- }
- }
- if (drive_type.wake_up != vendors[vendor_index].wake_up) {
- TRACE(-1, "==========================================");
- TRACE(-1, "wakeup type mismatch:");
- TRACEx2(-1, "found: %s, expected: %s",
- methods[drive_type.wake_up].name,
- methods[vendors[vendor_index].wake_up].name);
- TRACE(-1, "please report this to <khp@dolphinics.no>");
- TRACE(-1, "==========================================");
- }
- }
- TRACE_EXIT;
-}
-
-void ftape_calc_timeouts(void)
-{
- TRACE_FUN(8, "ftape_calc_timeouts");
- int speed; /* deci-ips ! */
- int length;
-
- /* tape transport speed
- * data rate: QIC-40 QIC-80 QIC-3010 QIC-3020
- *
- * 250 Kbps 25 ips n/a n/a n/a
- * 500 Kbps 50 ips 34 ips 22.6 ips n/a
- * 1 Mbps n/a 68 ips 45.2 ips 22.6 ips
- * 2 Mbps n/a n/a n/a 45.2 ips
- *
- * fast tape transport speed is at least 68 ips.
- */
- switch (qic_std) {
- case QIC_TAPE_QIC40:
- speed = (ftape_data_rate == 3) ? 250 : 500;
- break;
- case QIC_TAPE_QIC80:
- speed = (ftape_data_rate == 2) ? 340 : 680;
- break;
- case QIC_TAPE_QIC3010:
- speed = (ftape_data_rate == 2) ? 226 : 452;
- break;
- case QIC_TAPE_QIC3020:
- speed = (ftape_data_rate == 1) ? 226 : 452;
- break;
- default:
- TRACE(-1, "Unknown qic_std (bug) ?");
- speed = 500;
- break;
- }
- if (tape_len <= 0) {
- /* Handle unknown length tapes as 1100 ft ones (worst case)
- */
- TRACE(1, "Unknown tape length, using worst case timing values!");
- length = 1100;
- } else {
- length = tape_len;
- }
- if (drive_type.speed == 0) {
- unsigned long t0;
- int dt;
-
- ftape_seek_to_bot();
- t0 = jiffies;
- ftape_seek_to_eot();
- ftape_seek_to_bot();
- dt = (int) ((jiffies - t0) * MSPT);
- drive_type.speed = (2 * 12 * length * 1000) / dt;
- TRACE(-1, "==========================================");
- TRACEx1(-1, "drive : %s", drive_type.name);
- TRACEx2(-1, "delta time = %d, length = %d", dt, length);
- TRACEx1(-1, "has max tape speed of %d ips", drive_type.speed);
- TRACE(-1, "please report this to <khp@dolphinics.no>");
- TRACE(-1, "==========================================");
- }
- /* time to go from bot to eot at normal speed (data rate):
- * time = (1+delta) * length (ft) * 12 (inch/ft) / speed (ips)
- * delta = 10 % for seek speed, 20 % for rewind speed.
- */
- timeout.seek = (length * 132 * SECOND) / speed;
- timeout.rewind = (length * 144 * SECOND) / (10 * drive_type.speed);
- timeout.reset = 20 * SECOND + timeout.rewind;
- TRACEx2(4, "speed = %d, length = %d", speed, length);
- TRACEx1(4, "seek timeout: %d sec", (timeout.seek + 500) / 1000);
- TRACEx1(4, "rewind timeout: %d sec", (timeout.rewind + 500) / 1000);
- TRACE_EXIT;
-}
-
-int ftape_init_drive(int *formatted)
-{
- TRACE_FUN(5, "ftape_init_drive");
- int result = 0;
- int status;
-
- result = ftape_report_raw_drive_status(&status);
- if (result >= 0 && (status & QIC_STATUS_CARTRIDGE_PRESENT)) {
- if (!(status & QIC_STATUS_AT_BOT)) {
- /* Antique drives will get here after a soft reset,
- * modern ones only if the driver is loaded when the
- * tape wasn't rewound properly.
- */
- ftape_seek_to_bot();
- }
- if (!(status & QIC_STATUS_REFERENCED)) {
- TRACE(5, "starting seek_load_point");
- result = ftape_command_wait(QIC_SEEK_LOAD_POINT,
- timeout.reset, &status);
- if (result < 0) {
- TRACE(1, "seek_load_point failed (command)");
- }
- }
- }
- if (result >= 0) {
- int rate;
-
- *formatted = (status & QIC_STATUS_REFERENCED);
- if (!*formatted) {
- TRACE(1, "Warning: tape is not formatted !");
- }
- /* Select highest rate supported by both fdc and drive.
- * Start with highest rate supported by the fdc.
- */
- if (fdc.type >= i82078_1)
- rate = 0;
- else if (fdc.type >= i82077)
- rate = 1;
- else
- rate = 2;
- do {
- result = ftape_set_data_rate(rate);
- if (result >= 0) {
- ftape_calc_timeouts();
- break;
- }
- ++rate;
- } while (rate < 4);
- if (result < 0) {
- result = -EIO;
- }
- }
- if (result >= 0) {
- /* Tape should be at bot if new cartridge ! */
- ftape_new_cartridge();
- }
- init_drive_needed = 0;
- TRACE_EXIT;
- return result;
-}
-
-/* OPEN routine called by kernel-interface code
- */
-int _ftape_open(void)
-{
- TRACE_FUN(8, "_ftape_open");
- int result;
- static int new_tape = 1;
-
- result = fdc_init();
- if (result >= 0) {
- result = ftape_activate_drive(&drive_type);
- if (result < 0) {
- fdc_disable();
- fdc_release_irq_and_dma();
-
- } else {
- result = ftape_get_drive_status(&new_tape, &no_tape, &write_protected);
- if (result < 0) {
- ftape_detach_drive();
- } else {
- if (drive_type.vendor_id == UNKNOWN_VENDOR) {
- ftape_log_vendor_id();
- }
- if (no_tape) {
- ftape_offline = 1;
- } else if (new_tape) {
- ftape_offline = 0;
- init_drive_needed = 1;
- read_only = 0; /* enable writes again */
- }
- if (!ftape_offline && init_drive_needed) {
- result = ftape_init_drive(&formatted);
- if (result >= 0) {
- new_tape = 0;
- } else {
- ftape_detach_drive();
- }
- }
- if (result >= 0) {
- clear_history();
- }
- }
- }
- }
- TRACE_EXIT;
- return result;
-}
-
-/* RELEASE routine called by kernel-interface code
- */
-int _ftape_close(void)
-{
- TRACE_FUN(8, "_ftape_close");
- int result = 0;
- int last_segment = 0;
-
- if (!ftape_offline) {
- result = ftape_flush_buffers();
- last_segment = ftape_seg_pos - 1;
- if (!(ftape_unit & FTAPE_NO_REWIND)) {
- if (result >= 0) {
- result = ftape_update_header_segments(NULL, 1);
- if (result < 0) {
- TRACE(1, "error: update of header segments failed");
- }
- } else {
- TRACE(1, "error: unable to update header segments");
- }
- }
- ftape_abort_operation();
- if (!(ftape_unit & FTAPE_NO_REWIND)) {
- if (!no_tape) {
- TRACE(5, "rewinding tape");
- result = ftape_seek_to_bot();
- }
- ftape_reset_position();
- ftape_zap_read_buffers();
- ftape_zap_write_buffers();
- }
- }
- ftape_detach_drive();
- fdc_uninit();
- if (history.used) {
- TRACE(3, "== Non-fatal errors this run: ==");
- TRACE(3, "fdc isr statistics:");
- TRACEi(3, " id_am_errors :", history.id_am_errors);
- TRACEi(3, " id_crc_errors :", history.id_crc_errors);
- TRACEi(3, " data_am_errors :", history.data_am_errors);
- TRACEi(3, " data_crc_errors :", history.data_crc_errors);
- TRACEi(3, " overrun_errors :", history.overrun_errors);
- TRACEi(3, " no_data_errors :", history.no_data_errors);
- TRACEi(3, " retries :", history.retries);
- if (history.used & 1) {
- TRACE(3, "ecc statistics:");
- TRACEi(3, " crc_errors :", history.crc_errors);
- TRACEi(3, " crc_failures :", history.crc_failures);
- TRACEi(3, " ecc_failures :", history.ecc_failures);
- TRACEi(3, " sectors corrected:", history.corrected);
- }
- TRACEx2(3, "media defects : %d%s", history.defects,
- history.defects ? " !!!" : "");
- TRACEi(3, "repositions :", history.rewinds);
- TRACEi(3, "last segment :", last_segment);
- }
- if (going_offline) {
- going_offline = 0;
- ftape_offline = 1;
- }
- TRACE_EXIT;
- return result;
-}
-
-/* IOCTL routine called by kernel-interface code
- */
-int _ftape_ioctl(unsigned int command, void *arg)
-{
- TRACE_FUN(8, "ftape_ioctl");
- int result = EINVAL;
- union {
- struct mtop mtop;
- struct mtget mtget;
- } krnl_arg;
- int arg_size = (command & IOCSIZE_MASK) >> IOCSIZE_SHIFT;
-
- /* This check will only catch arguments that are too large !
- */
- if ((command & IOC_INOUT) && arg_size > sizeof(krnl_arg)) {
- TRACEi(1, "bad argument size:", arg_size);
- TRACE_EXIT;
- return -EINVAL;
- }
- if (command & IOC_IN) {
- int error = verify_area(VERIFY_READ, arg, arg_size);
- if (error) {
- TRACE_EXIT;
- return error;
- }
- copy_from_user(&krnl_arg.mtop, arg, arg_size);
- }
- TRACEx1(5, "called with ioctl command: 0x%08x", command);
- switch (command) {
- /* cpio compatibility
- * mtrasx and mtreset are mt extension by Hennus Bergman
- * mtseek and mttell are mt extension by eddy olk
- */
- case MTIOCTOP:
- TRACEx1(5, "calling MTIOCTOP command: 0x%08x", krnl_arg.mtop.mt_op);
- switch (krnl_arg.mtop.mt_op) {
- case MTNOP:
- /* gnu mt calls MTNOP before MTIOCGET to set status */
- result = 0;
- break;
- case MTRESET:
- result = ftape_reset_drive();
- init_drive_needed = 1;
- if (result < 0 || ftape_offline) {
- break;
- }
- result = ftape_seek_to_bot();
- ftape_reset_position();
- break;
- case MTREW:
- case MTOFFL:
- if (ftape_offline) {
- result = -EIO;
- break;
- }
- ftape_flush_buffers();
- ftape_update_header_segments(NULL, 1);
- result = ftape_seek_to_bot();
- ftape_reset_position();
- if (krnl_arg.mtop.mt_op == MTOFFL) {
- going_offline = 1;
- TRACE(4, "Putting tape drive offline");
- }
- result = 0;
- break;
- case MTRETEN:
- if (ftape_offline) {
- result = -EIO;
- break;
- }
- result = ftape_seek_to_eot();
- if (result >= 0) {
- result = ftape_seek_to_bot();
- }
- ftape_reset_position();
- break;
- case MTERASE:
- if (ftape_offline) {
- result = -EIO;
- break;
- }
- result = ftape_erase();
- break;
- case MTEOM:
- if (ftape_offline) {
- result = -EIO;
- break;
- }
- result = ftape_seek_eom();
- break;
- case MTFSFM:
- if (ftape_offline) {
- result = -EIO;
- break;
- }
- eof_mark = 1; /* position ready to extend */
- case MTFSF:
- if (ftape_offline) {
- result = -EIO;
- break;
- }
- result = ftape_seek_eof(krnl_arg.mtop.mt_count);
- break;
- case MTBSFM:
- if (ftape_offline) {
- result = -EIO;
- break;
- }
- eof_mark = 1; /* position ready to extend */
- case MTBSF:
- if (ftape_offline) {
- result = -EIO;
- break;
- }
- result = ftape_seek_eof(-krnl_arg.mtop.mt_count);
- break;
- case MTFSR:
- if (ftape_offline) {
- result = -EIO;
- break;
- }
- tracing = krnl_arg.mtop.mt_count;
- TRACEx1(2, "tracing set to %d", tracing);
- result = 0;
- break;
- case MTBSR:
- if (ftape_offline) {
- result = -EIO;
- break;
- }
-#if 0
- result = ftape_fix();
-#else
- result = 0;
-#endif
- break;
- case MTWEOF:
- if (ftape_offline) {
- result = -EIO;
- break;
- }
- result = ftape_weof(krnl_arg.mtop.mt_count, ftape_seg_pos, 1);
- if (result >= 0) {
- ftape_seg_pos += krnl_arg.mtop.mt_count - 1;
- }
- break;
- /* MTRASx and MTRESET are mt extension by Hennus Bergman
- */
- case MTRAS1:
- case MTRAS2:
- case MTRAS3:
- case MTSEEK:
- case MTTELL:
- default:
- TRACEi(1, "MTIOCTOP sub-command not implemented:", krnl_arg.mtop.mt_op);
- result = -EIO;
- break;
- }
- break;
- case MTIOCGET:
- krnl_arg.mtget.mt_type = drive_type.vendor_id + 0x800000;
- krnl_arg.mtget.mt_resid = 0; /* not implemented */
- krnl_arg.mtget.mt_dsreg = 0; /* status register */
- krnl_arg.mtget.mt_gstat = /* device independent status */
- ((ftape_offline) ? 0 : GMT_ONLINE(-1L)) |
- ((write_protected) ? GMT_WR_PROT(-1L) : 0) |
- ((no_tape) ? GMT_DR_OPEN(-1L) : 0);
- krnl_arg.mtget.mt_erreg = ftape_last_error; /* error register */
- result = ftape_file_no(&krnl_arg.mtget.mt_fileno,
- &krnl_arg.mtget.mt_blkno);
- break;
- case MTIOCPOS:
- TRACE(5, "Mag tape ioctl command: MTIOCPOS");
- TRACE(1, "MTIOCPOS command not implemented");
- break;
- default:
- result = -EINVAL;
- break;
- }
- if (command & IOC_OUT) {
- int error = verify_area(VERIFY_WRITE, arg, arg_size);
- if (error) {
- TRACE_EXIT;
- return error;
- }
- copy_to_user(arg, &krnl_arg, arg_size);
- }
- TRACE_EXIT;
- return result;
-}
-
-void ftape_init_driver(void)
-{
- drive_type.vendor_id = UNKNOWN_VENDOR;
- drive_type.speed = 0;
- drive_type.wake_up = unknown_wake_up;
- drive_type.name = "Unknown";
-
- timeout.seek = 650 * SECOND;
- timeout.reset = 670 * SECOND;
- timeout.rewind = 650 * SECOND;
- timeout.head_seek = 15 * SECOND;
- timeout.stop = 5 * SECOND;
- timeout.pause = 16 * SECOND;
-
- qic_std = -1;
- tape_len = -1;
- current_command = 0;
- current_cylinder = -1;
-
- segments_per_track = 102;
- segments_per_head = 1020;
- segments_per_cylinder = 4;
- tracks_per_tape = 20;
- ftape_failure = 1;
- ftape_seg_pos = 0;
- first_data_segment = -1;
- ftape_state = idle;
- no_tape = 1;
- formatted = 0;
- ftape_data_rate = 0;
- going_offline = 0;
- read_only = 0;
-
- init_drive_needed = 1;
- header_segment_1 = -1;
- header_segment_2 = -1;
- used_header_segment = -1;
- location.track = -1;
- location.known = 0;
- tape_running = 0;
- might_be_off_track = 1;
-
- ftape_new_cartridge(); /* init some tape related variables */
- ftape_init_bsm();
-}
diff --git a/drivers/char/ftape/ftape-ctl.h b/drivers/char/ftape/ftape-ctl.h
deleted file mode 100644
index 59fcfdc76..000000000
--- a/drivers/char/ftape/ftape-ctl.h
+++ /dev/null
@@ -1,94 +0,0 @@
-#ifndef _FTAPE_CTL_H
-#define _FTAPE_CTL_H
-
-/*
- * Copyright (C) 1993-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING. If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- *
- $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-ctl.h,v $
- $Author: bas $
- *
- $Revision: 1.4 $
- $Date: 1995/05/03 18:04:03 $
- $State: Beta $
- *
- * This file contains the non-standard IOCTL related definitions
- * for the QIC-40/80 floppy-tape driver for Linux.
- */
-
-#include <linux/ioctl.h>
-#include <linux/mtio.h>
-
-#include "vendors.h"
-
-
-typedef struct {
- int used; /* any reading or writing done */
- /* isr statistics */
- unsigned int id_am_errors; /* id address mark not found */
- unsigned int id_crc_errors; /* crc error in id address mark */
- unsigned int data_am_errors; /* data address mark not found */
- unsigned int data_crc_errors; /* crc error in data field */
- unsigned int overrun_errors; /* fdc access timing problem */
- unsigned int no_data_errors; /* sector not found */
- unsigned int retries; /* number of tape retries */
- /* ecc statistics */
- unsigned int crc_errors; /* crc error in data */
- unsigned int crc_failures; /* bad data without crc error */
- unsigned int ecc_failures; /* failed to correct */
- unsigned int corrected; /* total sectors corrected */
- /* general statistics */
- unsigned int rewinds; /* number of tape rewinds */
- unsigned int defects; /* bad sectors due to media defects */
-} history_record;
-
-/*
- * ftape-ctl.c defined global vars.
- */
-extern int ftape_failure;
-extern int write_protected;
-extern ftape_offline;
-extern int formatted;
-extern int no_tape;
-extern history_record history;
-extern int ftape_data_rate;
-extern int going_offline;
-extern vendor_struct drive_type;
-extern int segments_per_track;
-extern int segments_per_head;
-extern int segments_per_cylinder;
-extern int tracks_per_tape;
-extern int ftape_seg_pos;
-extern int first_data_segment;
-extern int ftape_state;
-extern int read_only;
-
-/*
- * ftape-ctl.c defined global functions.
- */
-extern int _ftape_open(void);
-extern int _ftape_close(void);
-extern int _ftape_ioctl(unsigned int command, void *arg);
-extern int ftape_seek_to_bot(void);
-extern int ftape_seek_to_eot(void);
-extern int ftape_new_cartridge(void);
-extern int ftape_abort_operation(void);
-extern void ftape_reset_position(void);
-extern void ftape_calc_timeouts(void);
-extern void ftape_init_driver(void);
-
-#endif
diff --git a/drivers/char/ftape/ftape-eof.c b/drivers/char/ftape/ftape-eof.c
deleted file mode 100644
index 75c4cbd76..000000000
--- a/drivers/char/ftape/ftape-eof.c
+++ /dev/null
@@ -1,554 +0,0 @@
-
-/*
- * Copyright (C) 1994-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING. If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-eof.c,v $
- $Author: bas $
- *
- $Revision: 1.21 $
- $Date: 1995/05/27 08:54:21 $
- $State: Beta $
- *
- * This file contains the eof mark handling code
- * for the QIC-40/80 floppy-tape driver for Linux.
- */
-
-#include <linux/ftape.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-
-#include "tracing.h"
-#include "ftape-eof.h"
-#include "ftape-write.h"
-#include "ftape-read.h"
-#include "ftape-rw.h"
-#include "ftape-ctl.h"
-#include "ftape-bsm.h"
-
-/* Global vars.
- */
-int failed_sector_log_changed = 0;
-int eof_mark = 0;
-
-/* Local vars.
- */
-static struct failed_sector_entry {
- unsigned short segment;
- unsigned short sector;
-} *eof_mark_ptr;
-
-typedef union {
- struct failed_sector_entry mark;
- unsigned long entry;
-} eof_mark_union;
-
-/* a copy of the failed sector log from the header segment.
- */
-static eof_mark_union eof_map[(2048 - 256) / 4];
-
-/* index into eof_map table pointing to last found eof mark.
- */
-static int eof_index;
-
-/* number of eof marks (entries in bad sector log) on tape.
- */
-static int nr_of_eof_marks = -1;
-
-static char linux_tape_label[] = "Linux raw format V";
-enum {
- min_fmt_version = 1, max_fmt_version = 2
-};
-static unsigned ftape_fmt_version = 0;
-
-
-/* Ftape (mis)uses the bad sector log to record end-of-file marks.
- * Initially (when the tape is erased) all entries in the bad sector
- * log are added to the tape's bad sector map. The bad sector log
- * then is cleared.
- *
- * The bad sector log normally contains entries of the form:
- * even 16-bit word: segment number of bad sector
- * odd 16-bit word: encoded date
- * There can be a total of 448 entries (1792 bytes).
- *
- * My guess is that no program is using this bad sector log (the
- * format seems useless as there is no indication of the bad sector
- * itself, only the segment)
- * However, if any program does use the bad sector log, the format
- * used by ftape will let the program think there are some bad sectors
- * and no harm is done.
- *
- * The eof mark entries that ftape stores in the bad sector log:
- * even 16-bit word: segment number of eof mark
- * odd 16-bit word: sector number of eof mark [1..32]
- *
- * The eof_map as maintained is a sorted list of eof mark entries.
- *
- *
- * The tape name field in the header segments is used to store a
- * linux tape identification string and a version number.
- * This way the tape can be recognized as a Linux raw format
- * tape when using tools under other OS's.
- *
- * 'Wide' QIC tapes (format code 4) don't have a failed sector list
- * anymore. That space is used for the (longer) bad sector map that
- * now is a variable length list too.
- * We now store our end-of-file marker list after the bad-sector-map
- * on tape. The list is delimited by a (long) 0 entry.
- */
-
-int ftape_validate_label(char *label)
-{
- TRACE_FUN(8, "ftape_validate_label");
- int result = 0;
-
- TRACEx1(4, "tape label = `%s'", label);
- ftape_fmt_version = 0;
- if (memcmp(label, linux_tape_label, strlen(linux_tape_label)) == 0) {
- int pos = strlen(linux_tape_label);
- while (label[pos] >= '0' && label[pos] <= '9') {
- ftape_fmt_version *= 10;
- ftape_fmt_version = label[pos++] - '0';
- }
- result = (ftape_fmt_version >= min_fmt_version &&
- ftape_fmt_version <= max_fmt_version);
- }
- TRACEx1(4, "format version = %d", ftape_fmt_version);
- TRACE_EXIT;
- return result;
-}
-
-static byte *
- find_end_of_eof_list(byte * ptr, byte * limit)
-{
- while (ptr + 3 < limit) {
- if (*(unsigned long *) ptr) {
- ++(unsigned long *) ptr;
- } else {
- return ptr;
- }
- }
- return NULL;
-}
-
-void reset_eof_list(void)
-{
- TRACE_FUN(8, "reset_eof_list");
-
- eof_mark_ptr = &eof_map[0].mark;
- eof_index = 0;
- eof_mark = 0;
- TRACE_EXIT;
-}
-
-/* Test if `segment' has an eof mark set (optimized for sequential access).
- * return 0 if not eof mark or sector number (> 0) if eof mark set.
- */
-int check_for_eof(unsigned segment)
-{
- TRACE_FUN(8, "check_for_eof");
- static unsigned last_reference = INT_MAX;
- int result;
-
- if (segment < last_reference) {
- reset_eof_list();
- }
- last_reference = segment;
- while (eof_index < nr_of_eof_marks && segment > eof_mark_ptr->segment) {
- ++eof_mark_ptr;
- ++eof_index;
- }
- if (eof_index < nr_of_eof_marks && segment == eof_mark_ptr->segment) {
- TRACEx3(5, "hit mark %d/%d at index %d",
- eof_map[eof_index].mark.segment, eof_map[eof_index].mark.sector,
- eof_index);
- if (eof_mark_ptr->sector >= SECTORS_PER_SEGMENT) {
- TRACEx2(-1, "Bad file mark detected: %d/%d",
- eof_mark_ptr->segment, eof_mark_ptr->sector);
- result = 0; /* return bogus (but valid) value */
- } else {
- result = eof_mark_ptr->sector;
- }
- } else {
- result = 0;
- }
- TRACE_EXIT;
- return result;
-}
-
-void clear_eof_mark_if_set(unsigned segment, unsigned byte_count)
-{
- TRACE_FUN(5, "clear_eof_mark_if_set");
- if (ftape_fmt_version != 0 &&
- check_for_eof(segment) > 0 &&
- byte_count >= eof_mark_ptr->sector * SECTOR_SIZE) {
- TRACEx3(5, "clearing mark %d/%d at index %d",
- eof_mark_ptr->segment, eof_mark_ptr->sector, eof_index);
- memmove(&eof_map[eof_index], &eof_map[eof_index + 1],
- (nr_of_eof_marks - eof_index) * sizeof(*eof_map));
- --nr_of_eof_marks;
- failed_sector_log_changed = 1;
- }
- TRACE_EXIT;
-}
-
-void put_file_mark_in_map(unsigned segment, unsigned sector)
-{
- TRACE_FUN(8, "put_file_mark_in_map");
- eof_mark_union new;
- int index;
- eof_mark_union *ptr;
-
- if (ftape_fmt_version != 0) {
- new.mark.segment = segment;
- new.mark.sector = sector;
- for (index = 0, ptr = &eof_map[0];
- index < nr_of_eof_marks && ptr->mark.segment < segment;
- ++index, ++ptr) {
- }
- if (index < nr_of_eof_marks) {
- if (ptr->mark.segment == segment) {
- /* overwrite */
- if (ptr->mark.sector == sector) {
- TRACEx2(5, "mark %d/%d already exists",
- new.mark.segment, new.mark.sector);
- } else {
- TRACEx5(5, "overwriting %d/%d at index %d with %d/%d",
- ptr->mark.segment, ptr->mark.sector, index,
- new.mark.segment, new.mark.sector);
- ptr->entry = new.entry;
- failed_sector_log_changed = 1;
- }
- } else {
- /* insert */
- TRACEx5(5, "inserting %d/%d at index %d before %d/%d",
- new.mark.segment, new.mark.sector, index,
- ptr->mark.segment, ptr->mark.sector);
- memmove(ptr + 1, ptr, (nr_of_eof_marks - index) * sizeof(*eof_map));
- ptr->entry = new.entry;
- ++nr_of_eof_marks;
- failed_sector_log_changed = 1;
- }
- } else {
- /* append */
- TRACEx3(5, "appending %d/%d at index %d",
- new.mark.segment, new.mark.sector, index);
- ptr->entry = new.entry;
- ++nr_of_eof_marks;
- failed_sector_log_changed = 1;
- }
- }
- TRACE_EXIT;
-}
-
-/* Write count file marks to tape starting at first non-bad
- * sector following the given segment and sector.
- * sector = base 1 !
- */
-int ftape_weof(unsigned count, unsigned segment, unsigned sector)
-{
- TRACE_FUN(5, "ftape_weof");
- int result = 0;
- unsigned long mask = get_bad_sector_entry(segment);
- unsigned sector_nr = 0;
-
- if (ftape_fmt_version != 0) {
- if (sector < 1 || sector > 29 ||
- segment + count >= ftape_last_segment.id) {
- TRACEx3(5, "parameter out of range: %d, %d, %d", count, segment, sector);
- result = -EIO;
- } else {
- while (count-- > 0) {
- do { /* count logical sectors */
- do { /* skip until good sector */
- while (mask & 1) { /* skip bad sectors */
- ++sector_nr;
- mask >>= 1;
- }
- if (sector_nr >= 29) {
- if (++segment >= ftape_last_segment.id) {
- TRACEx1(5, "segment out of range: %d", segment);
- result = -EIO;
- break;
- }
- mask = get_bad_sector_entry(segment);
- sector_nr = 0;
- }
- } while (mask & 1);
- ++sector_nr; /* point to good sector */
- mask >>= 1;
- } while (--sector);
- if (result >= 0) {
- TRACEx2(5, "writing filemark %d/%d", segment, sector_nr);
- put_file_mark_in_map(segment, sector_nr);
- ++segment; /* next segment */
- sector_nr = 0;
- sector = 1; /* first sector */
- }
- }
- }
- } else {
- result = -EPERM;
- }
- TRACE_EXIT;
- return result;
-}
-
-int ftape_erase(void)
-{
- TRACE_FUN(5, "ftape_erase");
- int result = 0;
- int i;
- unsigned long now = 0;
- byte *buffer = deblock_buffer;
-
- if (write_protected) {
- result = -EROFS;
- } else {
- result = read_header_segment(buffer);
- if (result >= 0) {
- /* Copy entries from bad-sector-log into bad-sector-map
- */
- TRACEx1(5, "old label: `%s'", (char *) (buffer + 30));
- if (!ftape_validate_label((char *) &buffer[30])) {
- TRACE(5, "invalid label, overwriting with new");
- memset(buffer + 30, 0, 44);
- memcpy(buffer + 30, linux_tape_label, strlen(linux_tape_label));
- buffer[30 + strlen(linux_tape_label)] = '2';
- TRACEx1(5, "new label: `%s'", (char *) (buffer + 30));
- PUT4(buffer, 74, now);
- if (format_code != 4) {
- for (i = 0; i < nr_of_eof_marks; ++i) {
- unsigned failing_segment = eof_map[i].mark.segment;
-
- if (!valid_segment_no(failing_segment)) {
- TRACEi(4, "bad entry in failed sector log:", failing_segment);
- } else {
- put_bad_sector_entry(failing_segment, EMPTY_SEGMENT);
- TRACEx2(4, "moved entry %d from failed sector log (%d)",
- i, failing_segment);
- }
- }
- }
- }
- /* Clear failed sector log: remove all tape marks
- */
- failed_sector_log_changed = 1;
- memset(eof_map, 0, sizeof(eof_map));
- nr_of_eof_marks = 0;
- ftape_fmt_version = max_fmt_version;
-#if 0
- fix_tape(buffer); /* see ftape-bsm.c ! */
-#endif
- result = ftape_update_header_segments(buffer, 1);
- prevent_flush(); /* prevent flush_buffers writing file marks */
- reset_eof_list();
- }
- }
- TRACE_EXIT;
- return result;
-}
-
-void extract_file_marks(byte * address)
-{
- TRACE_FUN(8, "extract_file_marks");
- int i;
-
- if (format_code == 4) {
- byte *end;
- byte *start = find_end_of_bsm_list(address + 256,
- address + 29 * SECTOR_SIZE);
-
- memset(eof_map, 0, sizeof(eof_map));
- nr_of_eof_marks = 0;
- if (start) {
- start += 3; /* skip end of list mark */
- end = find_end_of_eof_list(start, address + 29 * SECTOR_SIZE);
- if (end && end - start <= sizeof(eof_map)) {
- nr_of_eof_marks = (end - start) / sizeof(unsigned long);
- memcpy(eof_map, start, end - start);
- } else {
- TRACE(1, "File Mark List is too long or damaged !");
- }
- } else {
- TRACE(1, "Bad Sector List is too long or damaged !");
- }
- } else {
- memcpy(eof_map, address + 256, sizeof(eof_map));
- nr_of_eof_marks = GET2(address, 144);
- }
- TRACEi(4, "number of file marks:", nr_of_eof_marks);
- if (ftape_fmt_version == 1) {
- TRACE(-1, "swapping version 1 fields");
- /* version 1 format uses swapped sector and segment fields, correct that !
- */
- for (i = 0; i < nr_of_eof_marks; ++i) {
- unsigned short tmp = eof_map[i].mark.segment;
- eof_map[i].mark.segment = eof_map[i].mark.sector;
- eof_map[i].mark.sector = tmp;
- }
- }
- for (i = 0; i < nr_of_eof_marks; ++i) {
- TRACEx2(4, "eof mark: %5d/%2d",
- eof_map[i].mark.segment, eof_map[i].mark.sector);
- }
- reset_eof_list();
- TRACE_EXIT;
-}
-
-int update_failed_sector_log(byte * buffer)
-{
- TRACE_FUN(8, "update_failed_sector_log");
-
- if (ftape_fmt_version != 0 && failed_sector_log_changed) {
- if (ftape_fmt_version == 1) {
- TRACE(-1, "upgrading version 1 format to version 2");
- /* version 1 will be upgraded to version 2 when written.
- */
- buffer[30 + strlen(linux_tape_label)] = '2';
- ftape_fmt_version = 2;
- TRACEx1(-1, "new tape label = \"%s\"", &buffer[30]);
- }
- if (format_code == 4) {
- byte *dest = find_end_of_bsm_list(buffer + 256,
- buffer + 29 * SECTOR_SIZE) + 3;
-
- if (dest) {
- TRACEx2(4, "eof_map at byte offset %6d, size %d",
- dest - buffer - 256, nr_of_eof_marks * sizeof(unsigned long));
- memcpy(dest, eof_map, nr_of_eof_marks * sizeof(unsigned long));
- PUT4(dest, nr_of_eof_marks * sizeof(unsigned long), 0);
- }
- } else {
- memcpy(buffer + 256, eof_map, sizeof(eof_map));
- PUT2(buffer, 144, nr_of_eof_marks);
- }
- failed_sector_log_changed = 0;
- return 1;
- }
- TRACE_EXIT;
- return 0;
-}
-
-int ftape_seek_eom(void)
-{
- TRACE_FUN(5, "ftape_seek_eom");
- int result = 0;
- unsigned eom;
-
- if (first_data_segment == -1) {
- result = read_header_segment(deblock_buffer);
- }
- if (result >= 0 && ftape_fmt_version != 0) {
- eom = first_data_segment;
- eof_index = 0;
- eof_mark_ptr = &eof_map[0].mark;
- /* If fresh tape, count should be zero but we don't
- * want to worry about the case it's one.
- */
- for (eof_index = 1, eof_mark_ptr = &eof_map[1].mark;
- eof_index < nr_of_eof_marks; ++eof_index, ++eof_mark_ptr) {
- /* The eom is recorded as two eof marks in succeeding segments
- * where the second one is always at segment number 1.
- */
- if (eof_mark_ptr->sector == 1) {
- if (eof_mark_ptr->segment == (eof_mark_ptr - 1)->segment + 1) {
- eom = eof_mark_ptr->segment;
- break;
- }
- }
- }
- ftape_seg_pos = eom;
- TRACEx1(5, "eom found at segment %d", eom);
- } else {
- TRACE(5, "Couldn't get eof mark table");
- result = -EIO;
- }
- TRACE_EXIT;
- return result;
-}
-
-int ftape_seek_eof(unsigned count)
-{
- TRACE_FUN(5, "ftape_seek_eof");
- int result = 0;
- enum {
- not = 0, begin, end
- } bad_seek = not;
-
- if (first_data_segment == -1) {
- result = read_header_segment(deblock_buffer);
- }
- TRACEx1(5, "tape positioned at segment %d", ftape_seg_pos);
- if (ftape_fmt_version == 0) {
- result = -1;
- }
- if (result >= 0 && count != 0) {
- for (eof_index = 0; eof_index <= nr_of_eof_marks; ++eof_index) {
- if (eof_index == nr_of_eof_marks || /* start seeking after last mark */
- ftape_seg_pos <= eof_map[eof_index].mark.segment) {
- eof_index += count;
- if (eof_index < 1) { /* begin of tape */
- ftape_seg_pos = first_data_segment;
- if (eof_index < 0) { /* `before' begin of tape */
- eof_index = 0;
- bad_seek = begin;
- }
- } else if (eof_index >= nr_of_eof_marks) { /* `after' end of tape */
- ftape_seg_pos = segments_per_track * tracks_per_tape;
- if (eof_index > nr_of_eof_marks) {
- eof_index = nr_of_eof_marks;
- bad_seek = end;
- }
- } else { /* after requested file mark */
- ftape_seg_pos = eof_map[eof_index - 1].mark.segment + 1;
- }
- eof_mark_ptr = &eof_map[eof_index].mark;
- break;
- }
- }
- }
- if (result < 0) {
- TRACE(5, "Couldn't get eof mark table");
- result = -EIO;
- } else if (bad_seek != not) {
- TRACEx1(1, "seek reached %s of tape",
- (bad_seek == begin) ? "begin" : "end");
- result = -EIO;
- } else {
- TRACEx1(5, "tape repositioned to segment %d", ftape_seg_pos);
- }
- TRACE_EXIT;
- return result;
-}
-
-int ftape_file_no(daddr_t * f_no, daddr_t * b_no)
-{
- TRACE_FUN(5, "ftape_file_no");
- int result = 0;
- int i;
-
- *f_no = eof_index;
- *b_no = ftape_seg_pos;
- TRACEi(4, "number of file marks:", nr_of_eof_marks);
- for (i = 0; i < nr_of_eof_marks; ++i) {
- TRACEx2(4, "eof mark: %5d/%2d",
- eof_map[i].mark.segment, eof_map[i].mark.sector);
- }
- TRACE_EXIT;
- return result;
-}
diff --git a/drivers/char/ftape/ftape-io.c b/drivers/char/ftape/ftape-io.c
deleted file mode 100644
index 3957bc8ab..000000000
--- a/drivers/char/ftape/ftape-io.c
+++ /dev/null
@@ -1,1050 +0,0 @@
-/*
- * Copyright (C) 1993-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING. If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-io.c,v $
- $Author: bas $
- *
- $Revision: 1.58 $
- $Date: 1995/05/27 08:54:21 $
- $State: Beta $
- *
- * This file contains the general control functions
- * for the QIC-40/80 floppy-tape driver for Linux.
- */
-
-#include <linux/errno.h>
-#include <linux/sched.h>
-#include <linux/mm.h>
-#include <linux/ftape.h>
-#include <asm/uaccess.h>
-#include <asm/system.h>
-#include <linux/ioctl.h>
-#include <linux/mtio.h>
-
-#include "tracing.h"
-#include "fdc-io.h"
-#include "qic117.h"
-#include "ftape-io.h"
-#include "ftape-ctl.h"
-#include "ftape-rw.h"
-#include "ftape-write.h"
-#include "ftape-read.h"
-#include "ftape-eof.h"
-#include "kernel-interface.h"
-#include "calibr.h"
-
-/* Global vars.
- */
-/* NOTE: sectors start numbering at 1, all others at 0 ! */
-timeout_table timeout;
-vendor_struct drive_type;
-int qic_std;
-int tape_len;
-volatile int current_command;
-const struct qic117_command_table qic117_cmds[] = QIC117_COMMANDS;
-int might_be_off_track;
-
-/* Local vars.
- */
-static int command_parameter = 0;
-/* command-restrictions is a table according
- * to the QIC-117 specs specifying the state
- * the drive status should be in at command execution.
- */
-static const ftape_error ftape_errors[] = QIC117_ERRORS;
-static int ftape_udelay_count;
-static int ftape_udelay_time;
-static const struct {
- char *text;
- int fdc_code;
- byte drive_code;
- int precomp;
-} rates[4] = {
-
-#if defined(FDC_82078SL)
- {
- "2 M", -1 /* unsupported */ , QIC_CONFIG_RATE_2000, 0
- },
-#else
- {
- "2 M", fdc_data_rate_2000, QIC_CONFIG_RATE_2000, 0
- },
-#endif
- {
- "1 M", fdc_data_rate_1000, QIC_CONFIG_RATE_1000, 42
- },
- {
- "500 K", fdc_data_rate_500, QIC_CONFIG_RATE_500, 125
- },
- {
- "250 K", fdc_data_rate_250, QIC_CONFIG_RATE_250, 250
- },
-};
-typedef enum {
- prehistoric, pre_qic117c, post_qic117b, post_qic117d
-} qic_model;
-
-
-void udelay(int usecs)
-{
- volatile int count = (1 + (usecs * ftape_udelay_count - 1) /
- ftape_udelay_time);
- volatile int i;
-
- while (count-- > 0) {
- for (i = 0; i < 20; ++i);
- }
-}
-
-int udelay_calibrate(void)
-{
- return calibrate("udelay", udelay, &ftape_udelay_count, &ftape_udelay_time);
-}
-
-/* Delay (msec) routine.
- */
-void ftape_sleep(unsigned int time)
-{
- TRACE_FUN(8, "ftape_sleep");
- unsigned long flags;
- int ticks = 1 + (time + MSPT - 1) / MSPT;
-
- /* error in range [0..1] MSPT
- */
- if (time < MSPT) {
- /* Time too small for scheduler, do a busy wait ! */
- udelay(1000 * time);
- } else {
- TRACEx2(8, "%d msec, %d ticks", time, ticks);
- current->timeout = jiffies + ticks;
- current->state = TASK_INTERRUPTIBLE;
- save_flags(flags);
- sti();
- do {
- while (current->state != TASK_RUNNING) {
- schedule();
- }
- if (current->signal & ~current->blocked) {
- TRACE(1, "awoken by non-blocked signal :-(");
- break; /* exit on signal */
- }
- } while (current->timeout > 0);
- restore_flags(flags);
- }
- TRACE_EXIT;
-}
-
-/* forward */ int ftape_report_raw_drive_status(int *status);
-
-/* Issue a tape command:
- * Generate command # of step pulses.
- */
-int ftape_command(int command)
-{
- TRACE_FUN(8, "ftape_command");
- int result = 0;
- int track;
- int old_tracing = tracing;
- static int level = 0;
- int status = -1;
-
- if (++level > 5) {
- /* This is a bug we'll want to know about.
- */
- TRACEx1(1, "bug - recursion for command: %d", command);
- result = -EIO;
- } else if (command_parameter) {
- /* Don't check restrictions for parameters.
- */
- TRACEx1(5, "called with parameter = %d", command - 2);
- } else if (command <= 0 || command > NR_ITEMS(qic117_cmds)) {
- /* This is a bug we'll want to know about too.
- */
- TRACEx1(-1, "bug - bad command: %d", command);
- result = -EIO;
- } else {
- /* disable logging and restriction check for some commands,
- * check all other commands that have a prescribed starting status.
- */
- if (command == QIC_REPORT_DRIVE_STATUS) {
- TRACE(8, "report drive status called");
- tracing = 0;
- } else if (command == QIC_REPORT_NEXT_BIT) {
- tracing = 0;
- } else {
- TRACEx1(5, "%s", qic117_cmds[command].name);
- /* A new motion command during an uninterruptible (motion)
- * command requires a ready status before the new command
- * can be issued. Otherwise a new motion command needs to
- * be checked against required status.
- */
- if (qic117_cmds[command].cmd_type == motion &&
- qic117_cmds[current_command].non_intr) {
- ftape_report_raw_drive_status(&status);
- if ((status & QIC_STATUS_READY) == 0) {
- TRACEx2(4, "motion cmd (%d) during non-intr cmd (%d)",
- command, current_command);
- TRACE(4, "waiting until drive gets ready");
- ftape_ready_wait(timeout.seek, &status);
- }
- }
- if (qic117_cmds[command].mask != 0) {
- byte difference;
-
- /* Some commands do require a certain status:
- */
- if (status == -1) { /* not yet set */
- ftape_report_raw_drive_status(&status);
- }
- difference = ((status ^ qic117_cmds[command].state) &
- qic117_cmds[command].mask);
- /* Wait until the drive gets ready. This may last forever
- * if the drive never gets ready...
- */
- while ((difference & QIC_STATUS_READY) != 0) {
- TRACEx1(4, "command %d issued while not ready", command);
- TRACE(4, "waiting until drive gets ready");
- ftape_ready_wait(timeout.seek, &status);
- difference = ((status ^ qic117_cmds[command].state) &
- qic117_cmds[command].mask);
- /* Bail out on signal !
- */
- if (current->signal & _DONT_BLOCK) {
- result = -EINTR;
- break;
- }
- }
- while (result == 0 && (difference & QIC_STATUS_ERROR) != 0) {
- int err;
- int cmd;
-
- TRACEx1(4, "command %d issued while error pending", command);
- TRACE(4, "clearing error status");
- ftape_report_error(&err, &cmd, 1);
- ftape_report_raw_drive_status(&status);
- difference = ((status ^ qic117_cmds[command].state) &
- qic117_cmds[command].mask);
- /* Bail out on fatal signal !
- */
- if (current->signal & _DONT_BLOCK) {
- result = -EINTR;
- break;
- }
- }
- if (result == 0 && difference) {
- /* Any remaining difference can't be solved here.
- */
- if (difference & (QIC_STATUS_CARTRIDGE_PRESENT |
- QIC_STATUS_NEW_CARTRIDGE |
- QIC_STATUS_REFERENCED)) {
- TRACE(1, "Fatal: tape removed or reinserted !");
- ftape_failure = 1;
- } else {
- TRACEx2(1, "wrong state: 0x%02x should be: 0x%02x",
- status & qic117_cmds[command].mask,
- qic117_cmds[command].state);
- }
- result = -EIO;
- }
- if (~status & QIC_STATUS_READY & qic117_cmds[command].mask) {
- TRACE(1, "Bad: still busy!");
- result = -EBUSY;
- }
- }
- }
- }
- tracing = old_tracing;
- /* Now all conditions are met or result is < 0.
- */
- if (result >= 0) {
- /* Always wait for a command_timeout period to separate
- * individuals commands and/or parameters.
- */
- ftape_sleep(3 * MILLISECOND);
- /* Keep cylinder nr within range, step towards home if possible.
- */
- if (current_cylinder >= command) {
- track = current_cylinder - command;
- } else {
- track = current_cylinder + command;
- }
- result = fdc_seek(track);
- /* position is no longer valid after any of these commands
- * have completed.
- */
- if (qic117_cmds[command].cmd_type == motion &&
- command != QIC_LOGICAL_FORWARD && command != QIC_STOP_TAPE) {
- location.known = 0;
- }
- command_parameter = 0; /* always turned off for next command */
- current_command = command;
- }
- --level;
- TRACE_EXIT;
- return result;
-}
-
-/* Send a tape command parameter:
- * Generates command # of step pulses.
- * Skips tape-status call !
- */
-int ftape_parameter(int command)
-{
- command_parameter = 1;
- return ftape_command(command + 2);
-}
-
-/* Wait for the drive to get ready.
- * timeout time in milli-seconds
- * Returned status is valid if result != -EIO
- */
-int ftape_ready_wait(int timeout, int *status)
-{
- TRACE_FUN(8, "ftape_ready_wait");
- int result;
- unsigned long t0;
- const int poll_delay = 100 * MILLISECOND;
-
- for (;;) {
- t0 = jiffies;
- result = ftape_report_raw_drive_status(status);
- if (result < 0) {
- TRACE(1, "ftape_report_raw_drive_status failed");
- result = -EIO;
- break;
- }
- if (*status & QIC_STATUS_READY) {
- result = 0;
- break;
- }
- if (timeout >= 0) {
- /* this will fail when jiffies wraps around about
- * once every year :-)
- */
- timeout -= ((jiffies - t0) * SECOND) / HZ;
- if (timeout <= 0) {
- TRACE(1, "timeout");
- result = -ETIME;
- break;
- }
- ftape_sleep(poll_delay);
- timeout -= poll_delay;
- } else {
- ftape_sleep(poll_delay);
- }
- if (current->signal & _NEVER_BLOCK) {
- TRACE(1, "interrupted by fatal signal");
- result = -EINTR;
- break; /* exit on signal */
- }
- }
- TRACE_EXIT;
- return result;
-}
-
-/* Issue command and wait up to timeout seconds for drive ready
- */
-int ftape_command_wait(int command, int timeout, int *status)
-{
- TRACE_FUN(8, "ftape_command_wait");
- int result;
-
- /* Drive should be ready, issue command
- */
- result = ftape_command(command);
- if (result >= 0) {
- result = ftape_ready_wait(timeout, status);
- }
- TRACE_EXIT;
- return result;
-}
-
-int ftape_parameter_wait(int command, int timeout, int *status)
-{
- TRACE_FUN(8, "ftape_parameter_wait");
- int result;
-
- /* Drive should be ready, issue command
- */
- result = ftape_parameter(command);
- if (result >= 0) {
- result = ftape_ready_wait(timeout, status);
- }
- TRACE_EXIT;
- return result;
-}
-
-/*--------------------------------------------------------------------------
- * Report operations
- */
-
-/* Query the drive about its status. The command is sent and
- result_length bits of status are returned (2 extra bits are read
- for start and stop). */
-
-static int ftape_report_operation(int *status, int command, int result_length)
-{
- TRACE_FUN(8, "ftape_report_operation");
- int i, st3;
- int result;
- unsigned int t0, t1, dt;
-
- result = ftape_command(command);
- if (result < 0) {
- TRACE(1, "ftape_command failed");
- TRACE_EXIT;
- return result;
- }
- t0 = timestamp();
- dt = 0;
- i = 0;
- do {
- ++i;
- ftape_sleep(3 * MILLISECOND); /* see remark below */
- result = fdc_sense_drive_status(&st3);
- if (result < 0) {
- TRACE(1, "fdc_sense_drive_status failed");
- TRACE_EXIT;
- return result;
- }
- /* Calculate time difference every iteration because timer may
- * wrap around (but only one !) and timediff will account for this.
- * Note that the sleep above must be < 1/HZ or we'll lose ticks !
- */
- t1 = timestamp();
- dt += timediff(t0, t1);
- t0 = t1;
- /* Ack should be asserted within Ttimout + Tack = 6 msec.
- * Looks like some drives fail to do this so extend this
- * period to 300 msec.
- */
- } while (!(st3 & ST3_TRACK_0) && dt < 300000);
- if (st3 & ST3_TRACK_0) {
- /* dt may be larger than expected because of other tasks
- * scheduled while we were sleeping.
- */
- if (i > 1 && dt > 6000) {
- TRACEx2(1, "Acknowledge after %u msec. (%i iter)", dt / 1000, i);
- }
- } else {
- TRACEx2(1, "No acknowledge after %u msec. (%i iter)", dt / 1000, i);
- TRACE(1, "timeout on Acknowledge");
- TRACE_EXIT;
- return -EIO;
- }
- *status = 0;
- for (i = 0; i < result_length + 1; i++) {
- result = ftape_command(QIC_REPORT_NEXT_BIT);
- if (result < 0) {
- TRACE(1, "report next bit failed");
- TRACE_EXIT;
- return result;
- }
-#if 1
- /* fdc_seek does interrupt wait, so why should we ?
- * (it will only fail causing fdc to be reset...)
- * It's only purpose may be the delay, we'll have to find out!
- */
-#else
- fdc_interrupt_wait(25 * MILLISECOND); /* fails only if hw fails */
-#endif
- result = fdc_sense_drive_status(&st3);
- if (result < 0) {
- TRACE(1, "fdc_sense_drive_status (2) failed");
- TRACE_EXIT;
- return result;
- }
- if (i < result_length) {
- *status |= ((st3 & ST3_TRACK_0) ? 1 : 0) << i;
- } else {
- if ((st3 & ST3_TRACK_0) == 0) {
- TRACE(1, "missing status stop bit");
- TRACE_EXIT;
- return -EIO;
- }
- }
- }
- /* this command will put track zero and index back into normal state */
- result = ftape_command(QIC_REPORT_NEXT_BIT);
- TRACE_EXIT;
- return 0;
-}
-
-/* Report the current drive status. */
-
-int ftape_report_raw_drive_status(int *status)
-{
- TRACE_FUN(8, "ftape_report_raw_drive_status");
- int result;
- int count = 0;
-
- do {
- result = ftape_report_operation(status, QIC_REPORT_DRIVE_STATUS, 8);
- } while (result < 0 && ++count <= 3);
- if (result < 0) {
- TRACE(1, "report_operation failed");
- result = -EIO;
- } else if (*status & QIC_STATUS_READY) {
- current_command = 0; /* completed */
- }
- TRACE_EXIT;
- return result;
-}
-
-int ftape_report_drive_status(int *status)
-{
- TRACE_FUN(8, "ftape_report_drive_status");
- int result;
-
- result = ftape_report_raw_drive_status(status);
- if (result < 0) {
- TRACE(1, "ftape_report_raw_drive_status failed");
- TRACE_EXIT;
- return result;
- }
- if (*status & QIC_STATUS_NEW_CARTRIDGE ||
- !(*status & QIC_STATUS_CARTRIDGE_PRESENT)) {
- ftape_failure = 1; /* will inhibit further operations */
- TRACE_EXIT;
- return -EIO;
- }
- if (*status & QIC_STATUS_READY && *status & QIC_STATUS_ERROR) {
- /* Let caller handle all errors */
- TRACE(2, "warning: error status set!");
- result = 1;
- }
- TRACE_EXIT;
- return result;
-}
-
-int ftape_report_error(int *error, int *command, int report)
-{
- TRACE_FUN(8, "ftape_report_error");
- int code;
- int result;
-
- result = ftape_report_operation(&code, QIC_REPORT_ERROR_CODE, 16);
- if (result < 0) {
- result = -EIO;
- } else {
- *error = code & 0xff;
- *command = (code >> 8) & 0xff;
- if (report) {
- if (*error != 0) {
- TRACEi(3, "errorcode:", *error);
- } else {
- TRACE(3, "No error");
- }
- }
- if (report && *error != 0 && tracing > 3) {
- if (*error >= 0 && *error < NR_ITEMS(ftape_errors)) {
- TRACEx1(-1, "%sFatal ERROR:",
- (ftape_errors[*error].fatal ? "" : "Non-"));
- TRACEx1(-1, "%s ...", ftape_errors[*error].message);
- } else {
- TRACE(-1, "Unknown ERROR !");
- }
- if (*command >= 0 && *command < NR_ITEMS(qic117_cmds) &&
- qic117_cmds[*command].name != NULL) {
- TRACEx1(-1, "... caused by command \'%s\'",
- qic117_cmds[*command].name);
- } else {
- TRACEi(-1, "... caused by unknown command", *command);
- }
- }
- }
- TRACE_EXIT;
- return result;
-}
-
-int ftape_in_error_state(int status)
-{
- TRACE_FUN(8, "ftape_in_error_state");
- int result = 0;
-
- if ((status & QIC_STATUS_READY) && (status & QIC_STATUS_ERROR)) {
- TRACE(2, "warning: error status set!");
- result = 1;
- }
- TRACE_EXIT;
- return result;
-}
-
-static int ftape_report_configuration(qic_model * model, int *rate,
- int *qic_std, int *tape_len)
-{
- int result;
- int config;
- int status;
-
- TRACE_FUN(8, "ftape_report_configuration");
- result = ftape_report_operation(&config, QIC_REPORT_DRIVE_CONFIGURATION, 8);
- if (result < 0) {
- *model = prehistoric;
- *rate = QIC_CONFIG_RATE_500;
- *qic_std = QIC_TAPE_QIC40;
- *tape_len = 205;
- result = 0;
- } else {
- *rate = (config & QIC_CONFIG_RATE_MASK) >> QIC_CONFIG_RATE_SHIFT;
- result = ftape_report_operation(&status, QIC_REPORT_TAPE_STATUS, 8);
- if (result < 0) {
- /* pre- QIC117 rev C spec. drive, QIC_CONFIG_80 bit is valid.
- */
- *qic_std = (config & QIC_CONFIG_80) ? QIC_TAPE_QIC80 : QIC_TAPE_QIC40;
- *tape_len = (config & QIC_CONFIG_LONG) ? 307 : 205;
- *model = pre_qic117c;
- result = 0;
- } else {
- *model = post_qic117b;
- TRACEx1(8, "report tape status result = %02x", status);
- /* post- QIC117 rev C spec. drive, QIC_CONFIG_80 bit is invalid.
- */
- switch (status & QIC_TAPE_STD_MASK) {
- case QIC_TAPE_QIC40:
- case QIC_TAPE_QIC80:
- case QIC_TAPE_QIC3020:
- case QIC_TAPE_QIC3010:
- *qic_std = status & QIC_TAPE_STD_MASK;
- break;
- default:
- *qic_std = -1;
- break;
- }
- switch (status & QIC_TAPE_LEN_MASK) {
- case QIC_TAPE_205FT:
- /* Unfortunately the new QIC-117 rev G standard shows
- * no way to discriminate between 205 and 425 ft tapes.
- * The obvious way seems not to be used: the QIC_CONFIG_LONG
- * bit isn't used for this (on all drives ?).
- */
- if (config & QIC_CONFIG_LONG) {
- *tape_len = 425; /* will this ever execute ??? */
- } else {
- *tape_len = 0; /* length unknown: 205 or 425 ft. */
- }
- break;
- case QIC_TAPE_307FT:
- *tape_len = 307;
- break;
- case QIC_TAPE_400FT:
- /*
- * Trouble! Iomega Ditto 800 and Conner TST800R drives reports
- * 400ft for 750ft tapes. Yuck, yuck, yuck. Since the value
- * is only used to compute a timeout value, the largest of the
- * two is used.
- */
- *tape_len = 750; /* either 400 or 750 ft. */
- break;
- case QIC_TAPE_1100FT:
- *tape_len = 1100;
- break;
- case QIC_TAPE_FLEX:
- *tape_len = 0;
- break;
- default:
- *tape_len = -1;
- break;
- }
- if (*qic_std == -1 || *tape_len == -1) {
- TRACE(2, "post qic-117b spec drive with unknown tape");
- result = -EIO;
- } else {
- result = 0;
- }
- }
- }
- TRACE_EXIT;
- return (result < 0) ? -EIO : 0;
-}
-
-int ftape_report_rom_version(int *version)
-{
- int result;
-
- result = ftape_report_operation(version, QIC_REPORT_ROM_VERSION, 8);
- return (result < 0) ? -EIO : 0;
-}
-
-int ftape_report_signature(int *signature)
-{
- int result;
-
- result = ftape_command(28);
- result = ftape_report_operation(signature, 9, 8);
- result = ftape_command(30);
- return (result < 0) ? -EIO : 0;
-}
-
-void ftape_report_vendor_id(unsigned int *id)
-{
- TRACE_FUN(8, "ftape_report_vendor_id");
- int result;
-
- /*
- * We'll try to get a vendor id from the drive.
- * First according to the QIC-117 spec, a 16-bit id is requested.
- * If that fails we'll try an 8-bit version, otherwise we'll try
- * an undocumented query.
- */
- result = ftape_report_operation((int *) id, QIC_REPORT_VENDOR_ID, 16);
- if (result < 0) {
- result = ftape_report_operation((int *) id, QIC_REPORT_VENDOR_ID, 8);
- if (result < 0) {
- /* The following is an undocumented call found in the CMS code.
- */
- result = ftape_report_operation((int *) id, 24, 8);
- if (result < 0) {
- *id = UNKNOWN_VENDOR;
- } else {
- TRACEx1(4, "got old 8 bit id: %04x", *id);
- *id |= 0x20000;
- }
- } else {
- TRACEx1(4, "got 8 bit id: %04x", *id);
- *id |= 0x10000;
- }
- } else {
- TRACEx1(4, "got 16 bit id: %04x", *id);
- }
- if (*id == 0x0047) {
- int version;
- int sign;
-
- result = ftape_report_rom_version(&version);
- if (result < 0) {
- TRACE(-1, "report rom version failed");
- TRACE_EXIT;
- return;
- }
- TRACEx1(4, "CMS rom version: %d", version);
- ftape_command(QIC_ENTER_DIAGNOSTIC_1);
- ftape_command(QIC_ENTER_DIAGNOSTIC_1);
- result = ftape_report_operation(&sign, 9, 8);
- if (result < 0) {
- int error, command;
-
- ftape_report_error(&error, &command, 1);
- ftape_command(QIC_ENTER_PRIMARY_MODE);
- TRACE_EXIT;
- return; /* faalt hier ! */
- } else {
- TRACEx1(4, "CMS signature: %02x", sign);
- }
- if (sign == 0xa5) {
- result = ftape_report_operation(&sign, 37, 8);
- if (result < 0) {
- if (version >= 63) {
- *id = 0x8880;
- TRACE(4, "This is an Iomega drive !");
- } else {
- *id = 0x0047;
- TRACE(4, "This is a real CMS drive !");
- }
- } else {
- *id = 0x0047;
- TRACEx1(4, "CMS status: %d", sign);
- }
- } else {
- *id = UNKNOWN_VENDOR;
- }
- ftape_command(QIC_ENTER_PRIMARY_MODE);
- }
- TRACE_EXIT;
-}
-
-void ftape_set_rate_test(int *supported)
-{
- TRACE_FUN(8, "ftape_set_rate_test");
- int error;
- int command;
- int i;
- int result;
- int status;
-
- /* Check if the drive does support the select rate command by testing
- * all different settings.
- * If any one is accepted we assume the command is supported, else not.
- */
- *supported = 0;
- for (i = 0; i < NR_ITEMS(rates); ++i) {
- result = ftape_command(QIC_SELECT_RATE);
- if (result >= 0) {
- result = ftape_parameter_wait(rates[i].drive_code,
- 1 * SECOND, &status);
- if (result >= 0) {
- if (status & QIC_STATUS_ERROR) {
- result = ftape_report_error(&error, &command, 0);
- } else {
- *supported = 1; /* did accept a request */
- }
- }
- }
- }
- TRACEx1(4, "Select Rate command is%s supported",
- *supported ? "" : " not");
- TRACE_EXIT;
-}
-
-int ftape_set_data_rate(int new_rate)
-{
- TRACE_FUN(8, "ftape_set_data_rate");
- int status;
- int result;
- int data_rate;
- qic_model model;
- int supported;
- static int first_time = 1;
-
- if (first_time) {
- ftape_set_rate_test(&supported);
- first_time = 0;
- }
- if (rates[new_rate].fdc_code == -1) {
- TRACEx1(4, "%sb/s data rate not supported by the fdc",
- rates[new_rate].text);
- result = -EINVAL;
- } else {
- int error = 0;
- int command;
-
- result = ftape_command(QIC_SELECT_RATE);
- if (result >= 0) {
- result = ftape_parameter_wait(rates[new_rate].drive_code,
- 1 * SECOND, &status);
- result = ftape_report_raw_drive_status(&status);
- if (result >= 0 && (status & QIC_STATUS_ERROR)) {
- result = ftape_report_error(&error, &command, 0);
- if (result >= 0 && supported &&
- error == 31 && command == QIC_SELECT_RATE) {
- result = -EINVAL;
- }
- }
- }
- if (result >= 0) {
- result = ftape_report_configuration(&model, &data_rate,
- &qic_std, &tape_len);
- if (result >= 0 && data_rate != rates[new_rate].drive_code) {
- result = -EINVAL;
- }
- }
- if (result < 0) {
- TRACEx1(4, "could not set %sb/s data rate", rates[new_rate].text);
- } else {
- TRACEx2(2, "%s drive @ %sb/s",
- (model == prehistoric) ? "prehistoric" :
- ((model == pre_qic117c) ? "pre QIC-117C" :
- ((model == post_qic117b) ? "post QIC-117B" : "post QIC-117D")),
- rates[new_rate].text);
- if (tape_len == 0) {
- TRACEx1(2, "unknown length QIC-%s tape",
- (qic_std == QIC_TAPE_QIC40) ? "40" :
- ((qic_std == QIC_TAPE_QIC80) ? "80" :
- ((qic_std == QIC_TAPE_QIC3010) ? "3010" : "3020")));
- } else {
- TRACEx2(2, "%d ft. QIC-%s tape",
- tape_len,
- (qic_std == QIC_TAPE_QIC40) ? "40" :
- ((qic_std == QIC_TAPE_QIC80) ? "80" :
- ((qic_std == QIC_TAPE_QIC3010) ? "3010" : "3020")));
- }
- /*
- * Set data rate and write precompensation as specified:
- *
- * | QIC-40/80 | QIC-3010/3020
- * rate | precomp | precomp
- * ----------+-------------+--------------
- * 250 Kbps. | 250 ns. | 0 ns.
- * 500 Kbps. | 125 ns. | 0 ns.
- * 1 Mbps. | 42 ns. | 0 ns.
- * 2 Mbps | N/A | 0 ns.
- */
- if (qic_std == QIC_TAPE_QIC40 || qic_std == QIC_TAPE_QIC80) {
- fdc_set_write_precomp(rates[new_rate].precomp);
- } else {
- fdc_set_write_precomp(0);
- }
- fdc_set_data_rate(rates[new_rate].fdc_code);
- ftape_data_rate = new_rate; /* store rate set */
- }
- }
- if (result < 0 && result != -EINVAL) {
- result = -EIO;
- }
- TRACE_EXIT;
- return result;
-}
-
-/* Seek the head to the specified track.
- */
-int ftape_seek_head_to_track(int track)
-{
- TRACE_FUN(8, "ftape_seek_head_to_track");
- int status;
- int result;
-
- location.track = -1; /* remains set in case of error */
- if (track < 0 || track >= tracks_per_tape) {
- TRACE(-1, "track out of bounds");
- result = -EINVAL;
- } else {
- TRACEx1(5, "seeking track %d", track);
- result = ftape_command(QIC_SEEK_HEAD_TO_TRACK);
- if (result < 0) {
- TRACE(1, "ftape_command failed");
- } else {
- result = ftape_parameter_wait(track, timeout.head_seek, &status);
- if (result < 0) {
- TRACE(1, "ftape_parameter_wait failed");
- } else {
- location.track = track;
- might_be_off_track = 0;
- }
- }
- }
- TRACE_EXIT;
- return result;
-}
-
-int ftape_wakeup_drive(wake_up_types method)
-{
- TRACE_FUN(8, "ftape_wakeup_drive");
- int result;
- int status;
- int motor_on = 0;
-
- switch (method) {
- case wake_up_colorado:
- result = ftape_command(QIC_PHANTOM_SELECT);
- if (result == 0) {
- result = ftape_parameter( /* unit */ 0);
- }
- break;
- case wake_up_mountain:
- result = ftape_command(QIC_SOFT_SELECT);
- if (result == 0) {
- ftape_sleep(MILLISECOND); /* NEEDED */
- result = ftape_parameter(18);
- }
- break;
- case wake_up_insight:
- ftape_sleep(100 * MILLISECOND);
- motor_on = 1;
- fdc_motor(motor_on); /* enable is done by motor-on */
- case no_wake_up:
- result = 0;
- break;
- default:
- result = -ENODEV; /* unknown wakeup method */
- }
- /* If wakeup succeeded we shouldn't get an error here..
- */
- if (result == 0) {
- result = ftape_report_raw_drive_status(&status);
- if (result < 0 && motor_on) {
- fdc_motor(0); /* motor off if failed */
- }
- }
- TRACE_EXIT;
- return result;
-}
-
-int ftape_put_drive_to_sleep(vendor_struct drive_type)
-{
- TRACE_FUN(8, "ftape_put_drive_to_sleep");
- int result;
-
- switch (drive_type.wake_up) {
- case wake_up_colorado:
- result = ftape_command(QIC_PHANTOM_DESELECT);
- break;
- case wake_up_mountain:
- result = ftape_command(QIC_SOFT_DESELECT);
- break;
- case wake_up_insight:
- fdc_motor(0); /* enable is done by motor-on */
- case no_wake_up: /* no wakeup / no sleep ! */
- result = 0;
- break;
- default:
- result = -ENODEV; /* unknown wakeup method */
- }
- TRACE_EXIT;
- return result;
-}
-
-int ftape_reset_drive(void)
-{
- TRACE_FUN(8, "ftape_reset_drive");
- int result = 0;
- int status;
- int err_code;
- int err_command;
- int i;
-
- /* We want to re-establish contact with our drive.
- * Fire a number of reset commands (single step pulses)
- * and pray for success.
- */
- for (i = 0; i < 2; ++i) {
- TRACE(5, "Resetting fdc");
- fdc_reset();
- ftape_sleep(10 * MILLISECOND);
- TRACE(5, "Reset command to drive");
- result = ftape_command(QIC_RESET);
- if (result == 0) {
- ftape_sleep(1 * SECOND); /* drive not accessible during 1 second */
- TRACE(5, "Re-selecting drive");
- /* Strange, the QIC-117 specs don't mention this but the
- * drive gets deselected after a soft reset !
- * So we need to enable it again.
- */
- result = ftape_wakeup_drive(drive_type.wake_up);
- if (result < 0) {
- TRACE(1, "Wakeup failed !");
- }
- TRACE(5, "Waiting until drive gets ready");
- result = ftape_ready_wait(timeout.reset, &status);
- if (result == 0 && status & QIC_STATUS_ERROR) {
- result = ftape_report_error(&err_code, &err_command, 1);
- if (result == 0 && err_code == 27) {
- /* Okay, drive saw reset command and responded as it should
- */
- break;
- } else {
- result = -EIO;
- }
- } else {
- result = -EIO;
- }
- }
- if (current->signal & _DONT_BLOCK) {
- TRACE(1, "aborted by non-blockable signal");
- result = -EINTR;
- break; /* exit on signal */
- }
- }
- if (result != 0) {
- TRACE(1, "General failure to reset tape drive");
- } else {
- /* Restore correct settings
- */
- ftape_set_data_rate(ftape_data_rate); /* keep original rate */
- }
- TRACE_EXIT;
- return result;
-}
diff --git a/drivers/char/ftape/ftape-io.h b/drivers/char/ftape/ftape-io.h
deleted file mode 100644
index 5e105125d..000000000
--- a/drivers/char/ftape/ftape-io.h
+++ /dev/null
@@ -1,77 +0,0 @@
-#ifndef _FTAPE_IO_H
-#define _FTAPE_IO_H
-
-/*
- * Copyright (C) 1993-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING. If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- *
- $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-io.h,v $
- $Author: bas $
- *
- $Revision: 1.36 $
- $Date: 1995/05/06 16:11:53 $
- $State: Beta $
- *
- * This file contains definitions for the glue part
- * of the QIC-40/80 floppy-tape driver for Linux.
- */
-
-#include "vendors.h"
-
-typedef struct {
- unsigned seek;
- unsigned reset;
- unsigned rewind;
- unsigned head_seek;
- unsigned stop;
- unsigned pause;
-} timeout_table;
-
-/*
- * ftape-io.c defined global vars.
- */
-extern timeout_table timeout;
-extern int qic_std;
-extern int tape_len;
-extern volatile int current_command;
-extern const struct qic117_command_table qic117_cmds[];
-extern int might_be_off_track;
-
-/*
- * ftape-io.c defined global functions.
- */
-extern void udelay(int usecs);
-extern int udelay_calibrate(void);
-extern void ftape_sleep(unsigned int time);
-extern void ftape_report_vendor_id(unsigned int *id);
-extern int ftape_command(int command);
-extern int ftape_command_wait(int command, int timeout, int *status);
-extern int ftape_report_drive_status(int *status);
-extern int ftape_report_raw_drive_status(int *status);
-extern int ftape_report_status(int *status);
-extern int ftape_interrupt_wait(int time);
-extern int ftape_ready_wait(int timeout, int *status);
-extern int ftape_seek_head_to_track(int track);
-extern int ftape_parameter(int command);
-extern int ftape_in_error_state(int status);
-extern int ftape_set_data_rate(int rate);
-extern int ftape_report_error(int *error, int *command, int report);
-extern int ftape_reset_drive(void);
-extern int ftape_put_drive_to_sleep(vendor_struct drive_type);
-extern int ftape_wakeup_drive(wake_up_types method);
-
-#endif
diff --git a/drivers/char/ftape/ftape-read.c b/drivers/char/ftape/ftape-read.c
deleted file mode 100644
index 23fdab945..000000000
--- a/drivers/char/ftape/ftape-read.c
+++ /dev/null
@@ -1,677 +0,0 @@
-/*
- * Copyright (C) 1993-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING. If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-read.c,v $
- $Author: bas $
- *
- $Revision: 1.30 $
- $Date: 1995/05/27 08:54:21 $
- $State: Beta $
- *
- * This file contains the reading code
- * for the QIC-117 floppy-tape driver for Linux.
- */
-
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/mm.h>
-#include <linux/ftape.h>
-#include <asm/uaccess.h>
-
-#include "tracing.h"
-#include "ftape-read.h"
-#include "qic117.h"
-#include "ftape-io.h"
-#include "ftape-ctl.h"
-#include "ftape-rw.h"
-#include "ftape-write.h"
-#include "ftape-eof.h"
-#include "ecc.h"
-#include "ftape-bsm.h"
-
-/* Global vars.
- */
-
-/* Local vars.
- */
-int buf_pos_rd = 0;
-int buf_len_rd = 0;
-
-void ftape_zap_read_buffers(void)
-{
- int i;
-
- for (i = 0; i < NR_BUFFERS; ++i) {
- /*
- * changed to "fit" with dynamic allocation of tape_buffer. --khp
- */
- buffer[i].address = tape_buffer[i];
- buffer[i].status = waiting;
- buffer[i].bytes = 0;
- buffer[i].skip = 0;
- buffer[i].retry = 0;
- }
- buf_len_rd = 0;
- buf_pos_rd = 0;
- eof_mark = 0;
- ftape_state = idle;
-}
-
-static unsigned long convert_sector_map(buffer_struct * buff)
-{
- TRACE_FUN(8, "convert_sector_map");
- int i = 0;
- unsigned long bad_map = get_bad_sector_entry(buff->segment_id);
- unsigned long src_map = buff->soft_error_map | buff->hard_error_map;
- unsigned long dst_map = 0;
-
- if (bad_map || src_map) {
- TRACEx1(5, "bad_map = 0x%08lx", bad_map);
- TRACEx1(5, "src_map = 0x%08lx", src_map);
- }
- while (bad_map) {
- while ((bad_map & 1) == 0) {
- if (src_map & 1) {
- dst_map |= (1 << i);
- }
- src_map >>= 1;
- bad_map >>= 1;
- ++i;
- }
- /* (bad_map & 1) == 1 */
- src_map >>= 1;
- bad_map >>= 1;
- }
- if (src_map) {
- dst_map |= (src_map << i);
- }
- if (dst_map) {
- TRACEx1(5, "dst_map = 0x%08lx", dst_map);
- }
- TRACE_EXIT;
- return dst_map;
-}
-
-int correct_and_copy(unsigned int tail, byte * destination)
-{
- TRACE_FUN(8, "correct_and_copy");
- struct memory_segment mseg;
- int result;
- BAD_SECTOR read_bad;
-
- mseg.read_bad = convert_sector_map(&buffer[tail]);
- mseg.marked_bad = 0; /* not used... */
- mseg.blocks = buffer[tail].bytes / SECTOR_SIZE;
- mseg.data = buffer[tail].address;
- /* If there are no data sectors we can skip this segment.
- */
- if (mseg.blocks <= 3) {
- TRACE(4, "empty segment");
- TRACE_EXIT;
- return 0;
- }
- read_bad = mseg.read_bad;
- history.crc_errors += count_ones(read_bad);
- result = ecc_correct_data(&mseg);
- if (read_bad != 0 || mseg.corrected != 0) {
- TRACElx(4, "crc error map:", read_bad);
- TRACElx(4, "corrected map:", mseg.corrected);
- history.corrected += count_ones(mseg.corrected);
- }
- if (result == ECC_CORRECTED || result == ECC_OK) {
- if (result == ECC_CORRECTED) {
- TRACEi(3, "ecc corrected segment:", buffer[tail].segment_id);
- }
- memcpy(destination, mseg.data, (mseg.blocks - 3) * SECTOR_SIZE);
- if ((read_bad ^ mseg.corrected) & mseg.corrected) {
- /* sectors corrected without crc errors set */
- history.crc_failures++;
- }
- TRACE_EXIT;
- return (mseg.blocks - 3) * SECTOR_SIZE;
- } else {
- TRACEi(1, "ecc failure on segment", buffer[tail].segment_id);
- history.ecc_failures++;
- TRACE_EXIT;
- return -EAGAIN; /* should retry */
- }
- TRACE_EXIT;
- return 0;
-}
-
-/* Read given segment into buffer at address.
- */
-int read_segment(unsigned segment_id, byte * address, int *eof_mark,
- int read_ahead)
-{
- TRACE_FUN(5, "read_segment");
- int read_done = 0;
- int result = 0;
- int bytes_read = 0;
- int retry = 0;
-
- TRACEi(5, "segment_id =", segment_id);
- if (ftape_state != reading) {
- if (ftape_state == writing) {
- ftape_flush_buffers(); /* flush write buffer */
- TRACE(5, "calling ftape_abort_operation");
- result = ftape_abort_operation();
- if (result < 0) {
- TRACE(1, "ftape_abort_operation failed");
- TRACE_EXIT;
- return -EIO;
- }
- } else {
- /* clear remaining read buffers */
- ftape_zap_read_buffers();
- }
- ftape_state = reading;
- }
- if (segment_id >= segments_per_track * tracks_per_tape) {
- TRACE(5, "reading past end of tape");
- TRACE_EXIT;
- return -ENOSPC;
- }
- for (;;) {
- /* Search all full buffers for the first matching the wanted segment.
- * Clear other buffers on the fly.
- */
- while (!read_done && buffer[tail].status == done) {
- if (buffer[tail].segment_id == segment_id) {
- unsigned eof_sector;
- unsigned sector_count = 0;
- unsigned long bsm = get_bad_sector_entry(segment_id);
- int i;
-
- /* If out buffer is already full, return its contents.
- */
- if (buffer[tail].deleted) {
- TRACEi(5, "found segment in cache :", segment_id);
- TRACE_EXIT;
- /* Return a value that read_header_segment understands.
- * As this should only occur when searching for the header
- * segments it shouldn't be misinterpreted elsewhere.
- */
- return 0;
- }
- TRACEi(5, "found segment in cache :", segment_id);
- eof_sector = check_for_eof(segment_id);
- if (eof_sector > 0) {
- TRACEi(5, "end of file mark in sector:", eof_sector);
- for (i = 1; i < eof_sector; ++i) {
- if ((bsm & 1) == 0) {
- ++sector_count;
- }
- bsm >>= 1;
- }
- *eof_mark = 1;
- }
- if (eof_sector != 1) { /* not found or gt 1 */
- result = correct_and_copy(tail, address);
- TRACEi(5, "segment contains (bytes) :", result);
- if (result < 0) {
- if (result != -EAGAIN) {
- TRACE_EXIT;
- return result;
- }
- /* keep read_done == 0, will trigger ftape_abort_operation
- * because reading wrong segment.
- */
- TRACE(1, "ecc failed, retry");
- ++retry;
- } else {
- read_done = 1;
- }
- } else {
- read_done = 1;
- }
- if (eof_sector > 0) {
- bytes_read = sector_count * SECTOR_SIZE;
- TRACEi(5, "partial read count:", bytes_read);
- } else {
- bytes_read = result;
- }
- } else {
- TRACEi(5, "zapping segment in cache :", buffer[tail].segment_id);
- }
- buffer[tail].status = waiting;
- next_buffer(&tail);
- }
- if (!read_done && buffer[tail].status == reading) {
- if (buffer[tail].segment_id == segment_id) {
- int result = wait_segment(reading);
- if (result < 0) {
- if (result == -EINTR) {
- TRACE_EXIT;
- return result;
- }
- TRACE(1, "wait_segment failed while reading");
- ftape_abort_operation();
- }
- } else {
- /* We're reading the wrong segment, stop runner.
- */
- ftape_abort_operation();
- }
- }
- /* if just passed the last segment on a track, wait for BOT or EOT mark.
- */
- if (runner_status == logical_eot) {
- int status;
- result = ftape_ready_wait(timeout.seek, &status);
- if (result < 0) {
- TRACE(1, "ftape_ready_wait waiting for eot/bot failed");
- }
- if ((status & (QIC_STATUS_AT_BOT | QIC_STATUS_AT_EOT)) == 0) {
- TRACE(1, "eot/bot not reached");
- }
- runner_status = end_of_tape;
- }
- /* should runner stop ?
- */
- if (runner_status == aborting || runner_status == buffer_overrun ||
- runner_status == end_of_tape) {
- if (runner_status != end_of_tape &&
- !(runner_status == aborting && !tape_running)) {
- ftape_dumb_stop();
- }
- if (runner_status == aborting) {
- if (buffer[head].status == reading || buffer[head].status == error) {
- if (buffer[head].status == error) {
- history.defects += count_ones(buffer[head].hard_error_map);
- }
- buffer[head].status = waiting;
- }
- }
- runner_status = idle; /* aborted ? */
- }
- /* If segment to read is empty, do not start runner for it,
- * but wait for next read call.
- */
- if (get_bad_sector_entry(segment_id) == EMPTY_SEGMENT) {
- bytes_read = 0; /* flag empty segment */
- read_done = 1;
- }
- /* Allow escape from this loop on signal !
- */
- if (current->signal & _DONT_BLOCK) {
- TRACE(2, "interrupted by non-blockable signal");
- TRACE_EXIT;
- return -EINTR;
- }
- /* If we got a segment: quit, or else retry up to limit.
- */
- if (read_done) {
- break;
- }
- if (retry > RETRIES_ON_ECC_ERROR) {
- history.defects++;
- TRACE(1, "too many retries on ecc failure");
- TRACE_EXIT;
- return -ENODATA;
- }
- /* Now at least one buffer is empty !
- * Restart runner & tape if needed.
- */
- TRACEx3(8, "head: %d, tail: %d, runner_status: %d",
- head, tail, runner_status);
- TRACEx2(8, "buffer[].status, [head]: %d, [tail]: %d",
- buffer[head].status, buffer[tail].status);
- if (buffer[tail].status == waiting) {
- setup_new_segment(&buffer[head], segment_id, -1);
- if (!read_ahead) {
- buffer[head].next_segment = 0; /* disable read-ahead */
- }
- calc_next_cluster(&buffer[head]);
- if (runner_status == idle) {
- result = ftape_start_tape(segment_id,
- buffer[head].sector_offset);
- if (result < 0) {
- TRACEx1(1, "Error: segment %d unreachable", segment_id);
- TRACE_EXIT;
- return result;
- }
- runner_status = running;
- }
- buffer[head].status = reading;
- setup_fdc_and_dma(&buffer[head], FDC_READ);
- }
- }
- if (read_done) {
- TRACE_EXIT;
- return bytes_read;
- } else {
- TRACE(1, "too many retries");
- TRACE_EXIT;
- return -EIO;
- }
-}
-
-int read_header_segment(byte * address)
-{
- TRACE_FUN(5, "read_header_segment");
- int i;
- int result;
- int header_segment = -1;
- unsigned int max_floppy_side;
- unsigned int max_floppy_track;
- unsigned int max_floppy_sector;
- int first_failed = 0;
- int status;
- int new_tape_len;
-
- result = ftape_report_drive_status(&status);
- if (result < 0) {
- TRACE(1, "error: error_status or report failure");
- TRACE_EXIT;
- return -EIO;
- }
- TRACE(5, "reading...");
- ftape_last_segment.id = 68; /* will allow us to read the header ! */
- /* We're looking for the first header segment.
- * A header segment cannot contain bad sectors, therefor at the
- * tape start, segments with bad sectors are (according to QIC-40/80)
- * written with deleted data marks and must be skipped.
- */
- used_header_segment = -1;
- result = 0;
- for (header_segment = 0;
- header_segment < ftape_last_segment.id && result == 0;
- ++header_segment) {
- /* Set no read-ahead, the isr will force read-ahead whenever
- * it encounters deleted data !
- */
- result = read_segment(header_segment, address, &status, 0);
- if (result < 0 && !first_failed) {
- TRACE(1, "header segment damaged, trying backup");
- first_failed = 1;
- result = 0; /* force read of next (backup) segment */
- }
- }
- if (result < 0 || header_segment >= ftape_last_segment.id) {
- TRACE(1, "no readable header segment found");
- TRACE_EXIT;
- return -EIO;
- }
- result = ftape_abort_operation();
- if (result < 0) {
- TRACE(1, "ftape_abort_operation failed");
- TRACE_EXIT;
- return -EIO;
- }
- if (GET4(address, 0) != 0xaa55aa55) {
- TRACE(1, "wrong signature in header segment");
- TRACE_EXIT;
- return -EIO;
- }
- header_segment_1 = GET2(address, 6);
- header_segment_2 = GET2(address, 8);
- TRACEx2(2, "header segments are %d and %d",
- header_segment_1, header_segment_2);
- used_header_segment = (first_failed) ? header_segment_2 : header_segment_1;
-
- /* Verify tape parameters...
- * QIC-40/80 spec: tape_parameters:
- *
- * segments-per-track segments_per_track
- * tracks-per-cartridge tracks_per_tape
- * max-floppy-side (segments_per_track *
- * tracks_per_tape - 1) /
- * segments_per_head
- * max-floppy-track segments_per_head /
- * segments_per_cylinder - 1
- * max-floppy-sector segments_per_cylinder *
- * SECTORS_PER_SEGMENT
- */
- format_code = (format_type) * (address + 4);
- segments_per_track = GET2(address, 24);
- tracks_per_tape = *(address + 26);
- max_floppy_side = *(address + 27);
- max_floppy_track = *(address + 28);
- max_floppy_sector = *(address + 29);
- TRACEx6(4, "(fmt/spt/tpc/fhm/ftm/fsm) = %d/%d/%d/%d/%d/%d",
- format_code, segments_per_track, tracks_per_tape,
- max_floppy_side, max_floppy_track, max_floppy_sector);
- new_tape_len = tape_len;
- switch (format_code) {
- case fmt_425ft:
- new_tape_len = 425;
- break;
- case fmt_normal:
- if (tape_len == 0) { /* otherwise 307 ft */
- new_tape_len = 205;
- }
- break;
- case fmt_1100ft:
- new_tape_len = 1100;
- break;
- case fmt_wide:{
- int segments_per_1000_inch = 1; /* non-zero default for switch */
- switch (qic_std) {
- case QIC_TAPE_QIC40:
- segments_per_1000_inch = 332;
- break;
- case QIC_TAPE_QIC80:
- segments_per_1000_inch = 488;
- break;
- case QIC_TAPE_QIC3010:
- segments_per_1000_inch = 730;
- break;
- case QIC_TAPE_QIC3020:
- segments_per_1000_inch = 1430;
- break;
- }
- new_tape_len = (1000 * segments_per_track +
- (segments_per_1000_inch - 1)) / segments_per_1000_inch;
- break;
- }
- default:
- TRACE(1, "unknown tape format, please report !");
- TRACE_EXIT;
- return -EIO;
- }
- if (new_tape_len != tape_len) {
- tape_len = new_tape_len;
- TRACEx1(1, "calculated tape length is %d ft", tape_len);
- ftape_calc_timeouts();
- }
- if (segments_per_track == 0 && tracks_per_tape == 0 &&
- max_floppy_side == 0 && max_floppy_track == 0 &&
- max_floppy_sector == 0) {
- /* QIC-40 Rev E and earlier has no values in the header.
- */
- segments_per_track = 68;
- tracks_per_tape = 20;
- max_floppy_side = 1;
- max_floppy_track = 169;
- max_floppy_sector = 128;
- }
- /* This test will compensate for the wrong parameter on tapes
- * formatted by Conner software.
- */
- if (segments_per_track == 150 &&
- tracks_per_tape == 28 &&
- max_floppy_side == 7 &&
- max_floppy_track == 149 &&
- max_floppy_sector == 128) {
- TRACE(-1, "the famous CONNER bug: max_floppy_side off by one !");
- max_floppy_side = 6;
- }
- /* This test will compensate for the wrong parameter on tapes
- * formatted by Colorado Windows software.
- */
- if (segments_per_track == 150 &&
- tracks_per_tape == 28 &&
- max_floppy_side == 6 &&
- max_floppy_track == 150 &&
- max_floppy_sector == 128) {
- TRACE(-1, "the famous Colorado bug: max_floppy_track off by one !");
- max_floppy_track = 149;
- }
- segments_per_head = ((max_floppy_sector / SECTORS_PER_SEGMENT) *
- (max_floppy_track + 1));
- /*
- * Verify drive_configuration with tape parameters
- */
- if (segments_per_head == 0 || segments_per_cylinder == 0 ||
- ((segments_per_track * tracks_per_tape - 1) / segments_per_head
- != max_floppy_side) ||
- (segments_per_head / segments_per_cylinder - 1 != max_floppy_track) ||
- (segments_per_cylinder * SECTORS_PER_SEGMENT != max_floppy_sector)
-#ifdef TESTING
- || (format_code == 4 && (max_floppy_track != 254 || max_floppy_sector != 128))
-#endif
- ) {
- TRACE(1, "Tape parameters inconsistency, please report");
- TRACE_EXIT;
- return -EIO;
- }
- first_data_segment = GET2(address, 10); /* first data segment */
- TRACEi(4, "first data segment:", first_data_segment);
- extract_bad_sector_map(address);
- /* Find the highest segment id that allows still one full
- * deblock_buffer to be written to tape.
- */
- ftape_last_segment.size = 0;
- for (i = segments_per_track * tracks_per_tape - 1; i >= 0; --i) {
- int space = SECTORS_PER_SEGMENT - 3 - count_ones(get_bad_sector_entry(i));
- if (space > 0) {
- ftape_last_segment.size += space; /* sectors free */
- ftape_last_segment.free = (ftape_last_segment.size -
- sizeof(deblock_buffer) / SECTOR_SIZE);
- if (ftape_last_segment.free >= 0) {
- ftape_last_segment.id = i;
- TRACEx2(4, "`last' segment is %d, %d Kb",
- ftape_last_segment.id, ftape_last_segment.size);
- break;
- }
- }
- }
- /* Copy the failed sector log into our local buffer.
- */
- if (!ftape_validate_label(&deblock_buffer[30])) {
- TRACE(-1, "This tape has no `Linux raw format' label,\n"
- "***** Use `mt' to erase this tape if you want to use file marks !");
- } else {
- extract_file_marks(address);
- }
- ftape_reset_position();
- TRACE_EXIT;
- return 0;
-}
-
-int _ftape_read(char *buff, int req_len)
-{
- TRACE_FUN(5, "_ftape_read");
- int result = 0;
- int cnt;
- int to_do = req_len;
- static int remaining;
- int bytes_read = 0;
-
- if (ftape_offline || !formatted || no_tape) {
- TRACEx3(-1, "offline = %d, formatted = %d, no_tape = %d",
- ftape_offline, formatted, no_tape);
- result = -EIO;
- } else {
- history.used |= 1;
- if (first_data_segment == -1) {
- result = read_header_segment(deblock_buffer);
- }
- }
- if (result < 0) {
- TRACE_EXIT;
- return result;
- }
- /* As GNU tar doesn't accept partial read counts when the multiple
- * volume flag is set, we make sure to return the requested amount
- * of data. Except, of course, at the end of the tape or file mark.
- */
- while (to_do > 0) { /* don't return with a partial count ! */
- /* If we're reading the `last' segment(s) on tape, make sure we don't
- * get more than 29 Kb from it (As it only contains this much).
- * This works only for sequential access, so random access should
- * stay away from this `last' segment.
- * Note: ftape_seg_pos points to the next segment that will be
- * read, so it's one too high here!
- */
- if (!eof_mark && ftape_seg_pos - 1 >= ftape_last_segment.id) {
- TRACEi(5, "remaining of last segment:", remaining);
- if (to_do > remaining) {
- to_do = remaining; /* fake a smaller request */
- TRACE(5, "clipped request to remaining");
- }
- }
- while (!eof_mark && buf_len_rd == 0) {
- /* When starting to read the `last' segment, set remaining
- */
- if (ftape_seg_pos == ftape_last_segment.id) {
- remaining = sizeof(deblock_buffer);
- TRACEi(5, "remaining set to:", remaining);
- }
- result = read_segment(ftape_seg_pos, deblock_buffer, &eof_mark, 1);
- if (result < 0) {
- if (result == -ENODATA) {
- /* Unable to recover tape data, return error and skip bad spot.
- */
- ++ftape_seg_pos;
- }
- TRACEx1(4, "read_segment result: %d", result);
- TRACE_EXIT;
- return result;
- }
- /* Allow escape from this loop on signal !
- */
- if (current->signal & _DONT_BLOCK) {
- TRACE(2, "interrupted by non-blockable signal");
- TRACE_EXIT;
- return -EINTR;
- }
- buf_pos_rd = 0;
- buf_len_rd = result;
- ++ftape_seg_pos;
- }
- /* Take as much as we can use
- */
- cnt = (buf_len_rd < to_do) ? buf_len_rd : to_do;
- TRACEi(7, "nr bytes just read:", cnt);
- if (cnt > 0) {
- result = verify_area(VERIFY_WRITE, buff, cnt);
- if (result) {
- TRACEx1(1, "verify_area failed, exitcode = %d", result);
- TRACE_EXIT;
- return -EIO;
- }
- copy_to_user(buff, deblock_buffer + buf_pos_rd, cnt);
- buff += cnt;
- to_do -= cnt; /* what's left from req_len */
- remaining -= cnt; /* what remains on this tape */
- bytes_read += cnt; /* what we got so far */
- buf_pos_rd += cnt; /* index in buffer */
- buf_len_rd -= cnt; /* remaining bytes in buffer */
- }
- if (eof_mark && buf_len_rd == 0) { /* nothing left */
- TRACE(5, "partial count because of eof mark");
- if (bytes_read == 0) {
- eof_mark = 0; /* no need for mark next read */
- }
- break;
- }
- }
- TRACE_EXIT;
- return bytes_read;
-}
diff --git a/drivers/char/ftape/ftape-rw.c b/drivers/char/ftape/ftape-rw.c
deleted file mode 100644
index f1e0c10ef..000000000
--- a/drivers/char/ftape/ftape-rw.c
+++ /dev/null
@@ -1,953 +0,0 @@
-/*
- * Copyright (C) 1993-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING. If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-rw.c,v $
- $Author: bas $
- *
- $Revision: 1.54 $
- $Date: 1995/05/27 08:55:27 $
- $State: Beta $
- *
- * This file contains some common code for the segment read and segment
- * write routines for the QIC-117 floppy-tape driver for Linux.
- */
-
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/ftape.h>
-
-#include "tracing.h"
-#include "ftape-rw.h"
-#include "fdc-io.h"
-#include "kernel-interface.h"
-#include "qic117.h"
-#include "ftape-io.h"
-#include "ftape-ctl.h"
-#include "ftape-read.h"
-#include "ftape-eof.h"
-#include "ecc.h"
-#include "ftape-bsm.h"
-
-/* Global vars.
- */
-volatile enum runner_status_enum runner_status = idle;
-byte deblock_buffer[(SECTORS_PER_SEGMENT - 3) * SECTOR_SIZE];
-byte scratch_buffer[(SECTORS_PER_SEGMENT - 3) * SECTOR_SIZE];
-buffer_struct buffer[NR_BUFFERS];
-struct wait_queue *wait_intr = NULL;
-volatile int head;
-volatile int tail; /* not volatile but need same type as head */
-int fdc_setup_error;
-ftape_last_segment_struct ftape_last_segment;
-int header_segment_1 = -1;
-int header_segment_2 = -1;
-int used_header_segment = -1;
-location_record location =
-{-1, 0};
-volatile int tape_running = 0;
-format_type format_code;
-
-/* Local vars.
- */
-static int overrun_count_offset = 0;
-static int inhibit_correction = 0;
-
-
-/* Increment cyclic buffer nr.
- */
-buffer_struct *
- next_buffer(volatile int *x)
-{
- if (++*x >= NR_BUFFERS) {
- *x = 0;
- }
- return &buffer[*x];
-}
-
-int valid_segment_no(unsigned segment)
-{
- return (segment >= first_data_segment && segment <= ftape_last_segment.id);
-}
-
-/* Count nr of 1's in pattern.
- */
-int count_ones(unsigned long mask)
-{
- int bits;
-
- for (bits = 0; mask != 0; mask >>= 1) {
- if (mask & 1) {
- ++bits;
- }
- }
- return bits;
-}
-
-/* Calculate Floppy Disk Controller and DMA parameters for a segment.
- * head: selects buffer struct in array.
- * offset: number of physical sectors to skip (including bad ones).
- * count: number of physical sectors to handle (including bad ones).
- */
-static int setup_segment(buffer_struct * buff, unsigned int segment_id,
- unsigned int sector_offset, unsigned int sector_count, int retry)
-{
- TRACE_FUN(8, "setup_segment");
- unsigned long offset_mask;
- unsigned long mask;
-
- buff->segment_id = segment_id;
- buff->sector_offset = sector_offset;
- buff->remaining = sector_count;
- buff->head = segment_id / segments_per_head;
- buff->cyl = (segment_id % segments_per_head) / segments_per_cylinder;
- buff->sect = (segment_id % segments_per_cylinder) * SECTORS_PER_SEGMENT + 1;
- buff->deleted = 0;
- offset_mask = (1 << buff->sector_offset) - 1;
- mask = get_bad_sector_entry(segment_id) & offset_mask;
- while (mask) {
- if (mask & 1) {
- offset_mask >>= 1; /* don't count bad sector */
- }
- mask >>= 1;
- }
- buff->data_offset = count_ones(offset_mask); /* good sectors to skip */
- buff->ptr = buff->address + buff->data_offset * SECTOR_SIZE;
- TRACEx1(5, "data offset = %d sectors", buff->data_offset);
- if (retry) {
- buff->soft_error_map &= offset_mask; /* keep skipped part */
- } else {
- buff->hard_error_map = buff->soft_error_map = 0;
- }
- buff->bad_sector_map = get_bad_sector_entry(buff->segment_id);
- if (buff->bad_sector_map != 0) {
- TRACEx2(4, "segment: %d, bad sector map: %08lx",
- buff->segment_id, buff->bad_sector_map);
- } else {
- TRACEx1(5, "segment: %d", buff->segment_id);
- }
- if (buff->sector_offset > 0) {
- buff->bad_sector_map >>= buff->sector_offset;
- }
- if (buff->sector_offset != 0 || buff->remaining != SECTORS_PER_SEGMENT) {
- TRACEx2(5, "sector offset = %d, count = %d",
- buff->sector_offset, buff->remaining);
- }
- /*
- * Segments with 3 or less sectors are not written with
- * valid data because there is no space left for the ecc.
- * The data written is whatever happens to be in the buffer.
- * Reading such a segment will return a zero byte-count.
- * To allow us to read/write segments with all bad sectors
- * we fake one readable sector in the segment. This prevents
- * having to handle these segments in a very special way.
- * It is not important if the reading of this bad sector
- * fails or not (the data is ignored). It is only read to
- * keep the driver running.
- * The QIC-40/80 spec. has no information on how to handle
- * this case, so this is my interpretation.
- */
- if (buff->bad_sector_map == EMPTY_SEGMENT) {
- TRACE(5, "empty segment, fake first sector good");
- buff->bad_sector_map = FAKE_SEGMENT;
- }
- fdc_setup_error = 0;
- buff->next_segment = segment_id + 1;
- TRACE_EXIT;
- return 0;
-}
-
-/* Calculate Floppy Disk Controller and DMA parameters for a new segment.
- */
-int setup_new_segment(buffer_struct * buff, unsigned int segment_id, int skip)
-{
- TRACE_FUN(5, "setup_new_segment");
- int result = 0;
- static int old_segment_id = -1;
- static int old_ftape_state = idle;
- int retry = 0;
- unsigned offset = 0;
- int count = SECTORS_PER_SEGMENT;
-
- TRACEx3(5, "%s segment %d (old = %d)",
- (ftape_state == reading) ? "reading" : "writing",
- segment_id, old_segment_id);
- if (ftape_state != old_ftape_state) { /* when verifying */
- old_segment_id = -1;
- old_ftape_state = ftape_state;
- }
- if (segment_id == old_segment_id) {
- ++buff->retry;
- ++history.retries;
- TRACEx1(5, "setting up for retry nr %d", buff->retry);
- retry = 1;
- if (skip && buff->skip > 0) { /* allow skip on retry */
- offset = buff->skip;
- count -= offset;
- TRACEx1(5, "skipping %d sectors", offset);
- }
- } else {
- buff->retry = 0;
- buff->skip = 0;
- old_segment_id = segment_id;
- }
- result = setup_segment(buff, segment_id, offset, count, retry);
- TRACE_EXIT;
- return result;
-}
-
-/* Determine size of next cluster of good sectors.
- */
-int calc_next_cluster(buffer_struct * buff)
-{
- /* Skip bad sectors.
- */
- while (buff->remaining > 0 && (buff->bad_sector_map & 1) != 0) {
- buff->bad_sector_map >>= 1;
- ++buff->sector_offset;
- --buff->remaining;
- }
- /* Find next cluster of good sectors
- */
- if (buff->bad_sector_map == 0) { /* speed up */
- buff->sector_count = buff->remaining;
- } else {
- unsigned long map = buff->bad_sector_map;
-
- buff->sector_count = 0;
- while (buff->sector_count < buff->remaining && (map & 1) == 0) {
- ++buff->sector_count;
- map >>= 1;
- }
- }
- return buff->sector_count;
-}
-
-int check_bot_eot(int status)
-{
- TRACE_FUN(5, "check_bot_eot");
-
- if (status & (QIC_STATUS_AT_BOT | QIC_STATUS_AT_EOT)) {
- location.bot = ((location.track & 1) == 0 ?
- (status & QIC_STATUS_AT_BOT) :
- (status & QIC_STATUS_AT_EOT));
- location.eot = !location.bot;
- location.segment = (location.track +
- (location.bot ? 0 : 1)) * segments_per_track - 1;
- location.sector = -1;
- location.known = 1;
- TRACEx1(5, "tape at logical %s", location.bot ? "bot" : "eot");
- TRACEx1(5, "segment = %d", location.segment);
- } else {
- location.known = 0;
- }
- TRACE_EXIT;
- return location.known;
-}
-
-/* Read Id of first sector passing tape head.
- */
-int ftape_read_id(void)
-{
- TRACE_FUN(8, "ftape_read_id");
- int result;
- int status;
- byte out[2];
-
- /* Assume tape is running on entry, be able to handle
- * situation where it stopped or is stopping.
- */
- location.known = 0; /* default is location not known */
- out[0] = FDC_READID;
- out[1] = FTAPE_UNIT;
- result = fdc_command(out, 2);
- if (result < 0) {
- TRACE(1, "fdc_command failed");
- } else {
- result = fdc_interrupt_wait(20 * SECOND);
- if (result == 0) {
- if (fdc_sect == 0) {
- result = ftape_report_drive_status(&status);
- if (result == 0) {
- if (status & QIC_STATUS_READY) {
- tape_running = 0;
- TRACE(5, "tape has stopped");
- check_bot_eot(status);
- if (!location.known) {
- result = -EIO;
- }
- } else {
- /* If read-id failed because of a hard or soft
- * error, return an error. Higher level must retry!
- */
- result = -EIO;
- }
- }
- } else {
- location.known = 1;
- location.segment = (segments_per_head * fdc_head
- + segments_per_cylinder * fdc_cyl
- + (fdc_sect - 1) / SECTORS_PER_SEGMENT);
- location.sector = (fdc_sect - 1) % SECTORS_PER_SEGMENT;
- location.eot =
- location.bot = 0;
- }
- } else if (result == -ETIME) {
- /* Didn't find id on tape, must be near end: Wait until stopped.
- */
- result = ftape_ready_wait(FOREVER, &status);
- if (result >= 0) {
- tape_running = 0;
- TRACE(5, "tape has stopped");
- check_bot_eot(status);
- if (!location.known) {
- result = -EIO;
- }
- }
- } else {
- /* Interrupted or otherwise failing fdc_interrupt_wait()
- */
- TRACE(1, "fdc_interrupt_wait failed :(");
- result = -EIO;
- }
- }
- if (!location.known) {
- TRACE(5, "no id found");
- } else {
- if (location.sector == 0) {
- TRACEx2(5, "passing segment %d/%d", location.segment, location.sector);
- } else {
- TRACEx2(6, "passing segment %d/%d", location.segment, location.sector);
- }
- }
- TRACE_EXIT;
- return result;
-}
-
-static int logical_forward(void)
-{
- tape_running = 1;
- return ftape_command(QIC_LOGICAL_FORWARD);
-}
-
-static int stop_tape(int *pstatus)
-{
- TRACE_FUN(5, "stop_tape");
- int retry = 0;
- int result;
-
- do {
- result = ftape_command_wait(QIC_STOP_TAPE, timeout.stop, pstatus);
- if (result == 0) {
- if ((*pstatus & QIC_STATUS_READY) == 0) {
- result = -EIO;
- } else {
- tape_running = 0;
- }
- }
- } while (result < 0 && ++retry <= 3);
- if (result < 0) {
- TRACE(1, "failed ! (fatal)");
- }
- TRACE_EXIT;
- return result;
-}
-
-int ftape_dumb_stop(void)
-{
- TRACE_FUN(5, "ftape_dumb_stop");
- int result;
- int status;
-
- /* Abort current fdc operation if it's busy (probably read
- * or write operation pending) with a reset.
- */
- result = fdc_ready_wait(100 /* usec */ );
- if (result < 0) {
- TRACE(1, "aborting fdc operation");
- fdc_reset();
- }
- /* Reading id's after the last segment on a track may fail
- * but eventually the drive will become ready (logical eot).
- */
- result = ftape_report_drive_status(&status);
- location.known = 0;
- do {
- if (result == 0 && status & QIC_STATUS_READY) {
- /* Tape is not running any more.
- */
- TRACE(5, "tape already halted");
- check_bot_eot(status);
- tape_running = 0;
- } else if (tape_running) {
- /* Tape is (was) still moving.
- */
-#ifdef TESTING
- ftape_read_id();
-#endif
- result = stop_tape(&status);
- } else {
- /* Tape not yet ready but stopped.
- */
- result = ftape_ready_wait(timeout.pause, &status);
- }
- } while (tape_running);
-#ifndef TESTING
- location.known = 0;
-#endif
- TRACE_EXIT;
- return result;
-}
-
-/* Wait until runner has finished tail buffer.
- */
-int wait_segment(buffer_state_enum state)
-{
- TRACE_FUN(5, "wait_segment");
- int result = 0;
-
- while (buffer[tail].status == state) {
- /* First buffer still being worked on, wait up to timeout.
- */
- result = fdc_interrupt_wait(50 * SECOND);
- if (result < 0) {
- if (result != -EINTR) {
- TRACE(1, "fdc_interrupt_wait failed");
- result = -ETIME;
- }
- break;
- }
- if (fdc_setup_error) {
- TRACE(1, "setup error");
- /* recover... */
- result = -EIO;
- break;
- }
- }
- TRACE_EXIT;
- return result;
-}
-
-/* forward */ static int seek_forward(int segment_id);
-
-int fast_seek(int count, int reverse)
-{
- TRACE_FUN(5, "fast_seek");
- int result = 0;
- int status;
-
- if (count > 0) {
- /* If positioned at begin or end of tape, fast seeking needs
- * special treatment.
- * Starting from logical bot needs a (slow) seek to the first
- * segment before the high speed seek. Most drives do this
- * automatically but some older don't, so we treat them
- * all the same.
- * Starting from logical eot is even more difficult because
- * we cannot (slow) reverse seek to the last segment.
- * TO BE IMPLEMENTED.
- */
- inhibit_correction = 0;
- if (location.known &&
- ((location.bot && !reverse) ||
- (location.eot && reverse))) {
- if (!reverse) {
- /* (slow) skip to first segment on a track
- */
- seek_forward(location.track * segments_per_track);
- --count;
- } else {
- /* When seeking backwards from end-of-tape the number
- * of erased gaps found seems to be higher than expected.
- * Therefor the drive must skip some more segments than
- * calculated, but we don't know how many.
- * Thus we will prevent the re-calculation of offset
- * and overshoot when seeking backwards.
- */
- inhibit_correction = 1;
- count += 3; /* best guess */
- }
- }
- } else {
- TRACEx1(5, "warning: zero or negative count: %d", count);
- }
- if (count > 0) {
- int i;
- int nibbles = count > 255 ? 3 : 2;
-
- if (count > 4095) {
- TRACE(4, "skipping clipped at 4095 segment");
- count = 4095;
- }
- /* Issue this tape command first. */
- if (!reverse) {
- TRACEx1(4, "skipping %d segment(s)", count);
- result = ftape_command(nibbles == 3 ?
- QIC_SKIP_EXTENDED_FORWARD : QIC_SKIP_FORWARD);
- } else {
- TRACEx1(4, "backing up %d segment(s)", count);
- result = ftape_command(nibbles == 3 ?
- QIC_SKIP_EXTENDED_REVERSE : QIC_SKIP_REVERSE);
- }
- if (result < 0) {
- TRACE(4, "Skip command failed");
- } else {
- --count; /* 0 means one gap etc. */
- for (i = 0; i < nibbles; ++i) {
- if (result >= 0) {
- result = ftape_parameter(count & 15);
- count /= 16;
- }
- }
- result = ftape_ready_wait(timeout.rewind, &status);
- if (result >= 0) {
- tape_running = 0;
- }
- }
- }
- TRACE_EXIT;
- return result;
-}
-
-static int validate(int id)
-{
- /* Check to see if position found is off-track as reported once.
- * Because all tracks in one direction lie next to each other,
- * if off-track the error will be approximately 2 * segments_per_track.
- */
- if (location.track == -1) {
- return 1; /* unforeseen situation, don't generate error */
- } else {
- /* Use margin of segments_per_track on both sides because ftape
- * needs some margin and the error we're looking for is much larger !
- */
- int lo = (location.track - 1) * segments_per_track;
- int hi = (location.track + 2) * segments_per_track;
-
- return (id >= lo && id < hi);
- }
-}
-
-static int seek_forward(int segment_id)
-{
- TRACE_FUN(5, "seek_forward");
- int failures = 0;
- int result = 0;
- int count;
- static int margin = 1; /* fixed: stop this before target */
- static int overshoot = 1;
- static int min_count = 8;
- int expected = -1;
- int target = segment_id - margin;
- int fast_seeking;
-
- if (!location.known) {
- TRACE(1, "fatal: cannot seek from unknown location");
- result = -EIO;
- } else if (!validate(segment_id)) {
- TRACE(1, "fatal: head off track (bad hardware?)");
- ftape_sleep(1 * SECOND);
- ftape_failure = 1;
- result = -EIO;
- } else {
- int prev_segment = location.segment;
-
- TRACEx4(4, "from %d/%d to %d/0 - %d", location.segment,
- location.sector, segment_id, margin);
- count = target - location.segment - overshoot;
- fast_seeking = (count > min_count + (location.bot ? 1 : 0));
- if (fast_seeking) {
- TRACEx1(5, "fast skipping %d segments", count);
- expected = segment_id - margin;
- fast_seek(count, 0);
- }
- if (!tape_running) {
- logical_forward();
- }
- while (location.segment < segment_id) {
- /* This requires at least one sector in a (bad) segment to
- * have a valid and readable sector id !
- * It looks like this is not guaranteed, so we must try
- * to find a way to skip an EMPTY_SEGMENT. !!! FIXME !!!
- */
- if (ftape_read_id() < 0 || !location.known) {
- location.known = 0;
- if (!tape_running || ++failures > SECTORS_PER_SEGMENT ||
- (current->signal & _DONT_BLOCK)) {
- TRACE(1, "read_id failed completely");
- result = -EIO;
- break;
- } else {
- TRACEx1(5, "read_id failed, retry (%d)", failures);
- }
- } else if (fast_seeking) {
- TRACEx4(4, "ended at %d/%d (%d,%d)", location.segment,
- location.sector, overshoot, inhibit_correction);
- if (!inhibit_correction &&
- (location.segment < expected ||
- location.segment > expected + margin)) {
- int error = location.segment - expected;
- TRACEx2(4, "adjusting overshoot from %d to %d",
- overshoot, overshoot + error);
- overshoot += error;
- /* All overshoots have the same direction, so it should
- * never become negative, but who knows.
- */
- if (overshoot < -5 || overshoot > 10) {
- if (overshoot < 0) {
- overshoot = -5; /* keep sane value */
- } else {
- overshoot = 10; /* keep sane value */
- }
- TRACEx1(4, "clipped overshoot to %d", overshoot);
- }
- }
- fast_seeking = 0;
- }
- if (location.known) {
- if (location.segment > prev_segment + 1) {
- TRACEx1(4, "missed segment %d while skipping", prev_segment + 1);
- }
- prev_segment = location.segment;
- }
- }
- if (location.segment > segment_id) {
- TRACEx2(4, "failed: skip ended at segment %d/%d",
- location.segment, location.sector);
- result = -EIO;
- }
- }
- TRACE_EXIT;
- return result;
-}
-
-static int skip_reverse(int segment_id, int *pstatus)
-{
- TRACE_FUN(5, "skip_reverse");
- int result = 0;
- int failures = 0;
- static int overshoot = 1;
- static int min_rewind = 2; /* 1 + overshoot */
- static const int margin = 1; /* stop this before target */
- int expected = 0;
- int count;
- int short_seek;
- int target = segment_id - margin;
-
- if (location.known && !validate(segment_id)) {
- TRACE(1, "fatal: head off track (bad hardware?)");
- ftape_sleep(1 * SECOND);
- ftape_failure = 1;
- result = -EIO;
- } else
- do {
- if (!location.known) {
- TRACE(-1, "warning: location not known");
- }
- TRACEx4(4, "from %d/%d to %d/0 - %d",
- location.segment, location.sector, segment_id, margin);
- /* min_rewind == 1 + overshoot_when_doing_minimum_rewind
- * overshoot == overshoot_when_doing_larger_rewind
- * Initially min_rewind == 1 + overshoot, optimization
- * of both values will be done separately.
- * overshoot and min_rewind can be negative as both are
- * sums of three components:
- * any_overshoot == rewind_overshoot - stop_overshoot - start_overshoot
- */
- if (location.segment - target - (min_rewind - 1) < 1) {
- short_seek = 1;
- } else {
- count = location.segment - target - overshoot;
- short_seek = (count < 1);
- }
- if (short_seek) {
- count = 1; /* do shortest rewind */
- expected = location.segment - min_rewind;
- if (expected / segments_per_track != location.track) {
- expected = location.track * segments_per_track;
- }
- } else {
- expected = target;
- }
- fast_seek(count, 1);
- logical_forward();
- result = ftape_read_id();
- if (result == 0 && location.known) {
- TRACEx5(4, "ended at %d/%d (%d,%d,%d)", location.segment,
- location.sector, min_rewind, overshoot, inhibit_correction);
- if (!inhibit_correction &&
- (location.segment < expected ||
- location.segment > expected + margin)) {
- int error = expected - location.segment;
- if (short_seek) {
- TRACEx2(4, "adjusting min_rewind from %d to %d",
- min_rewind, min_rewind + error);
- min_rewind += error;
- if (min_rewind < -5) { /* is this right ? FIXME ! */
- min_rewind = -5; /* keep sane value */
- TRACEx1(4, "clipped min_rewind to %d", min_rewind);
- }
- } else {
- TRACEx2(4, "adjusting overshoot from %d to %d",
- overshoot, overshoot + error);
- overshoot += error;
- if (overshoot < -5 || overshoot > 10) {
- if (overshoot < 0) {
- overshoot = -5; /* keep sane value */
- } else {
- overshoot = 10; /* keep sane value */
- }
- TRACEx1(4, "clipped overshoot to %d", overshoot);
- }
- }
- }
- } else {
- if ((!tape_running && !location.known) ||
- ++failures > SECTORS_PER_SEGMENT) {
- TRACE(1, "read_id failed completely");
- result = -EIO;
- break;
- } else {
- TRACEx1(5, "ftape_read_id failed, retry (%d)", failures);
- }
- result = ftape_report_drive_status(pstatus);
- if (result < 0) {
- TRACEi(1, "ftape_report_drive_status failed with code", result);
- break;
- }
- }
- } while (location.segment > segment_id &&
- (current->signal & _DONT_BLOCK) == 0);
- if (location.known) {
- TRACEx2(4, "current location: %d/%d", location.segment, location.sector);
- }
- TRACE_EXIT;
- return result;
-}
-
-static int determine_position(void)
-{
- TRACE_FUN(5, "determine_position");
- int retry = 0;
- int fatal = 0;
- int status;
- int result;
-
- if (!tape_running) {
- /* This should only happen if tape is stopped by isr.
- */
- TRACE(5, "waiting for tape stop");
- result = ftape_ready_wait(timeout.pause, &status);
- if (result < 0) {
- TRACE(5, "drive still running (fatal)");
- tape_running = 1; /* ? */
- }
- } else {
- ftape_report_drive_status(&status);
- }
- if (status & QIC_STATUS_READY) {
- /* Drive must be ready to check error state !
- */
- TRACE(5, "drive is ready");
- if (status & QIC_STATUS_ERROR) {
- int error;
- int command;
-
- /* Report and clear error state, try to continue.
- */
- TRACE(5, "error status set");
- ftape_report_error(&error, &command, 1);
- ftape_ready_wait(timeout.reset, &status);
- tape_running = 0; /* ? */
- }
- if (check_bot_eot(status)) {
- if (location.bot) {
- if ((status & QIC_STATUS_READY) == 0) {
- /* tape moving away from bot/eot, let's see if we
- * can catch up with the first segment on this track.
- */
- } else {
- TRACE(5, "start tape from logical bot");
- logical_forward(); /* start moving */
- }
- } else {
- if ((status & QIC_STATUS_READY) == 0) {
- TRACE(4, "waiting for logical end of track");
- result = ftape_ready_wait(timeout.reset, &status);
- /* error handling needed ? */
- } else {
- TRACE(4, "tape at logical end of track");
- }
- }
- } else {
- TRACE(5, "start tape");
- logical_forward(); /* start moving */
- location.known = 0; /* not cleared by logical forward ! */
- }
- }
- if (!location.known) {
- /* tape should be moving now, start reading id's
- */
- TRACE(5, "location unknown");
- do {
- result = ftape_read_id();
- if (result < 0) {
- /* read-id somehow failed, tape may have reached end
- * or some other error happened.
- */
- TRACE(5, "read-id failed");
- ftape_report_drive_status(&status);
- if (status & QIC_STATUS_READY) {
- tape_running = 0;
- TRACEx1(4, "tape stopped for unknown reason ! status = 0x%02x",
- status);
- if (status & QIC_STATUS_ERROR) {
- fatal = 1;
- } else {
- if (check_bot_eot(status)) {
- result = 0;
- } else {
- fatal = 1; /* oops, tape stopped but not at end ! */
- }
- }
- }
- result = -EIO;
- }
- } while (result < 0 && !fatal && ++retry < SECTORS_PER_SEGMENT);
- } else {
- result = 0;
- }
- TRACEx1(5, "tape is positioned at segment %d", location.segment);
- TRACE_EXIT;
- return result;
-}
-
-/* Get the tape running and position it just before the
- * requested segment.
- * Seek tape-track and reposition as needed.
- */
-int ftape_start_tape(int segment_id, int sector_offset)
-{
- TRACE_FUN(5, "ftape_start_tape");
- int track = segment_id / segments_per_track;
- int result = -EIO;
- int status;
- static int last_segment = -1;
- static int bad_bus_timing = 0;
- /* number of segments passing the head between starting the tape
- * and being able to access the first sector.
- */
- static int start_offset = 1;
- int retry = 0;
-
- /* If sector_offset > 0, seek into wanted segment instead of
- * into previous.
- * This allows error recovery if a part of the segment is bad
- * (erased) causing the tape drive to generate an index pulse
- * thus causing a no-data error before the requested sector
- * is reached.
- */
- tape_running = 0;
- TRACEx3(4, "target segment: %d/%d%s", segment_id, sector_offset,
- buffer[head].retry > 0 ? " retry" : "");
- if (buffer[head].retry > 0) { /* this is a retry */
- if (!bad_bus_timing && ftape_data_rate == 1 &&
- history.overrun_errors - overrun_count_offset >= 8) {
- ftape_set_data_rate(ftape_data_rate + 1);
- bad_bus_timing = 1;
- TRACE(2, "reduced datarate because of excessive overrun errors");
- }
- }
- last_segment = segment_id;
- if (location.track != track || (might_be_off_track &&
- buffer[head].retry == 0)) {
- /* current track unknown or not equal to destination
- */
- ftape_ready_wait(timeout.seek, &status);
- ftape_seek_head_to_track(track);
- overrun_count_offset = history.overrun_errors;
- }
- do {
- if (!location.known) {
- determine_position();
- }
- /* Check if we are able to catch the requested segment in time.
- */
- if (location.known && location.segment >= segment_id -
- ((tape_running || location.bot) ? 0 : start_offset)) {
- /* Too far ahead (in or past target segment).
- */
- if (tape_running) {
- result = stop_tape(&status);
- if (result < 0) {
- TRACEi(1, "stop tape failed with code", result);
- break;
- }
- TRACE(5, "tape stopped");
- tape_running = 0;
- }
- TRACE(5, "repositioning");
- ++history.rewinds;
- if (segment_id % segments_per_track < start_offset) {
- /* If seeking to first segments on track better do a complete
- * rewind to logical begin of track to get a more steady tape
- * motion.
- */
- result = ftape_command_wait((location.track & 1) ?
- QIC_PHYSICAL_FORWARD :
- QIC_PHYSICAL_REVERSE,
- timeout.rewind, &status);
- check_bot_eot(status); /* update location */
- } else {
- result = skip_reverse(segment_id - start_offset, &status);
- }
- }
- if (!location.known) {
- TRACE(-1, "panic: location not known");
- result = -EIO;
- if ((current->signal & _DONT_BLOCK) || ftape_failure) {
- break;
- } else {
- continue;
- }
- }
- TRACEx2(4, "current segment: %d/%d", location.segment, location.sector);
- /* We're on the right track somewhere before the wanted segment.
- * Start tape movement if needed and skip to just before or inside
- * the requested segment. Keep tape running.
- */
- result = 0;
- if (location.segment < segment_id -
- ((tape_running || location.bot) ? 0 : start_offset)) {
- if (sector_offset > 0) {
- result = seek_forward(segment_id);
- } else {
- result = seek_forward(segment_id - 1);
- }
- }
- if (result == 0 &&
- location.segment != segment_id - (sector_offset > 0 ? 0 : 1)) {
- result = -EIO;
- }
- } while (result < 0 && !ftape_failure &&
- (current->signal & _DONT_BLOCK) == 0 &&
- ++retry <= 5);
- if (result < 0) {
- TRACE(1, "failed to reposition");
- }
- TRACE_EXIT;
- return result;
-}
diff --git a/drivers/char/ftape/ftape-rw.h b/drivers/char/ftape/ftape-rw.h
deleted file mode 100644
index 983a1edd0..000000000
--- a/drivers/char/ftape/ftape-rw.h
+++ /dev/null
@@ -1,173 +0,0 @@
-#ifndef _FTAPE_RW_H
-#define _FTAPE_RW_H
-
-/*
- * Copyright (C) 1993-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING. If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- *
- $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-rw.h,v $
- $Author: bas $
- *
- $Revision: 1.33 $
- $Date: 1995/04/22 07:30:15 $
- $State: Beta $
- *
- * This file contains the definitions for the read and write
- * functions for the QIC-117 floppy-tape driver for Linux.
- *
- */
-
-#include "fdc-io.h"
-#include "kernel-interface.h"
-
-#define GET2( address, offset) *(short*)(address + offset)
-#define GET4( address, offset) *(long*)(address + offset)
-#define PUT2( address, offset, value) *(short*)(address + offset) = value
-#define PUT4( address, offset, value) *(long*)(address + offset) = value
-
-enum runner_status_enum {
- idle = 0,
- running,
- do_abort,
- aborting,
- logical_eot,
- end_of_tape,
- buffer_overrun,
- buffer_underrun,
-};
-
-typedef struct {
- byte *address;
- volatile buffer_state_enum status;
- volatile byte *ptr;
- volatile unsigned bytes;
- volatile unsigned segment_id;
-
- /* bitmap for remainder of segment not yet handled.
- * one bit set for each bad sector that must be skipped.
- */
- volatile unsigned long bad_sector_map;
-
- /* bitmap with bad data blocks in data buffer.
- * the errors in this map may be retried.
- */
- volatile unsigned long soft_error_map;
-
- /* bitmap with bad data blocks in data buffer
- * the errors in this map may not be retried.
- */
- volatile unsigned long hard_error_map;
-
- /* retry counter for soft errors.
- */
- volatile int retry;
-
- /* sectors to skip on retry ???
- */
- volatile unsigned int skip;
-
- /* nr of data blocks in data buffer
- */
- volatile unsigned data_offset;
-
- /* offset in segment for first sector to be handled.
- */
- volatile unsigned sector_offset;
-
- /* size of cluster of good sectors to be handled.
- */
- volatile unsigned sector_count;
-
- /* size of remaining part of segment to be handled.
- */
- volatile unsigned remaining;
-
- /* points to next segment (contiguous) to be handled,
- * or is zero if no read-ahead is allowed.
- */
- volatile unsigned next_segment;
-
- /* flag being set if deleted data was read.
- */
- volatile int deleted;
-
- volatile byte head;
- volatile byte cyl;
- volatile byte sect;
-} buffer_struct;
-
-typedef struct {
- int active;
- int error;
- int offset;
-} ftape_fast_start_struct;
-
-typedef struct {
- int id;
- int size;
- int free;
-} ftape_last_segment_struct;
-
-typedef struct {
- int track; /* tape head position */
- volatile int known; /* validates bot, segment, sector */
- volatile int bot; /* logical begin of track */
- volatile int eot; /* logical end of track */
- volatile int segment; /* current segment */
- volatile int sector; /* sector offset within current segment */
-} location_record;
-
-typedef enum {
- fmt_normal = 2, fmt_1100ft = 3, fmt_wide = 4, fmt_425ft = 5
-} format_type;
-
-/* ftape-rw.c defined global vars.
- */
-extern int tracing;
-extern byte trace_id;
-extern buffer_struct buffer[];
-extern location_record location;
-extern volatile ftape_fast_start_struct ftape_fast_start;
-extern byte deblock_buffer[(SECTORS_PER_SEGMENT - 3) * SECTOR_SIZE];
-extern byte scratch_buffer[(SECTORS_PER_SEGMENT - 3) * SECTOR_SIZE];
-extern ftape_last_segment_struct ftape_last_segment;
-extern int header_segment_1;
-extern int header_segment_2;
-extern int used_header_segment;
-extern unsigned int fast_seek_segment_time;
-extern volatile int tape_running;
-extern format_type format_code;
-
-/* ftape-rw.c defined global functions.
- */
-extern int count_ones(unsigned long mask);
-extern int valid_segment_no(unsigned segment);
-extern int setup_new_segment(buffer_struct * buff, unsigned int segment_id,
- int offset);
-extern int calc_next_cluster(buffer_struct * buff);
-extern buffer_struct *next_buffer(volatile int *x);
-extern int ftape_read_id(void);
-extern void ftape_tape_parameters(byte drive_configuration);
-extern int wait_segment(buffer_state_enum state);
-extern int ftape_dumb_stop(void);
-extern int ftape_start_tape(int segment_id, int offset);
-
-/* fdc-io.c defined global functions.
- */
-extern int setup_fdc_and_dma(buffer_struct * buff, byte operation);
-
-#endif /* _FTAPE_RW_H */
diff --git a/drivers/char/ftape/ftape-write.c b/drivers/char/ftape/ftape-write.c
deleted file mode 100644
index 0ba24c978..000000000
--- a/drivers/char/ftape/ftape-write.c
+++ /dev/null
@@ -1,723 +0,0 @@
-
-
-
-/*
- * Copyright (C) 1993-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING. If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-write.c,v $
- $Author: bas $
- *
- $Revision: 1.26 $
- $Date: 1995/05/27 08:55:27 $
- $State: Beta $
- *
- * This file contains the writing code
- * for the QIC-117 floppy-tape driver for Linux.
- */
-
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/mm.h>
-#include <linux/ftape.h>
-#include <asm/uaccess.h>
-
-#include "tracing.h"
-#include "ftape-write.h"
-#include "ftape-read.h"
-#include "qic117.h"
-#include "ftape-io.h"
-#include "ftape-ctl.h"
-#include "ftape-rw.h"
-#include "ftape-eof.h"
-#include "ecc.h"
-#include "ftape-bsm.h"
-
-
-/* Global vars.
- */
-
-/* Local vars.
- */
-static int buf_pos_wr = 0;
-static int last_write_failed = 0;
-static int need_flush = 0;
-
-#define WRITE_MULTI 0
-#define WRITE_SINGLE 1
-
-void ftape_zap_write_buffers(void)
-{
- int i;
-
- for (i = 0; i < NR_BUFFERS; ++i) {
- buffer[i].status = done;
- }
- need_flush = 0;
-}
-
-int copy_and_gen_ecc(char *destination, byte * source,
- unsigned int bad_sector_map)
-{
- TRACE_FUN(8, "copy_and_gen_ecc");
- int result;
- struct memory_segment mseg;
- int bads = count_ones(bad_sector_map);
-
- if (bads > 0) {
- TRACEi(4, "bad sectors in map:", bads);
- }
- if (bads + 3 >= SECTORS_PER_SEGMENT) {
- TRACE(4, "empty segment");
- mseg.blocks = 0; /* skip entire segment */
- result = 0; /* nothing written */
- } else {
- mseg.blocks = SECTORS_PER_SEGMENT - bads;
- mseg.data = destination;
- memcpy(mseg.data, source, (mseg.blocks - 3) * SECTOR_SIZE);
- result = ecc_set_segment_parity(&mseg);
- if (result < 0) {
- TRACE(1, "ecc_set_segment_parity failed");
- } else {
- result = (mseg.blocks - 3) * SECTOR_SIZE;
- }
- }
- TRACE_EXIT;
- return result;
-}
-
-void prevent_flush(void)
-{
- need_flush = 0;
- ftape_state = idle;
-}
-
-int start_writing(int mode)
-{
- TRACE_FUN(5, "start_writing");
- int result = 0;
- buffer_struct *buff = &buffer[head];
- int segment_id = buff->segment_id;
-
- if (ftape_state == writing && buff->status == waiting) {
- setup_new_segment(buff, segment_id, 1);
- if (mode == WRITE_SINGLE) {
- buffer[head].next_segment = 0; /* stop tape instead of pause */
- }
- calc_next_cluster(buff); /* prepare */
- buff->status = writing;
- if (runner_status == idle) {
- TRACEi(5, "starting runner for segment", segment_id);
- result = ftape_start_tape(segment_id, buff->sector_offset);
- if (result >= 0) {
- runner_status = running;
- }
- }
- if (result >= 0) {
- result = setup_fdc_and_dma(buff, FDC_WRITE); /* go */
- }
- ftape_state = writing;
- }
- TRACE_EXIT;
- return result;
-}
-
-int loop_until_writes_done(void)
-{
- TRACE_FUN(5, "loop_until_writes_done");
- int i;
- int result = 0;
-
- /*
- * Wait until all data is actually written to tape.
- */
- while (ftape_state == writing && buffer[head].status != done) {
- TRACEx2(7, "tail: %d, head: %d", tail, head);
- for (i = 0; i < NR_BUFFERS; ++i) {
- TRACEx3(8, "buffer[ %d] segment_id: %d, status: %d",
- i, buffer[i].segment_id, buffer[i].status);
- }
- result = fdc_interrupt_wait(5 * SECOND);
- if (result < 0) {
- TRACE(1, "fdc_interrupt_wait failed");
- last_write_failed = 1;
- break;
- }
- if (buffer[head].status == error) {
- /* Allow escape from loop when signaled !
- */
- if (current->signal & _DONT_BLOCK) {
- TRACE(2, "interrupted by signal");
- TRACE_EXIT;
- result = -EINTR; /* is this the right return value ? */
- break;
- }
- if (buffer[head].hard_error_map != 0) {
- /* Implement hard write error recovery here
- */
- }
- buffer[head].status = waiting; /* retry this one */
- if (runner_status == aborting) {
- ftape_dumb_stop();
- runner_status = idle;
- }
- if (runner_status != idle) {
- TRACE(1, "unexpected state: runner_status != idle");
- result = -EIO;
- break;
- }
- start_writing(WRITE_MULTI);
- }
- TRACE(5, "looping until writes done");
- result = 0; /* normal exit status */
- }
- TRACE_EXIT;
- return result;
-}
-
-/* Write given segment from buffer at address onto tape.
- */
-int write_segment(unsigned segment_id, byte * address, int flushing)
-{
- TRACE_FUN(5, "write_segment");
- int result = 0;
- int bytes_written = 0;
-
- TRACEi(5, "segment_id =", segment_id);
- if (ftape_state != writing) {
- if (ftape_state == reading) {
- TRACE(5, "calling ftape_abort_operation");
- result = ftape_abort_operation();
- if (result < 0) {
- TRACE(1, "ftape_abort_operation failed");
- }
- }
- ftape_zap_read_buffers();
- ftape_zap_write_buffers();
- ftape_state = writing;
- }
- /* if all buffers full we'll have to wait...
- */
- wait_segment(writing);
- if (buffer[tail].status == error) {
- /* setup for a retry
- */
- buffer[tail].status = waiting;
- bytes_written = -EAGAIN; /* force retry */
- if (buffer[tail].hard_error_map != 0) {
- TRACEx1(1, "warning: %d hard error(s) in written segment",
- count_ones(buffer[tail].hard_error_map));
- TRACEx1(4, "hard_error_map = 0x%08lx", buffer[tail].hard_error_map);
- /* Implement hard write error recovery here
- */
- }
- } else if (buffer[tail].status == done) {
- history.defects += count_ones(buffer[tail].hard_error_map);
- } else {
- TRACE(1, "wait for empty segment failed");
- result = -EIO;
- }
- /* If just passed last segment on tape: wait for BOT or EOT mark.
- */
- if (result >= 0 && runner_status == logical_eot) {
- int status;
-
- result = ftape_ready_wait(timeout.seek, &status);
- if (result < 0 || (status & (QIC_STATUS_AT_BOT | QIC_STATUS_AT_EOT)) == 0) {
- TRACE(1, "eot/bot not reached");
- } else {
- runner_status = end_of_tape;
- }
- }
- /* should runner stop ?
- */
- if (result >= 0 &&
- (runner_status == aborting || runner_status == buffer_underrun ||
- runner_status == end_of_tape)) {
- if (runner_status != end_of_tape) {
- result = ftape_dumb_stop();
- }
- if (result >= 0) {
- if (runner_status == aborting) {
- if (buffer[head].status == writing) {
- buffer[head].status = done; /* ????? */
- }
- }
- runner_status = idle; /* aborted ? */
- }
- }
- /* Don't start tape if runner idle and segment empty.
- */
- if (result >= 0 && !(runner_status == idle &&
- get_bad_sector_entry(segment_id) == EMPTY_SEGMENT)) {
- if (buffer[tail].status == done) {
- /* now at least one buffer is empty, fill it with our data.
- * skip bad sectors and generate ecc.
- * copy_and_gen_ecc return nr of bytes written,
- * range 0..29 Kb inclusive !
- */
- result = copy_and_gen_ecc(buffer[tail].address, address,
- get_bad_sector_entry(segment_id));
- if (result >= 0) {
- bytes_written = result;
- buffer[tail].segment_id = segment_id;
- buffer[tail].status = waiting;
- next_buffer(&tail);
- }
- }
- /* Start tape only if all buffers full or flush mode.
- * This will give higher probability of streaming.
- */
- if (result >= 0 && runner_status != running &&
- ((head == tail && buffer[tail].status == waiting) || flushing)) {
- result = start_writing(WRITE_MULTI);
- }
- }
- TRACE_EXIT;
- return (result < 0) ? result : bytes_written;
-}
-
-/* Write as much as fits from buffer to the given segment on tape
- * and handle retries.
- * Return the number of bytes written (>= 0), or:
- * -EIO write failed
- * -EINTR interrupted by signal
- * -ENOSPC device full
- */
-int _write_segment(unsigned int segment_id, byte * buffer, int flush)
-{
- TRACE_FUN(5, "_write_segment");
- int retry = 0;
- int result;
-
- history.used |= 2;
- for (;;) {
- if (segment_id > ftape_last_segment.id && !flush) {
- result = -ENOSPC; /* tape full */
- break;
- }
- result = write_segment(segment_id, buffer, flush);
- if (result < 0) {
- if (result == -EAGAIN) {
- if (++retry > 100) {
- TRACE(1, "write failed, >100 retries in segment");
- result = -EIO; /* give up */
- break;
- } else {
- TRACEx1(2, "write error, retry %d", retry);
- }
- } else {
- TRACEi(1, "write_segment failed, error:", -result);
- break;
- }
- } else { /* success */
- if (result == 0) { /* empty segment */
- TRACE(4, "empty segment, nothing written");
- }
- break;
- }
- /* Allow escape from loop when signaled !
- */
- if (current->signal & _DONT_BLOCK) {
- TRACE(2, "interrupted by signal");
- TRACE_EXIT;
- result = -EINTR; /* is this the right return value ? */
- break;
- }
- }
- TRACE_EXIT;
- return result;
-}
-
-int update_header_segment(unsigned segment, byte * buffer)
-{
- TRACE_FUN(5, "update_header_segment");
- int result = 0;
- int status;
-
- if (buffer == NULL) {
- TRACE(5, "no input buffer specified");
- buffer = deblock_buffer;
- result = read_segment(used_header_segment, buffer, &status, 0);
- if (bad_sector_map_changed) {
- store_bad_sector_map(buffer);
- }
- if (failed_sector_log_changed) {
- update_failed_sector_log(buffer);
- }
- }
- if (result >= 0 && GET4(buffer, 0) != 0xaa55aa55) {
- TRACE(1, "wrong header signature found, aborting");
- result = -EIO;
- }
- if (result >= 0) {
- result = _write_segment(segment, buffer, 0);
- if (result >= 0 && runner_status == idle) {
- /* Force flush for single segment instead of relying on
- * flush in read_segment for multiple segments.
- */
- result = start_writing(WRITE_SINGLE);
- if (result >= 0 && ftape_state == writing) {
- result = loop_until_writes_done();
- prevent_flush();
- }
- }
-#ifdef VERIFY_HEADERS
- if (result >= 0) { /* read back and verify */
- result = read_segment(segment, scratch_buffer, &status, 0);
- /* Should retry if soft error during read !
- * TO BE IMPLEMENTED
- */
- if (result >= 0) {
- if (memcmp(buffer, scratch_buffer, sizeof(buffer)) == 0) {
- result = 0; /* verified */
- TRACE(5, "verified");
- } else {
- result = -EIO; /* verify failed */
- TRACE(5, "verify failed");
- }
- }
- }
-#endif
- }
- TRACE_EXIT;
- return result;
-}
-
-int ftape_write_header_segments(byte * buffer)
-{
- TRACE_FUN(5, "ftape_write_header_segments");
- int result = 0;
- int retry = 0;
- int header_1_ok = 0;
- int header_2_ok = 0;
-
- do {
- if (!header_1_ok) {
- result = update_header_segment(header_segment_1, buffer);
- if (result < 0) {
- continue;
- }
- header_1_ok = 1;
- }
- if (!header_2_ok) {
- result = update_header_segment(header_segment_2, buffer);
- if (result < 0) {
- continue;
- }
- header_2_ok = 1;
- }
- } while (result < 0 && retry++ < 3);
- if (result < 0) {
- if (!header_1_ok) {
- TRACE(1, "update of first header segment failed");
- }
- if (!header_2_ok) {
- TRACE(1, "update of second header segment failed");
- }
- result = -EIO;
- }
- TRACE_EXIT;
- return result;
-}
-
-int ftape_update_header_segments(byte * buffer, int update)
-{
- TRACE_FUN(5, "ftape_update_header_segments");
- int result = 0;
- int dummy;
- int header_changed = 1;
-
- if (ftape_state == writing) {
- result = loop_until_writes_done();
- }
- if (read_only) {
- result = 0; /* exit and fake success */
- TRACE(4, "Tape set read-only: no update");
- } else if (result >= 0) {
- result = ftape_abort_operation();
- if (result >= 0) {
- if (buffer == NULL) {
- if (bad_sector_map_changed || failed_sector_log_changed) {
- ftape_seek_to_bot(); /* prevents extra rewind */
- buffer = deblock_buffer;
- result = read_segment(used_header_segment, buffer, &dummy, 0);
- if (result < 0) {
- TRACE_EXIT;
- return result;
- }
- }
- header_changed = 0;
- }
- if (update) {
- if (bad_sector_map_changed) {
- store_bad_sector_map(buffer);
- header_changed = 1;
- }
- if (failed_sector_log_changed) {
- update_failed_sector_log(buffer);
- header_changed = 1;
- }
- }
- if (header_changed) {
- ftape_seek_to_bot(); /* prevents extra rewind */
- result = ftape_write_header_segments(buffer);
- }
- }
- }
- TRACE_EXIT;
- return result;
-}
-
-int ftape_flush_buffers(void)
-{
- TRACE_FUN(5, "ftape_flush_buffers");
- int result;
- int pad_count;
- int data_remaining;
- static int active = 0;
-
- if (active) {
- TRACE(5, "nested call, abort");
- TRACE_EXIT;
- return 0;
- }
- active = 1;
- TRACEi(5, "entered, ftape_state =", ftape_state);
- if (ftape_state != writing && !need_flush) {
- active = 0;
- TRACE(5, "no need for flush");
- TRACE_EXIT;
- return 0;
- }
- data_remaining = buf_pos_wr;
- buf_pos_wr = 0; /* prevent further writes if this fails */
- TRACE(5, "flushing write buffers");
- if (last_write_failed) {
- ftape_zap_write_buffers();
- active = 0;
- TRACE_EXIT;
- return write_protected ? -EROFS : -EIO;
- }
- /*
- * If there is any data not written to tape yet, append zero's
- * up to the end of the sector. Then write the segment(s) to tape.
- */
- if (data_remaining > 0) {
- int written;
-
- do {
- TRACEi(4, "remaining in buffer:", data_remaining);
- pad_count = sizeof(deblock_buffer) - data_remaining;
- TRACEi(7, "flush, padding count:", pad_count);
- memset(deblock_buffer + data_remaining, 0, pad_count); /* pad buffer */
- result = _write_segment(ftape_seg_pos, deblock_buffer, 1);
- if (result < 0) {
- if (result != -ENOSPC) {
- last_write_failed = 1;
- }
- active = 0;
- TRACE_EXIT;
- return result;
- }
- written = result;
- clear_eof_mark_if_set(ftape_seg_pos, written);
- TRACEi(7, "flush, moved out buffer:", written);
- if (written > 0) {
- data_remaining -= written;
- if (data_remaining > 0) {
- /* Need another segment for remaining data, move the remainder
- * to the beginning of the buffer
- */
- memmove(deblock_buffer, deblock_buffer + written, data_remaining);
- }
- }
- ++ftape_seg_pos;
- } while (data_remaining > 0);
- /* Data written to last segment == data_remaining + written
- * value is in range [1..29K].
- */
- TRACEx2(4, "last write: %d, netto pad-count: %d",
- data_remaining + written, -data_remaining);
- if (-1024 < data_remaining && data_remaining <= 0) {
- /* Last sector of segment was used for data, so put eof mark
- * in next segment and position at second file mark.
- */
- if (ftape_weof(2, ftape_seg_pos, 1) >= 0) {
- ++ftape_seg_pos; /* position between file marks */
- }
- } else {
- /* Put eof mark in previous segment after data and position
- * at second file mark.
- */
- ftape_weof(2, ftape_seg_pos - 1, 1 +
- ((SECTOR_SIZE - 1 + result + data_remaining) / SECTOR_SIZE));
- }
- } else {
- TRACE(7, "deblock_buffer empty");
- if (ftape_weof(2, ftape_seg_pos, 1) >= 0) {
- ++ftape_seg_pos; /* position between file marks */
- }
- start_writing(WRITE_MULTI);
- }
- TRACE(7, "waiting");
- result = loop_until_writes_done();
- if (result < 0) {
- TRACE(1, "flush buffers failed");
- }
- ftape_state = idle;
- last_write_failed = 0;
- need_flush = 0;
- active = 0;
- TRACE_EXIT;
- return result;
-}
-
-int _ftape_write(const char *buff, int req_len)
-{
- TRACE_FUN(5, "_ftape_write");
- int result = 0;
- int cnt;
- int written = 0;
-
- if (write_protected) {
- TRACE(1, "error: cartridge write protected");
- last_write_failed = 1;
- result = -EROFS;
- } else if (ftape_offline || !formatted || no_tape) {
- result = -EIO;
- } else if (first_data_segment == -1) {
- /*
- * If we haven't read the header segment yet, do it now.
- * This will verify the configuration, get the eof markers
- * and the bad sector table.
- * We'll use the deblock buffer for scratch.
- */
- result = read_header_segment(deblock_buffer);
- if (result >= 0 && ftape_seg_pos > ftape_last_segment.id) {
- result = -ENOSPC; /* full is full */
- }
- }
- if (result < 0) {
- TRACE_EXIT;
- return result;
- }
- /*
- * This part writes data blocks to tape until the
- * requested amount is written.
- * The data will go in a buffer until it's enough
- * for a segment without bad sectors. Then we'll write
- * that segment to tape.
- * The bytes written will be removed from the buffer
- * and the process is repeated until there is less
- * than one segment to write left in the buffer.
- */
- while (req_len > 0) {
- int space_left = sizeof(deblock_buffer) - buf_pos_wr;
-
- TRACEi(7, "remaining req_len:", req_len);
- TRACEi(7, " buf_pos:", buf_pos_wr);
- cnt = (req_len < space_left) ? req_len : space_left;
- if (cnt > 0) {
- result = verify_area(VERIFY_READ, buff, cnt);
- if (result) {
- TRACE(1, "verify_area failed");
- last_write_failed = 1;
- TRACE_EXIT;
- return result;
- }
- copy_from_user(deblock_buffer + buf_pos_wr, buff, cnt);
- buff += cnt;
- req_len -= cnt;
- buf_pos_wr += cnt;
- }
- TRACEi(7, "moved into blocking buffer:", cnt);
- while (buf_pos_wr >= sizeof(deblock_buffer)) {
- /* If this is the last buffer to be written, let flush handle it.
- */
- if (ftape_seg_pos >= ftape_last_segment.id) {
- TRACEi(7, "remaining in blocking buffer:", buf_pos_wr);
- TRACEi(7, "just written bytes:", written + cnt);
- TRACE_EXIT;
- return written + cnt;
- }
- /* Got one full buffer, write it to disk
- */
- result = _write_segment(ftape_seg_pos, deblock_buffer, 0);
- TRACEi(5, "_write_segment result =", result);
- if (result < 0) {
- if (result == -EAGAIN) {
- TRACE(5, "retry...");
- continue; /* failed, retry same segment */
- }
- last_write_failed = 1;
- TRACE_EXIT;
- return result;
- } else {
- clear_eof_mark_if_set(ftape_seg_pos, result);
- }
- if (result > 0 && result < buf_pos_wr) {
- /* Partial write: move remainder in lower part of buffer
- */
- memmove(deblock_buffer, deblock_buffer + result, buf_pos_wr - result);
- }
- TRACEi(7, "moved out of blocking buffer:", result);
- buf_pos_wr -= result; /* remainder */
- ++ftape_seg_pos;
- /* Allow us to escape from this loop with a signal !
- */
- if (current->signal & _DONT_BLOCK) {
- TRACE(2, "interrupted by signal");
- last_write_failed = 1;
- TRACE_EXIT;
- return -EINTR; /* is this the right return value ? */
- }
- }
- written += cnt;
- }
- TRACEi(7, "remaining in blocking buffer:", buf_pos_wr);
- TRACEi(7, "just written bytes:", written);
- last_write_failed = 0;
- if (!need_flush && written > 0) {
- need_flush = 1;
- }
- TRACE_EXIT;
- return written; /* bytes written */
-}
-
-int ftape_fix(void)
-{
- TRACE_FUN(5, "ftape_fix");
- int result = 0;
- int dummy;
- int status;
-
- if (write_protected) {
- result = -EROFS;
- } else {
- /* This will copy header segment 2 to header segment 1
- * Spares us a tape format operation if header 2 is still good.
- */
- header_segment_1 = 0;
- header_segment_2 = 1;
- first_data_segment = 2;
- result = read_segment(header_segment_2, scratch_buffer, &dummy, 0);
- result = ftape_ready_wait(timeout.pause, &status);
- result = ftape_write_header_segments(scratch_buffer);
- }
- TRACE_EXIT;
- return result;
-}
diff --git a/drivers/char/ftape/kernel-interface.c b/drivers/char/ftape/kernel-interface.c
deleted file mode 100644
index 9bcb35e46..000000000
--- a/drivers/char/ftape/kernel-interface.c
+++ /dev/null
@@ -1,358 +0,0 @@
-/*
- * Copyright (C) 1993-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING. If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- *
- * This file contains the code that interfaces the kernel
- * for the QIC-40/80 floppy-tape driver for Linux.
- */
-
-#include <linux/module.h>
-#include <linux/version.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <asm/uaccess.h>
-#include <linux/kernel.h>
-#include <linux/signal.h>
-#include <linux/major.h>
-#include <linux/malloc.h>
-#include <linux/init.h>
-#include <linux/ftape.h>
-#include <asm/dma.h>
-
-#include "tracing.h"
-#include "kernel-interface.h"
-#include "ftape-read.h"
-#include "ftape-write.h"
-#include "ftape-io.h"
-#include "ftape-ctl.h"
-#include "ftape-rw.h"
-#include "fdc-io.h"
-
-
-/* Global vars.
- */
-
-/* Allocating a 96Kb DMAable buffer in one chunk won't work due to
- * memory fragmentation. To avoid this, it is broken up into
- * NR_BUFFERS chunks of 32Kbyte. --khp
- */
-
-byte *tape_buffer[NR_BUFFERS] = {NULL};
-
-/* Local vars.
- */
-static int busy_flag = 0;
-static int old_sigmask;
-
-static int ftape_open(struct inode *ino, struct file *filep);
-static int ftape_close(struct inode *ino, struct file *filep);
-static int ftape_ioctl(struct inode *ino, struct file *filep,
- unsigned int command, unsigned long arg);
-static long ftape_read(struct inode *ino, struct file *fp,
- char *buff, unsigned long req_len);
-static long ftape_write(struct inode *ino, struct file *fp,
- const char *buff, unsigned long req_len);
-
-static struct file_operations ftape_cdev =
-{
- NULL, /* lseek */
- ftape_read, /* read */
- ftape_write, /* write */
- NULL, /* readdir */
- NULL, /* poll */
- ftape_ioctl, /* ioctl */
- NULL, /* mmap */
- ftape_open, /* open */
- ftape_close, /* release */
- NULL, /* fsync */
-};
-
-/*
- * DMA'able memory allocation stuff.
- */
-
-/* Pure 2^n version of get_order */
-static inline int __get_order(unsigned long size)
-{
- int order;
-
- size = (size-1) >> (PAGE_SHIFT-1);
- order = -1;
- do {
- size >>= 1;
- order++;
- } while (size);
- return order;
-}
-
-static inline
-void *dmaalloc(int order)
-{
- return (void *) __get_dma_pages(GFP_KERNEL, order);
-}
-
-static inline
-void dmafree(void *addr, int order)
-{
- free_pages((unsigned long) addr, order);
-}
-
-/*
- * Called by modules package when installing the driver
- * or by kernel during the initialization phase
- */
-
-#ifdef MODULE
-EXPORT_NO_SYMBOLS;
-#define ftape_init init_module
-#endif
-
-__initfunc(int ftape_init(void))
-{
- int n;
- int order;
- TRACE_FUN(5, "ftape_init");
-#ifdef MODULE
- printk(KERN_INFO "ftape-2.08 960314\n"
- KERN_INFO " (c) 1993-1995 Bas Laarhoven (bas@vimec.nl)\n"
- KERN_INFO " (c) 1995-1996 Kai Harrekilde-Petersen (khp@dolphinics.no)\n"
- KERN_INFO " QIC-117 driver for QIC-40/80/3010/3020 tape drives\n"
- KERN_INFO " Compiled for kernel version " UTS_RELEASE
-#ifdef MODVERSIONS
- " with versioned symbols"
-#endif
- "\n");
-#else /* !MODULE */
- /* print a short no-nonsense boot message */
- printk("ftape-2.08 960314 for Linux 1.3.70\n");
-#endif /* MODULE */
- TRACE(3, "installing QIC-117 ftape driver...");
- if (register_chrdev(QIC117_TAPE_MAJOR, "ft", &ftape_cdev)) {
- TRACE(1, "register_chrdev failed");
- TRACE_EXIT;
- return -EIO;
- }
- TRACEx1(3, "ftape_init @ 0x%p", ftape_init);
- /*
- * Allocate the DMA buffers. They are deallocated at cleanup() time.
- */
- order = __get_order(BUFF_SIZE);
- for (n = 0; n < NR_BUFFERS; n++) {
- tape_buffer[n] = (byte *) dmaalloc(order);
- if (!tape_buffer[n]) {
- TRACE(1, "dmaalloc() failed");
- for (n = 0; n < NR_BUFFERS; n++) {
- if (tape_buffer[n]) {
- dmafree(tape_buffer[n], order);
- tape_buffer[n] = NULL;
- }
- }
- current->blocked = old_sigmask; /* restore mask */
- if (unregister_chrdev(QIC117_TAPE_MAJOR, "ft") != 0) {
- TRACE(3, "unregister_chrdev failed");
- }
- TRACE_EXIT;
- return -ENOMEM;
- } else {
- TRACEx2(3, "dma-buffer #%d @ %p", n, tape_buffer[n]);
- }
- }
- busy_flag = 0;
- ftape_unit = -1;
- ftape_failure = 1; /* inhibit any operation but open */
- udelay_calibrate(); /* must be before fdc_wait_calibrate ! */
- fdc_wait_calibrate();
- TRACE_EXIT;
-
- return 0;
-}
-
-
-#ifdef MODULE
-/* Called by modules package when removing the driver
- */
-void cleanup_module(void)
-{
- int n;
- int order;
- TRACE_FUN(5, "cleanup_module");
-
- if (unregister_chrdev(QIC117_TAPE_MAJOR, "ft") != 0) {
- TRACE(3, "failed");
- } else {
- TRACE(3, "successful");
- }
- order = __get_order(BUFF_SIZE);
- for (n = 0; n < NR_BUFFERS; n++) {
- if (tape_buffer[n]) {
- dmafree(tape_buffer[n], order);
- tape_buffer[n] = NULL;
- TRACEx1(3, "removed dma-buffer #%d", n);
- } else {
- TRACEx1(1, "dma-buffer #%d == NULL (bug?)", n);
- }
- }
- TRACE_EXIT;
-}
-#endif /* MODULE */
-
-/* Open ftape device
- */
-static int ftape_open(struct inode *ino, struct file *filep)
-{
- TRACE_FUN(4, "ftape_open");
- int result;
- MOD_INC_USE_COUNT; /* lock module in memory */
-
- TRACEi(5, "called for minor", MINOR(ino->i_rdev));
- if (busy_flag) {
- TRACE(1, "failed: already busy");
- MOD_DEC_USE_COUNT; /* unlock module in memory */
- TRACE_EXIT;
- return -EBUSY;
- }
- if ((MINOR(ino->i_rdev) & ~FTAPE_NO_REWIND) > 3) {
- TRACE(1, "failed: illegal unit nr");
- MOD_DEC_USE_COUNT; /* unlock module in memory */
- TRACE_EXIT;
- return -ENXIO;
- }
- if (ftape_unit == -1 || FTAPE_UNIT != (MINOR(ino->i_rdev) & 3)) {
- /* Other selection than last time
- */
- ftape_init_driver();
- }
- ftape_unit = MINOR(ino->i_rdev);
- ftape_failure = 0; /* allow tape operations */
- old_sigmask = current->blocked;
- current->blocked = _BLOCK_ALL;
- fdc_save_drive_specs(); /* save Drive Specification regs on i82078-1's */
- result = _ftape_open();
- if (result < 0) {
- TRACE(1, "_ftape_open failed");
- current->blocked = old_sigmask; /* restore mask */
- MOD_DEC_USE_COUNT; /* unlock module in memory */
- TRACE_EXIT;
- return result;
- } else {
- busy_flag = 1;
- /* Mask signals that will disturb proper operation of the
- * program that is calling.
- */
- current->blocked = old_sigmask | _DO_BLOCK;
- TRACE_EXIT;
- return 0;
- }
-}
-
-/* Close ftape device
- */
-static int ftape_close(struct inode *ino, struct file *filep)
-{
- TRACE_FUN(4, "ftape_close");
- int result;
-
- if (!busy_flag || MINOR(ino->i_rdev) != ftape_unit) {
- TRACE(1, "failed: not busy or wrong unit");
- TRACE_EXIT;
- return 0; /* keep busy_flag !(?) */
- }
- current->blocked = _BLOCK_ALL;
- result = _ftape_close();
- if (result < 0) {
- TRACE(1, "_ftape_close failed");
- }
- fdc_restore_drive_specs(); /* restore original values */
- ftape_failure = 1; /* inhibit any operation but open */
- busy_flag = 0;
- current->blocked = old_sigmask; /* restore before open state */
- TRACE_EXIT;
- MOD_DEC_USE_COUNT; /* unlock module in memory */
- return 0;
-}
-
-/* Ioctl for ftape device
- */
-static int ftape_ioctl(struct inode *ino, struct file *filep,
- unsigned int command, unsigned long arg)
-{
- TRACE_FUN(4, "ftape_ioctl");
- int result = -EIO;
- int old_sigmask;
-
- if (!busy_flag || MINOR(ino->i_rdev) != ftape_unit || ftape_failure) {
- TRACE(1, "failed: not busy, failure or wrong unit");
- TRACE_EXIT;
- return -EIO;
- }
- old_sigmask = current->blocked; /* save mask */
- current->blocked = _BLOCK_ALL;
- /* This will work as long as sizeof( void*) == sizeof( long)
- */
- result = _ftape_ioctl(command, (void *) arg);
- current->blocked = old_sigmask; /* restore mask */
- TRACE_EXIT;
- return result;
-}
-
-/* Read from tape device
- */
-static long ftape_read(struct inode *ino, struct file *fp,
- char *buff, unsigned long req_len)
-{
- TRACE_FUN(5, "ftape_read");
- int result = -EIO;
- int old_sigmask;
-
- TRACEi(5, "called with count:", (int) req_len);
- if (!busy_flag || MINOR(ino->i_rdev) != ftape_unit || ftape_failure) {
- TRACE(1, "failed: not busy, failure or wrong unit");
- TRACE_EXIT;
- return -EIO;
- }
- old_sigmask = current->blocked; /* save mask */
- current->blocked = _BLOCK_ALL;
- result = _ftape_read(buff, req_len);
- TRACEi(7, "return with count:", result);
- current->blocked = old_sigmask; /* restore mask */
- TRACE_EXIT;
- return result;
-}
-
-/* Write to tape device
- */
-static long ftape_write(struct inode *ino, struct file *fp,
- const char *buff, unsigned long req_len)
-{
- TRACE_FUN(8, "ftape_write");
- int result = -EIO;
- int old_sigmask;
-
- TRACEi(5, "called with count:", (int) req_len);
- if (!busy_flag || MINOR(ino->i_rdev) != ftape_unit || ftape_failure) {
- TRACE(1, "failed: not busy, failure or wrong unit");
- TRACE_EXIT;
- return -EIO;
- }
- old_sigmask = current->blocked; /* save mask */
- current->blocked = _BLOCK_ALL;
- result = _ftape_write(buff, req_len);
- TRACEi(7, "return with count:", result);
- current->blocked = old_sigmask; /* restore mask */
- TRACE_EXIT;
- return result;
-}
diff --git a/drivers/char/ftape/lowlevel/.cvsignore b/drivers/char/ftape/lowlevel/.cvsignore
new file mode 100644
index 000000000..4671378ae
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/.cvsignore
@@ -0,0 +1 @@
+.depend
diff --git a/drivers/char/ftape/lowlevel/Makefile b/drivers/char/ftape/lowlevel/Makefile
new file mode 100644
index 000000000..36834e85e
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/Makefile
@@ -0,0 +1,60 @@
+#
+# Copyright (C) 1996, 1997 Clau-Justus Heine.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; see the file COPYING. If not, write to
+# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+# $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/Makefile,v $
+# $Revision: 1.4 $
+# $Date: 1997/10/07 09:26:02 $
+#
+# Makefile for the lowlevel part QIC-40/80/3010/3020 floppy-tape
+# driver for Linux.
+#
+
+#
+# This isn't used inside the kernel, only for my private development
+# version
+#
+ifndef TOPDIR
+TOPDIR=../..
+include $(TOPDIR)/MCONFIG
+endif
+
+O_TARGET := ftape.o
+O_OBJS = ftape-init.o fdc-io.o fdc-isr.o \
+ ftape-bsm.o ftape-ctl.o ftape-read.o ftape-rw.o \
+ ftape-write.o ftape-io.o ftape-calibr.o ftape-ecc.o fc-10.o \
+ ftape-buffer.o ftape-format.o
+
+ifeq ($(CONFIG_FTAPE),y)
+O_OBJS += ftape-setup.o
+endif
+
+ifndef CONFIG_FT_NO_TRACE_AT_ALL
+O_OBJS += ftape-tracing.o
+endif
+
+ifeq ($(CONFIG_PROC_FS),y)
+ifeq ($(CONFIG_FT_PROC_FS),y)
+O_OBJS += ftape-proc.o
+endif
+endif
+
+OX_OBJS = ftape_syms.o
+
+M_OBJS = $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
+
diff --git a/drivers/char/ftape/fc-10.c b/drivers/char/ftape/lowlevel/fc-10.c
index d18d6501b..cbfd71f49 100644
--- a/drivers/char/ftape/fc-10.c
+++ b/drivers/char/ftape/lowlevel/fc-10.c
@@ -1,6 +1,6 @@
-/* Yo, Emacs! we're -*- Linux-C -*-
+/*
*
-
+
Copyright (C) 1993,1994 Jon Tombs.
This program is distributed in the hope that it will be useful,
@@ -37,81 +37,88 @@
Crider (gcrider@iclnet.org) for finding and locating this bug, as well as
testing the patch.
+ Modification on 11 December 96, by Claus Heine (claus@momo.math.rwth-aachen.de):
+ Modified a little to use variahle ft_fdc_base, ft_fdc_irq, ft_fdc_dma
+ instead of preprocessor symbols. Thus we can compile this into the module
+ or kernel and let the user specify the options as command line arguments.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/fc-10.c,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:04 $
*
* This file contains code for the CMS FC-10/FC-20 card.
*/
-#include <linux/ftape.h>
#include <asm/io.h>
+#include <linux/ftape.h>
+#include "../lowlevel/ftape-tracing.h"
+#include "../lowlevel/fdc-io.h"
+#include "../lowlevel/fc-10.h"
-#include "tracing.h"
-#include "fdc-io.h"
-#include "fc-10.h"
-
-#ifdef PROBE_FC10
-
-/* This code will only work if the FC-10 (or FC-20) is set to
- * use DMA channels 1, 2, or 3. DMA channels 5 and 7 seem to be
- * initialized by the same command as channels 1 and 3, respectively.
- */
-#if (FDC_DMA > 3)
-#error : The FC-10/20 must be set to use DMA channels 1, 2, or 3!
-#endif
-
-/* Only allow the FC-10/20 to use IRQ 3-7, or 9. Note that CMS's program
- * only accepts IRQ's 2-7, but in linux, IRQ 2 is the same as IRQ 9.
- */
-#if (FDC_IRQ < 3 || FDC_IRQ == 8 || FDC_IRQ > 9)
-#error : The FC-10/20 must be set to use IRQ levels 3 - 7, or 9!
-#error : Note IRQ 9 is the same as IRQ 2
-#endif
-
-unsigned short inbs_magic[] = {
+__u16 inbs_magic[] = {
0x3, 0x3, 0x0, 0x4, 0x7, 0x2, 0x5, 0x3, 0x1, 0x4,
0x3, 0x5, 0x2, 0x0, 0x3, 0x7, 0x4, 0x2,
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7
};
-unsigned short fc10_ports[] = {
+__u16 fc10_ports[] = {
0x180, 0x210, 0x2A0, 0x300, 0x330, 0x340, 0x370
};
int fc10_enable(void)
{
int i;
- byte cardConfig = 0x00;
- byte x;
+ __u8 cardConfig = 0x00;
+ __u8 x;
+ TRACE_FUN(ft_t_flow);
+/* This code will only work if the FC-10 (or FC-20) is set to
+ * use DMA channels 1, 2, or 3. DMA channels 5 and 7 seem to be
+ * initialized by the same command as channels 1 and 3, respectively.
+ */
+ if (ft_fdc_dma > 3) {
+ TRACE_ABORT(0, ft_t_err,
+"Error: The FC-10/20 must be set to use DMA channels 1, 2, or 3!");
+ }
+/* Only allow the FC-10/20 to use IRQ 3-7, or 9. Note that CMS's program
+ * only accepts IRQ's 2-7, but in linux, IRQ 2 is the same as IRQ 9.
+ */
+ if (ft_fdc_irq < 3 || ft_fdc_irq == 8 || ft_fdc_irq > 9) {
+ TRACE_ABORT(0, ft_t_err,
+"Error: The FC-10/20 must be set to use IRQ levels 3 - 7, or 9!\n"
+KERN_INFO "Note: IRQ 9 is the same as IRQ 2");
+ }
/* Clear state machine ???
*/
for (i = 0; i < NR_ITEMS(inbs_magic); i++) {
- inb(FDC_BASE + inbs_magic[i]);
+ inb(ft_fdc_base + inbs_magic[i]);
}
- outb(0x0, FDC_BASE);
+ outb(0x0, ft_fdc_base);
- x = inb(FDC_BASE);
+ x = inb(ft_fdc_base);
if (x == 0x13 || x == 0x93) {
for (i = 1; i < 8; i++) {
- if (inb(FDC_BASE + i) != x) {
- return 0;
+ if (inb(ft_fdc_base + i) != x) {
+ TRACE_EXIT 0;
}
}
} else {
- return 0;
+ TRACE_EXIT 0;
}
- outb(0x8, FDC_BASE);
+ outb(0x8, ft_fdc_base);
for (i = 0; i < 8; i++) {
- if (inb(FDC_BASE + i) != 0x0) {
- return 0;
+ if (inb(ft_fdc_base + i) != 0x0) {
+ TRACE_EXIT 0;
}
}
- outb(0x10, FDC_BASE);
+ outb(0x10, ft_fdc_base);
for (i = 0; i < 8; i++) {
- if (inb(FDC_BASE + i) != 0xff) {
- return 0;
+ if (inb(ft_fdc_base + i) != 0xff) {
+ TRACE_EXIT 0;
}
}
@@ -122,39 +129,47 @@ int fc10_enable(void)
/* Clear state machine again ???
*/
for (i = 0; i < NR_ITEMS(inbs_magic); i++) {
- inb(FDC_BASE + inbs_magic[i]);
+ inb(ft_fdc_base + inbs_magic[i]);
}
/* Send io port */
for (i = 0; i < NR_ITEMS(fc10_ports); i++)
- if (FDC_BASE == fc10_ports[i])
+ if (ft_fdc_base == fc10_ports[i])
cardConfig = i + 1;
- if (cardConfig == 0)
- return 0; /* Invalid I/O Port */
+ if (cardConfig == 0) {
+ TRACE_EXIT 0; /* Invalid I/O Port */
+ }
/* and IRQ - If using IRQ 9, tell the FC card it is actually IRQ 2 */
- if (FDC_IRQ != 9)
- cardConfig |= FDC_IRQ << 3;
+ if (ft_fdc_irq != 9)
+ cardConfig |= ft_fdc_irq << 3;
else
cardConfig |= 2 << 3;
/* and finally DMA Channel */
- cardConfig |= FDC_DMA << 6;
- outb(cardConfig, FDC_BASE); /* DMA [2 bits]/IRQ [3 bits]/BASE [3 bits] */
+ cardConfig |= ft_fdc_dma << 6;
+ outb(cardConfig, ft_fdc_base); /* DMA [2 bits]/IRQ [3 bits]/BASE [3 bits] */
/* Enable FC-10 ???
*/
outb(0, fdc.ccr);
- outb(0, FDC_BASE + 0x6);
- outb(8, fdc.dor);
- outb(8, fdc.dor);
- outb(1, FDC_BASE + 0x6);
-
+ outb(0, fdc.dor2);
+ outb(FDC_DMA_MODE /* 8 */, fdc.dor);
+ outb(FDC_DMA_MODE /* 8 */, fdc.dor);
+ outb(1, fdc.dor2);
+
+ /*************************************
+ *
+ * cH: why the hell should this be necessary? This is done
+ * by fdc_reset()!!!
+ *
+ *************************************/
/* Initialize fdc, select drive B:
*/
- outb(0x08, fdc.dor); /* assert reset, dma & irq enabled */
- outb(0x0c, fdc.dor); /* release reset */
- outb(0x2d, fdc.dor); /* select drive 1 */
-
- return (x == 0x93) ? 2 : 1;
+ outb(FDC_DMA_MODE, fdc.dor); /* assert reset, dma & irq enabled */
+ /* 0x08 */
+ outb(FDC_DMA_MODE|FDC_RESET_NOT, fdc.dor); /* release reset */
+ /* 0x08 | 0x04 = 0x0c */
+ outb(FDC_DMA_MODE|FDC_RESET_NOT|FDC_MOTOR_1|FTAPE_SEL_B, fdc.dor);
+ /* 0x08 | 0x04 | 0x20 | 0x01 = 0x2d */
+ /* select drive 1 */ /* why not drive 0 ???? */
+ TRACE_EXIT (x == 0x93) ? 2 : 1;
}
-
-#endif /* CMS_FC10_CONTROLLER */
diff --git a/drivers/char/ftape/fc-10.h b/drivers/char/ftape/lowlevel/fc-10.h
index f38d4685e..da7b88bca 100644
--- a/drivers/char/ftape/fc-10.h
+++ b/drivers/char/ftape/lowlevel/fc-10.h
@@ -2,7 +2,7 @@
#define _FC_10_H
/*
- * Copyright (C) 1994 Bas Laarhoven.
+ * Copyright (C) 1994-1996 Bas Laarhoven.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -19,12 +19,9 @@
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
- $Source: /home/bas/distr/ftape-2.03b/RCS/fc-10.h,v $
- $Author: bas $
- *
- $Revision: 1.3 $
- $Date: 1995/01/08 14:16:21 $
- $State: Beta $
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/fc-10.h,v $
+ * $Revision: 1.1 $
+ * $Date: 1997/09/19 09:05:22 $
*
* This file contains definitions for the FC-10 code
* of the QIC-40/80 floppy-tape driver for Linux.
diff --git a/drivers/char/ftape/lowlevel/fdc-io.c b/drivers/char/ftape/lowlevel/fdc-io.c
new file mode 100644
index 000000000..904a99ac5
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/fdc-io.c
@@ -0,0 +1,1439 @@
+/*
+ * Copyright (C) 1993-1996 Bas Laarhoven,
+ * (C) 1996-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/fdc-io.c,v $
+ * $Revision: 1.7.4.2 $
+ * $Date: 1997/11/16 14:48:17 $
+ *
+ * This file contains the low-level floppy disk interface code
+ * for the QIC-40/80/3010/3020 floppy-tape driver "ftape" for
+ * Linux.
+ */
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/version.h>
+#include <linux/interrupt.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/irq.h>
+
+#include <linux/ftape.h>
+#include <linux/qic117.h>
+#include "../lowlevel/ftape-tracing.h"
+#include "../lowlevel/fdc-io.h"
+#include "../lowlevel/fdc-isr.h"
+#include "../lowlevel/ftape-io.h"
+#include "../lowlevel/ftape-rw.h"
+#include "../lowlevel/ftape-ctl.h"
+#include "../lowlevel/ftape-calibr.h"
+#include "../lowlevel/fc-10.h"
+
+/* Global vars.
+ */
+int ftape_motor = 0;
+volatile int ftape_current_cylinder = -1;
+volatile fdc_mode_enum fdc_mode = fdc_idle;
+fdc_config_info fdc = {0};
+struct wait_queue *ftape_wait_intr = NULL;
+
+unsigned int ft_fdc_base = CONFIG_FT_FDC_BASE;
+unsigned int ft_fdc_irq = CONFIG_FT_FDC_IRQ;
+unsigned int ft_fdc_dma = CONFIG_FT_FDC_DMA;
+unsigned int ft_fdc_threshold = CONFIG_FT_FDC_THR; /* bytes */
+unsigned int ft_fdc_rate_limit = CONFIG_FT_FDC_MAX_RATE; /* bits/sec */
+int ft_probe_fc10 = CONFIG_FT_PROBE_FC10;
+int ft_mach2 = CONFIG_FT_MACH2;
+
+/* Local vars.
+ */
+static unsigned int fdc_calibr_count;
+static unsigned int fdc_calibr_time;
+static int fdc_status;
+volatile __u8 fdc_head; /* FDC head from sector id */
+volatile __u8 fdc_cyl; /* FDC track from sector id */
+volatile __u8 fdc_sect; /* FDC sector from sector id */
+static int fdc_data_rate = 500; /* data rate (Kbps) */
+static int fdc_rate_code = 0; /* data rate code (0 == 500 Kbps) */
+static int fdc_seek_rate = 2; /* step rate (msec) */
+static void (*do_ftape) (void);
+static int fdc_fifo_state; /* original fifo setting - fifo enabled */
+static int fdc_fifo_thr; /* original fifo setting - threshold */
+static int fdc_lock_state; /* original lock setting - locked */
+static int fdc_fifo_locked = 0; /* has fifo && lock set ? */
+static __u8 fdc_precomp = 0; /* default precomp. value (nsec) */
+static __u8 fdc_prec_code = 0; /* fdc precomp. select code */
+
+static char ftape_id[] = "ftape"; /* used by request irq and free irq */
+
+void fdc_catch_stray_interrupts(int count)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ if (count == 0) {
+ ft_expected_stray_interrupts = 0;
+ } else {
+ ft_expected_stray_interrupts += count;
+ }
+ restore_flags(flags);
+}
+
+/* Wait during a timeout period for a given FDC status.
+ * If usecs == 0 then just test status, else wait at least for usecs.
+ * Returns -ETIME on timeout. Function must be calibrated first !
+ */
+int fdc_wait(unsigned int usecs, __u8 mask, __u8 state)
+{
+ int count_1 = (fdc_calibr_count * usecs +
+ fdc_calibr_count - 1) / fdc_calibr_time;
+
+ do {
+ fdc_status = inb_p(fdc.msr);
+ if ((fdc_status & mask) == state) {
+ return 0;
+ }
+ } while (count_1-- >= 0);
+ return -ETIME;
+}
+
+int fdc_ready_wait(unsigned int usecs)
+{
+ return fdc_wait(usecs, FDC_DATA_READY | FDC_BUSY, FDC_DATA_READY);
+}
+
+/* Why can't we just use udelay()?
+ */
+static void fdc_usec_wait(unsigned int usecs)
+{
+ fdc_wait(usecs, 0, 1); /* will always timeout ! */
+}
+
+int fdc_ready_out_wait(unsigned int usecs)
+{
+ fdc_usec_wait(FT_RQM_DELAY); /* wait for valid RQM status */
+ return fdc_wait(usecs, FDC_DATA_OUT_READY, FDC_DATA_OUT_READY);
+}
+
+int fdc_ready_in_wait(unsigned int usecs)
+{
+ fdc_usec_wait(FT_RQM_DELAY); /* wait for valid RQM status */
+ return fdc_wait(usecs, FDC_DATA_OUT_READY, FDC_DATA_IN_READY);
+}
+
+void fdc_wait_calibrate(void)
+{
+ ftape_calibrate("fdc_wait",
+ fdc_usec_wait, &fdc_calibr_count, &fdc_calibr_time);
+}
+
+/* Wait for a (short) while for the FDC to become ready
+ * and transfer the next command byte.
+ * Return -ETIME on timeout on getting ready (depends on hardware!).
+ */
+static int fdc_write(const __u8 data)
+{
+ fdc_usec_wait(FT_RQM_DELAY); /* wait for valid RQM status */
+ if (fdc_wait(150, FDC_DATA_READY_MASK, FDC_DATA_IN_READY) < 0) {
+ return -ETIME;
+ } else {
+ outb(data, fdc.fifo);
+ return 0;
+ }
+}
+
+/* Wait for a (short) while for the FDC to become ready
+ * and transfer the next result byte.
+ * Return -ETIME if timeout on getting ready (depends on hardware!).
+ */
+static int fdc_read(__u8 * data)
+{
+ fdc_usec_wait(FT_RQM_DELAY); /* wait for valid RQM status */
+ if (fdc_wait(150, FDC_DATA_READY_MASK, FDC_DATA_OUT_READY) < 0) {
+ return -ETIME;
+ } else {
+ *data = inb(fdc.fifo);
+ return 0;
+ }
+}
+
+/* Output a cmd_len long command string to the FDC.
+ * The FDC should be ready to receive a new command or
+ * an error (EBUSY or ETIME) will occur.
+ */
+int fdc_command(const __u8 * cmd_data, int cmd_len)
+{
+ int result = 0;
+ unsigned long flags;
+ int count = cmd_len;
+ int retry = 0;
+#ifdef TESTING
+ static unsigned int last_time = 0;
+ unsigned int time;
+#endif
+ TRACE_FUN(ft_t_any);
+
+ fdc_usec_wait(FT_RQM_DELAY); /* wait for valid RQM status */
+ save_flags(flags);
+ cli();
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,30)
+ if (!in_interrupt())
+#else
+ if (!intr_count)
+#endif
+ /* Yes, I know, too much comments inside this function
+ * ...
+ *
+ * Yet another bug in the original driver. All that
+ * havoc is caused by the fact that the isr() sends
+ * itself a command to the floppy tape driver (pause,
+ * micro step pause). Now, the problem is that
+ * commands are transmitted via the fdc_seek
+ * command. But: the fdc performs seeks in the
+ * background i.e. it doesn't signal busy while
+ * sending the step pulses to the drive. Therefore the
+ * non-interrupt level driver has no chance to tell
+ * whether the isr() just has issued a seek. Therefore
+ * we HAVE TO have a look at the the ft_hide_interrupt
+ * flag: it signals the non-interrupt level part of
+ * the driver that it has to wait for the fdc until it
+ * has completet seeking.
+ *
+ * THIS WAS PRESUMABLY THE REASON FOR ALL THAT
+ * "fdc_read timeout" errors, I HOPE :-)
+ */
+ if (ft_hide_interrupt) {
+ restore_flags(flags);
+ TRACE(ft_t_info,
+ "Waiting for the isr() completing fdc_seek()");
+ if (fdc_interrupt_wait(2 * FT_SECOND) < 0) {
+ TRACE(ft_t_warn,
+ "Warning: timeout waiting for isr() seek to complete");
+ }
+ if (ft_hide_interrupt || !ft_seek_completed) {
+ /* There cannot be another
+ * interrupt. The isr() only stops
+ * the tape and the next interrupt
+ * won't come until we have send our
+ * command to the drive.
+ */
+ TRACE_ABORT(-EIO, ft_t_bug,
+ "BUG? isr() is still seeking?\n"
+ KERN_INFO "hide: %d\n"
+ KERN_INFO "seek: %d",
+ ft_hide_interrupt,
+ ft_seek_completed);
+
+ }
+ fdc_usec_wait(FT_RQM_DELAY); /* wait for valid RQM status */
+ save_flags(flags);
+ cli();
+ }
+ fdc_status = inb(fdc.msr);
+ if ((fdc_status & FDC_DATA_READY_MASK) != FDC_DATA_IN_READY) {
+ restore_flags(flags);
+ TRACE_ABORT(-EBUSY, ft_t_err, "fdc not ready");
+ }
+ fdc_mode = *cmd_data; /* used by isr */
+#ifdef TESTING
+ if (fdc_mode == FDC_SEEK) {
+ time = ftape_timediff(last_time, ftape_timestamp());
+ if (time < 6000) {
+ TRACE(ft_t_bug,"Warning: short timeout between seek commands: %d",
+ time);
+ }
+ }
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,30)
+ if (!in_interrupt()) {
+ /* shouldn't be cleared if called from isr
+ */
+ ft_interrupt_seen = 0;
+ }
+#else
+ if (!intr_count) {
+ /* shouldn't be cleared if called from isr
+ */
+ ft_interrupt_seen = 0;
+ }
+#endif
+ while (count) {
+ result = fdc_write(*cmd_data);
+ if (result < 0) {
+ TRACE(ft_t_fdc_dma,
+ "fdc_mode = %02x, status = %02x at index %d",
+ (int) fdc_mode, (int) fdc_status,
+ cmd_len - count);
+ if (++retry <= 3) {
+ TRACE(ft_t_warn, "fdc_write timeout, retry");
+ } else {
+ TRACE(ft_t_err, "fdc_write timeout, fatal");
+ /* recover ??? */
+ break;
+ }
+ } else {
+ --count;
+ ++cmd_data;
+ }
+ }
+#ifdef TESTING
+ if (fdc_mode == FDC_SEEK) {
+ last_time = ftape_timestamp();
+ }
+#endif
+ restore_flags(flags);
+ TRACE_EXIT result;
+}
+
+/* Input a res_len long result string from the FDC.
+ * The FDC should be ready to send the result or an error
+ * (EBUSY or ETIME) will occur.
+ */
+int fdc_result(__u8 * res_data, int res_len)
+{
+ int result = 0;
+ unsigned long flags;
+ int count = res_len;
+ int retry = 0;
+ TRACE_FUN(ft_t_any);
+
+ save_flags(flags);
+ cli();
+ fdc_status = inb(fdc.msr);
+ if ((fdc_status & FDC_DATA_READY_MASK) != FDC_DATA_OUT_READY) {
+ TRACE(ft_t_err, "fdc not ready");
+ result = -EBUSY;
+ } else while (count) {
+ if (!(fdc_status & FDC_BUSY)) {
+ restore_flags(flags);
+ TRACE_ABORT(-EIO, ft_t_err, "premature end of result phase");
+ }
+ result = fdc_read(res_data);
+ if (result < 0) {
+ TRACE(ft_t_fdc_dma,
+ "fdc_mode = %02x, status = %02x at index %d",
+ (int) fdc_mode,
+ (int) fdc_status,
+ res_len - count);
+ if (++retry <= 3) {
+ TRACE(ft_t_warn, "fdc_read timeout, retry");
+ } else {
+ TRACE(ft_t_err, "fdc_read timeout, fatal");
+ /* recover ??? */
+ break;
+ ++retry;
+ }
+ } else {
+ --count;
+ ++res_data;
+ }
+ }
+ restore_flags(flags);
+ fdc_usec_wait(FT_RQM_DELAY); /* allow FDC to negate BSY */
+ TRACE_EXIT result;
+}
+
+/* Handle command and result phases for
+ * commands without data phase.
+ */
+int fdc_issue_command(const __u8 * out_data, int out_count,
+ __u8 * in_data, int in_count)
+{
+ TRACE_FUN(ft_t_any);
+
+ if (out_count > 0) {
+ TRACE_CATCH(fdc_command(out_data, out_count),);
+ }
+ /* will take 24 - 30 usec for fdc_sense_drive_status and
+ * fdc_sense_interrupt_status commands.
+ * 35 fails sometimes (5/9/93 SJL)
+ * On a loaded system it incidentally takes longer than
+ * this for the fdc to get ready ! ?????? WHY ??????
+ * So until we know what's going on use a very long timeout.
+ */
+ TRACE_CATCH(fdc_ready_out_wait(500 /* usec */),);
+ if (in_count > 0) {
+ TRACE_CATCH(fdc_result(in_data, in_count),
+ TRACE(ft_t_err, "result phase aborted"));
+ }
+ TRACE_EXIT 0;
+}
+
+/* Wait for FDC interrupt with timeout (in milliseconds).
+ * Signals are blocked so the wait will not be aborted.
+ * Note: interrupts must be enabled ! (23/05/93 SJL)
+ */
+int fdc_interrupt_wait(unsigned int time)
+{
+ struct wait_queue wait = {current, NULL};
+ int current_blocked = current->blocked;
+ static int resetting = 0;
+ TRACE_FUN(ft_t_fdc_dma);
+
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,0,16)
+ if (waitqueue_active(&ftape_wait_intr)) {
+ TRACE_ABORT(-EIO, ft_t_err, "error: nested call");
+ }
+#else
+ if (ftape_wait_intr) {
+ TRACE_ABORT(-EIO, ft_t_err, "error: nested call");
+ }
+#endif
+ /* timeout time will be up to USPT microseconds too long ! */
+ current->timeout = jiffies + (1000 * time + FT_USPT - 1) / FT_USPT;
+ current->state = TASK_INTERRUPTIBLE;
+ current->blocked = _BLOCK_ALL; /* isn't this already set by the
+ * high level routines?
+ */
+ add_wait_queue(&ftape_wait_intr, &wait);
+ while (!ft_interrupt_seen && current->state != TASK_RUNNING) {
+ schedule(); /* sets TASK_RUNNING on timeout */
+ }
+ current->blocked = current_blocked; /* restore */
+ remove_wait_queue(&ftape_wait_intr, &wait);
+ /* the following IS necessary. True: as well
+ * wake_up_interruptible() as the schedule() set TASK_RUNNING
+ * when they wakeup a task, BUT: it may very well be that
+ * ft_interrupt_seen is already set to 1 when we enter here
+ * in which case schedule() gets never called, and
+ * TASK_RUNNING never set. This has the funny effect that we
+ * execute all the code until we leave kernel space, but then
+ * the task is stopped (a task CANNOT be preempted while in
+ * kernel mode. Sending a pair of SIGSTOP/SIGCONT to the
+ * tasks wakes it up again. Funny! :-)
+ */
+ current->state = TASK_RUNNING;
+ if (ft_interrupt_seen) { /* woken up by interrupt */
+ current->timeout = 0; /* interrupt hasn't cleared this */
+ ft_interrupt_seen = 0;
+ TRACE_EXIT 0;
+ }
+ /* Original comment:
+ * In first instance, next statement seems unnecessary since
+ * it will be cleared in fdc_command. However, a small part of
+ * the software seems to rely on this being cleared here
+ * (ftape_close might fail) so stick to it until things get fixed !
+ */
+ /* My deeply sought of knowledge:
+ * Behold NO! It is obvious. fdc_reset() doesn't call fdc_command()
+ * but nevertheless uses fdc_interrupt_wait(). OF COURSE this needs to
+ * be reset here.
+ */
+ ft_interrupt_seen = 0; /* clear for next call */
+ if (!resetting) {
+ resetting = 1; /* break infinite recursion if reset fails */
+ TRACE(ft_t_any, "cleanup reset");
+ fdc_reset();
+ resetting = 0;
+ }
+ TRACE_EXIT (current->signal & ~current->blocked) ? -EINTR : -ETIME;
+}
+
+/* Start/stop drive motor. Enable DMA mode.
+ */
+void fdc_motor(int motor)
+{
+ int unit = ft_drive_sel;
+ int data = unit | FDC_RESET_NOT | FDC_DMA_MODE;
+ TRACE_FUN(ft_t_any);
+
+ ftape_motor = motor;
+ if (ftape_motor) {
+ data |= FDC_MOTOR_0 << unit;
+ TRACE(ft_t_noise, "turning motor %d on", unit);
+ } else {
+ TRACE(ft_t_noise, "turning motor %d off", unit);
+ }
+ if (ft_mach2) {
+ outb_p(data, fdc.dor2);
+ } else {
+ outb_p(data, fdc.dor);
+ }
+ ftape_sleep(10 * FT_MILLISECOND);
+ TRACE_EXIT;
+}
+
+static void fdc_update_dsr(void)
+{
+ TRACE_FUN(ft_t_any);
+
+ TRACE(ft_t_flow, "rate = %d Kbps, precomp = %d ns",
+ fdc_data_rate, fdc_precomp);
+ if (fdc.type >= i82077) {
+ outb_p((fdc_rate_code & 0x03) | fdc_prec_code, fdc.dsr);
+ } else {
+ outb_p(fdc_rate_code & 0x03, fdc.ccr);
+ }
+ TRACE_EXIT;
+}
+
+void fdc_set_write_precomp(int precomp)
+{
+ TRACE_FUN(ft_t_any);
+
+ TRACE(ft_t_noise, "New precomp: %d nsec", precomp);
+ fdc_precomp = precomp;
+ /* write precompensation can be set in multiples of 41.67 nsec.
+ * round the parameter to the nearest multiple and convert it
+ * into a fdc setting. Note that 0 means default to the fdc,
+ * 7 is used instead of that.
+ */
+ fdc_prec_code = ((fdc_precomp + 21) / 42) << 2;
+ if (fdc_prec_code == 0 || fdc_prec_code > (6 << 2)) {
+ fdc_prec_code = 7 << 2;
+ }
+ fdc_update_dsr();
+ TRACE_EXIT;
+}
+
+/* Reprogram the 82078 registers to use Data Rate Table 1 on all drives.
+ */
+void fdc_set_drive_specs(void)
+{
+ __u8 cmd[] = { FDC_DRIVE_SPEC, 0x00, 0x00, 0x00, 0x00, 0xc0};
+ int result;
+ TRACE_FUN(ft_t_any);
+
+ TRACE(ft_t_flow, "Setting of drive specs called");
+ if (fdc.type >= i82078_1) {
+ cmd[1] = (0 << 5) | (2 << 2);
+ cmd[2] = (1 << 5) | (2 << 2);
+ cmd[3] = (2 << 5) | (2 << 2);
+ cmd[4] = (3 << 5) | (2 << 2);
+ result = fdc_command(cmd, NR_ITEMS(cmd));
+ if (result < 0) {
+ TRACE(ft_t_err, "Setting of drive specs failed");
+ }
+ }
+ TRACE_EXIT;
+}
+
+/* Select clock for fdc, must correspond with tape drive setting !
+ * This also influences the fdc timing so we must adjust some values.
+ */
+int fdc_set_data_rate(int rate)
+{
+ int bad_rate = 0;
+ TRACE_FUN(ft_t_any);
+
+ /* Select clock for fdc, must correspond with tape drive setting !
+ * This also influences the fdc timing so we must adjust some values.
+ */
+ TRACE(ft_t_fdc_dma, "new rate = %d", rate);
+ switch (rate) {
+ case 250:
+ fdc_rate_code = fdc_data_rate_250;
+ break;
+ case 500:
+ fdc_rate_code = fdc_data_rate_500;
+ break;
+ case 1000:
+ if (fdc.type < i82077) {
+ bad_rate = 1;
+ } else {
+ fdc_rate_code = fdc_data_rate_1000;
+ }
+ break;
+ case 2000:
+ if (fdc.type < i82078_1) {
+ bad_rate = 1;
+ } else {
+ fdc_rate_code = fdc_data_rate_2000;
+ }
+ break;
+ default:
+ bad_rate = 1;
+ }
+ if (bad_rate) {
+ TRACE_ABORT(-EIO,
+ ft_t_fdc_dma, "%d is not a valid data rate", rate);
+ }
+ fdc_data_rate = rate;
+ fdc_update_dsr();
+ fdc_set_seek_rate(fdc_seek_rate); /* clock changed! */
+ ftape_udelay(1000);
+ TRACE_EXIT 0;
+}
+
+/* keep the unit select if keep_select is != 0,
+ */
+static void fdc_dor_reset(int keep_select)
+{
+ __u8 fdc_ctl = ft_drive_sel;
+
+ if (keep_select != 0) {
+ fdc_ctl |= FDC_DMA_MODE;
+ if (ftape_motor) {
+ fdc_ctl |= FDC_MOTOR_0 << ft_drive_sel;
+ }
+ }
+ ftape_udelay(10); /* ??? but seems to be necessary */
+ if (ft_mach2) {
+ outb_p(fdc_ctl & 0x0f, fdc.dor);
+ outb_p(fdc_ctl, fdc.dor2);
+ } else {
+ outb_p(fdc_ctl, fdc.dor);
+ }
+ fdc_usec_wait(10); /* delay >= 14 fdc clocks */
+ if (keep_select == 0) {
+ fdc_ctl = 0;
+ }
+ fdc_ctl |= FDC_RESET_NOT;
+ if (ft_mach2) {
+ outb_p(fdc_ctl & 0x0f, fdc.dor);
+ outb_p(fdc_ctl, fdc.dor2);
+ } else {
+ outb_p(fdc_ctl, fdc.dor);
+ }
+}
+
+/* Reset the floppy disk controller. Leave the ftape_unit selected.
+ */
+void fdc_reset(void)
+{
+ int st0;
+ int i;
+ int dummy;
+ unsigned long flags;
+ TRACE_FUN(ft_t_any);
+
+ save_flags(flags);
+ cli();
+
+ fdc_dor_reset(1); /* keep unit selected */
+
+ fdc_mode = fdc_idle;
+
+ /* maybe the cli()/sti() pair is not necessary, BUT:
+ * the following line MUST be here. Otherwise fdc_interrupt_wait()
+ * won't wait. Note that fdc_reset() is called from
+ * ftape_dumb_stop() when the fdc is busy transferring data. In this
+ * case fdc_isr() MOST PROBABLY sets ft_interrupt_seen, and tries
+ * to get the result bytes from the fdc etc. CLASH.
+ */
+ ft_interrupt_seen = 0;
+
+ /* Program data rate
+ */
+ fdc_update_dsr(); /* restore data rate and precomp */
+
+ restore_flags(flags);
+
+ /*
+ * Wait for first polling cycle to complete
+ */
+ if (fdc_interrupt_wait(1 * FT_SECOND) < 0) {
+ TRACE(ft_t_err, "no drive polling interrupt!");
+ } else { /* clear all disk-changed statuses */
+ for (i = 0; i < 4; ++i) {
+ if(fdc_sense_interrupt_status(&st0, &dummy) != 0) {
+ TRACE(ft_t_err, "sense failed for %d", i);
+ }
+ if (i == ft_drive_sel) {
+ ftape_current_cylinder = dummy;
+ }
+ }
+ TRACE(ft_t_noise, "drive polling completed");
+ }
+ /*
+ * SPECIFY COMMAND
+ */
+ fdc_set_seek_rate(fdc_seek_rate);
+ /*
+ * DRIVE SPECIFICATION COMMAND (if fdc type known)
+ */
+ if (fdc.type >= i82078_1) {
+ fdc_set_drive_specs();
+ }
+ TRACE_EXIT;
+}
+
+#if !defined(CLK_48MHZ)
+# define CLK_48MHZ 1
+#endif
+
+/* When we're done, put the fdc into reset mode so that the regular
+ * floppy disk driver will figure out that something is wrong and
+ * initialize the controller the way it wants.
+ */
+void fdc_disable(void)
+{
+ __u8 cmd1[] = {FDC_CONFIGURE, 0x00, 0x00, 0x00};
+ __u8 cmd2[] = {FDC_LOCK};
+ __u8 cmd3[] = {FDC_UNLOCK};
+ __u8 stat[1];
+ TRACE_FUN(ft_t_flow);
+
+ if (!fdc_fifo_locked) {
+ fdc_reset();
+ TRACE_EXIT;
+ }
+ if (fdc_issue_command(cmd3, 1, stat, 1) < 0 || stat[0] != 0x00) {
+ fdc_dor_reset(0);
+ TRACE_ABORT(/**/, ft_t_bug,
+ "couldn't unlock fifo, configuration remains changed");
+ }
+ fdc_fifo_locked = 0;
+ if (CLK_48MHZ && fdc.type >= i82078) {
+ cmd1[0] |= FDC_CLK48_BIT;
+ }
+ cmd1[2] = ((fdc_fifo_state) ? 0 : 0x20) + (fdc_fifo_thr - 1);
+ if (fdc_command(cmd1, NR_ITEMS(cmd1)) < 0) {
+ fdc_dor_reset(0);
+ TRACE_ABORT(/**/, ft_t_bug,
+ "couldn't reconfigure fifo to old state");
+ }
+ if (fdc_lock_state &&
+ fdc_issue_command(cmd2, 1, stat, 1) < 0) {
+ fdc_dor_reset(0);
+ TRACE_ABORT(/**/, ft_t_bug, "couldn't lock old state again");
+ }
+ TRACE(ft_t_noise, "fifo restored: %sabled, thr. %d, %slocked",
+ fdc_fifo_state ? "en" : "dis",
+ fdc_fifo_thr, (fdc_lock_state) ? "" : "not ");
+ fdc_dor_reset(0);
+ TRACE_EXIT;
+}
+
+/* Specify FDC seek-rate (milliseconds)
+ */
+int fdc_set_seek_rate(int seek_rate)
+{
+ /* set step rate, dma mode, and minimal head load and unload times
+ */
+ __u8 in[3] = { FDC_SPECIFY, 1, (1 << 1)};
+
+ fdc_seek_rate = seek_rate;
+ in[1] |= (16 - (fdc_data_rate * fdc_seek_rate) / 500) << 4;
+
+ return fdc_command(in, 3);
+}
+
+/* Sense drive status: get unit's drive status (ST3)
+ */
+int fdc_sense_drive_status(int *st3)
+{
+ __u8 out[2];
+ __u8 in[1];
+ TRACE_FUN(ft_t_any);
+
+ out[0] = FDC_SENSED;
+ out[1] = ft_drive_sel;
+ TRACE_CATCH(fdc_issue_command(out, 2, in, 1),);
+ *st3 = in[0];
+ TRACE_EXIT 0;
+}
+
+/* Sense Interrupt Status command:
+ * should be issued at the end of each seek.
+ * get ST0 and current cylinder.
+ */
+int fdc_sense_interrupt_status(int *st0, int *current_cylinder)
+{
+ __u8 out[1];
+ __u8 in[2];
+ TRACE_FUN(ft_t_any);
+
+ out[0] = FDC_SENSEI;
+ TRACE_CATCH(fdc_issue_command(out, 1, in, 2),);
+ *st0 = in[0];
+ *current_cylinder = in[1];
+ TRACE_EXIT 0;
+}
+
+/* step to track
+ */
+int fdc_seek(int track)
+{
+ __u8 out[3];
+ int st0, pcn;
+#ifdef TESTING
+ unsigned int time;
+#endif
+ TRACE_FUN(ft_t_any);
+
+ out[0] = FDC_SEEK;
+ out[1] = ft_drive_sel;
+ out[2] = track;
+#ifdef TESTING
+ time = ftape_timestamp();
+#endif
+ /* We really need this command to work !
+ */
+ ft_seek_completed = 0;
+ TRACE_CATCH(fdc_command(out, 3),
+ fdc_reset();
+ TRACE(ft_t_noise, "destination was: %d, resetting FDC...",
+ track));
+ /* Handle interrupts until ft_seek_completed or timeout.
+ */
+ for (;;) {
+ TRACE_CATCH(fdc_interrupt_wait(2 * FT_SECOND),);
+ if (ft_seek_completed) {
+ TRACE_CATCH(fdc_sense_interrupt_status(&st0, &pcn),);
+ if ((st0 & ST0_SEEK_END) == 0) {
+ TRACE_ABORT(-EIO, ft_t_err,
+ "no seek-end after seek completion !??");
+ }
+ break;
+ }
+ }
+#ifdef TESTING
+ time = ftape_timediff(time, ftape_timestamp()) / ABS(track - ftape_current_cylinder);
+ if ((time < 900 || time > 3100) && ABS(track - ftape_current_cylinder) > 5) {
+ TRACE(ft_t_warn, "Wrong FDC STEP interval: %d usecs (%d)",
+ time, track - ftape_current_cylinder);
+ }
+#endif
+ /* Verify whether we issued the right tape command.
+ */
+ /* Verify that we seek to the proper track. */
+ if (pcn != track) {
+ TRACE_ABORT(-EIO, ft_t_err, "bad seek..");
+ }
+ ftape_current_cylinder = track;
+ TRACE_EXIT 0;
+}
+
+/* Recalibrate and wait until home.
+ */
+int fdc_recalibrate(void)
+{
+ __u8 out[2];
+ int st0;
+ int pcn;
+ int retry;
+ int old_seek_rate = fdc_seek_rate;
+ TRACE_FUN(ft_t_any);
+
+ TRACE_CATCH(fdc_set_seek_rate(6),);
+ out[0] = FDC_RECAL;
+ out[1] = ft_drive_sel;
+ ft_seek_completed = 0;
+ TRACE_CATCH(fdc_command(out, 2),);
+ /* Handle interrupts until ft_seek_completed or timeout.
+ */
+ for (retry = 0;; ++retry) {
+ TRACE_CATCH(fdc_interrupt_wait(2 * FT_SECOND),);
+ if (ft_seek_completed) {
+ TRACE_CATCH(fdc_sense_interrupt_status(&st0, &pcn),);
+ if ((st0 & ST0_SEEK_END) == 0) {
+ if (retry < 1) {
+ continue; /* some drives/fdc's
+ * give an extra interrupt
+ */
+ } else {
+ TRACE_ABORT(-EIO, ft_t_err,
+ "no seek-end after seek completion !??");
+ }
+ }
+ break;
+ }
+ }
+ ftape_current_cylinder = pcn;
+ if (pcn != 0) {
+ TRACE(ft_t_err, "failed: resulting track = %d", pcn);
+ }
+ TRACE_CATCH(fdc_set_seek_rate(old_seek_rate),);
+ TRACE_EXIT 0;
+}
+
+static int perpend_mode = 0; /* set if fdc is in perpendicular mode */
+
+static int perpend_off(void)
+{
+ __u8 perpend[] = {FDC_PERPEND, 0x00};
+ TRACE_FUN(ft_t_any);
+
+ if (perpend_mode) {
+ /* Turn off perpendicular mode */
+ perpend[1] = 0x80;
+ TRACE_CATCH(fdc_command(perpend, 2),
+ TRACE(ft_t_err,"Perpendicular mode exit failed!"));
+ perpend_mode = 0;
+ }
+ TRACE_EXIT 0;
+}
+
+static int handle_perpend(int segment_id)
+{
+ __u8 perpend[] = {FDC_PERPEND, 0x00};
+ TRACE_FUN(ft_t_any);
+
+ /* When writing QIC-3020 tapes, turn on perpendicular mode
+ * if tape is moving in forward direction (even tracks).
+ */
+ if (ft_qic_std == QIC_TAPE_QIC3020 &&
+ ((segment_id / ft_segments_per_track) & 1) == 0) {
+/* FIXME: some i82077 seem to support perpendicular mode as
+ * well.
+ */
+#if 0
+ if (fdc.type < i82077AA) {}
+#else
+ if (fdc.type < i82077 && ft_data_rate < 1000) {
+#endif
+ /* fdc does not support perpendicular mode: complain
+ */
+ TRACE_ABORT(-EIO, ft_t_err,
+ "Your FDC does not support QIC-3020.");
+ }
+ perpend[1] = 0x03 /* 0x83 + (0x4 << ft_drive_sel) */ ;
+ TRACE_CATCH(fdc_command(perpend, 2),
+ TRACE(ft_t_err,"Perpendicular mode entry failed!"));
+ TRACE(ft_t_flow, "Perpendicular mode set");
+ perpend_mode = 1;
+ TRACE_EXIT 0;
+ }
+ TRACE_EXIT perpend_off();
+}
+
+static inline void fdc_setup_dma(char mode,
+ volatile void *addr, unsigned int count)
+{
+ /* Program the DMA controller.
+ */
+ disable_dma(fdc.dma);
+ clear_dma_ff(fdc.dma);
+ set_dma_mode(fdc.dma, mode);
+ set_dma_addr(fdc.dma, virt_to_bus((void*)addr));
+ set_dma_count(fdc.dma, count);
+#ifdef GCC_2_4_5_BUG
+ /* This seemingly stupid construction confuses the gcc-2.4.5
+ * code generator enough to create correct code.
+ */
+ if (1) {
+ int i;
+
+ for (i = 0; i < 1; ++i) {
+ ftape_udelay(1);
+ }
+ }
+#endif
+ enable_dma(fdc.dma);
+}
+
+/* Setup fdc and dma for formatting the next segment
+ */
+int fdc_setup_formatting(buffer_struct * buff)
+{
+ unsigned long flags;
+ __u8 out[6] = {
+ FDC_FORMAT, 0x00, 3, 4 * FT_SECTORS_PER_SEGMENT, 0x00, 0x6b
+ };
+ TRACE_FUN(ft_t_any);
+
+ TRACE_CATCH(handle_perpend(buff->segment_id),);
+ /* Program the DMA controller.
+ */
+ TRACE(ft_t_fdc_dma,
+ "phys. addr. = %lx", virt_to_bus((void*) buff->ptr));
+ save_flags(flags);
+ cli(); /* could be called from ISR ! */
+ fdc_setup_dma(DMA_MODE_WRITE, buff->ptr, FT_SECTORS_PER_SEGMENT * 4);
+ /* Issue FDC command to start reading/writing.
+ */
+ out[1] = ft_drive_sel;
+ out[4] = buff->gap3;
+ TRACE_CATCH(fdc_setup_error = fdc_command(out, sizeof(out)),
+ restore_flags(flags); fdc_mode = fdc_idle);
+ restore_flags(flags);
+ TRACE_EXIT 0;
+}
+
+
+/* Setup Floppy Disk Controller and DMA to read or write the next cluster
+ * of good sectors from or to the current segment.
+ */
+int fdc_setup_read_write(buffer_struct * buff, __u8 operation)
+{
+ unsigned long flags;
+ __u8 out[9];
+ int dma_mode;
+ TRACE_FUN(ft_t_any);
+
+ switch(operation) {
+ case FDC_VERIFY:
+ if (fdc.type < i82077) {
+ operation = FDC_READ;
+ }
+ case FDC_READ:
+ case FDC_READ_DELETED:
+ dma_mode = DMA_MODE_READ;
+ TRACE(ft_t_fdc_dma, "xfer %d sectors to 0x%p",
+ buff->sector_count, buff->ptr);
+ TRACE_CATCH(perpend_off(),);
+ break;
+ case FDC_WRITE_DELETED:
+ TRACE(ft_t_noise, "deleting segment %d", buff->segment_id);
+ case FDC_WRITE:
+ dma_mode = DMA_MODE_WRITE;
+ /* When writing QIC-3020 tapes, turn on perpendicular mode
+ * if tape is moving in forward direction (even tracks).
+ */
+ TRACE_CATCH(handle_perpend(buff->segment_id),);
+ TRACE(ft_t_fdc_dma, "xfer %d sectors from 0x%p",
+ buff->sector_count, buff->ptr);
+ break;
+ default:
+ TRACE_ABORT(-EIO,
+ ft_t_bug, "bug: illegal operation parameter");
+ }
+ TRACE(ft_t_fdc_dma, "phys. addr. = %lx",virt_to_bus((void*)buff->ptr));
+ save_flags(flags);
+ cli(); /* could be called from ISR ! */
+ if (operation != FDC_VERIFY) {
+ fdc_setup_dma(dma_mode, buff->ptr,
+ FT_SECTOR_SIZE * buff->sector_count);
+ }
+ /* Issue FDC command to start reading/writing.
+ */
+ out[0] = operation;
+ out[1] = ft_drive_sel;
+ out[2] = buff->cyl;
+ out[3] = buff->head;
+ out[4] = buff->sect + buff->sector_offset;
+ out[5] = 3; /* Sector size of 1K. */
+ out[6] = out[4] + buff->sector_count - 1; /* last sector */
+ out[7] = 109; /* Gap length. */
+ out[8] = 0xff; /* No limit to transfer size. */
+ TRACE(ft_t_fdc_dma, "C: 0x%02x, H: 0x%02x, R: 0x%02x, cnt: 0x%02x",
+ out[2], out[3], out[4], out[6] - out[4] + 1);
+ restore_flags(flags);
+ TRACE_CATCH(fdc_setup_error = fdc_command(out, 9),fdc_mode = fdc_idle);
+ TRACE_EXIT 0;
+}
+
+int fdc_fifo_threshold(__u8 threshold,
+ int *fifo_state, int *lock_state, int *fifo_thr)
+{
+ const __u8 cmd0[] = {FDC_DUMPREGS};
+ __u8 cmd1[] = {FDC_CONFIGURE, 0, (0x0f & (threshold - 1)), 0};
+ const __u8 cmd2[] = {FDC_LOCK};
+ const __u8 cmd3[] = {FDC_UNLOCK};
+ __u8 reg[10];
+ __u8 stat;
+ int i;
+ int result;
+ TRACE_FUN(ft_t_any);
+
+ if (CLK_48MHZ && fdc.type >= i82078) {
+ cmd1[0] |= FDC_CLK48_BIT;
+ }
+ /* Dump fdc internal registers for examination
+ */
+ TRACE_CATCH(fdc_command(cmd0, NR_ITEMS(cmd0)),
+ TRACE(ft_t_warn, "dumpreg cmd failed, fifo unchanged"));
+ /* Now read fdc internal registers from fifo
+ */
+ for (i = 0; i < (int)NR_ITEMS(reg); ++i) {
+ fdc_read(&reg[i]);
+ TRACE(ft_t_fdc_dma, "Register %d = 0x%02x", i, reg[i]);
+ }
+ if (fifo_state && lock_state && fifo_thr) {
+ *fifo_state = (reg[8] & 0x20) == 0;
+ *lock_state = reg[7] & 0x80;
+ *fifo_thr = 1 + (reg[8] & 0x0f);
+ }
+ TRACE(ft_t_noise,
+ "original fifo state: %sabled, threshold %d, %slocked",
+ ((reg[8] & 0x20) == 0) ? "en" : "dis",
+ 1 + (reg[8] & 0x0f), (reg[7] & 0x80) ? "" : "not ");
+ /* If fdc is already locked, unlock it first ! */
+ if (reg[7] & 0x80) {
+ fdc_ready_wait(100);
+ TRACE_CATCH(fdc_issue_command(cmd3, NR_ITEMS(cmd3), &stat, 1),
+ TRACE(ft_t_bug, "FDC unlock command failed, "
+ "configuration unchanged"));
+ }
+ fdc_fifo_locked = 0;
+ /* Enable fifo and set threshold at xx bytes to allow a
+ * reasonably large latency and reduce number of dma bursts.
+ */
+ fdc_ready_wait(100);
+ if ((result = fdc_command(cmd1, NR_ITEMS(cmd1))) < 0) {
+ TRACE(ft_t_bug, "configure cmd failed, fifo unchanged");
+ }
+ /* Now lock configuration so reset will not change it
+ */
+ if(fdc_issue_command(cmd2, NR_ITEMS(cmd2), &stat, 1) < 0 ||
+ stat != 0x10) {
+ TRACE_ABORT(-EIO, ft_t_bug,
+ "FDC lock command failed, stat = 0x%02x", stat);
+ }
+ fdc_fifo_locked = 1;
+ TRACE_EXIT result;
+}
+
+static int fdc_fifo_enable(void)
+{
+ TRACE_FUN(ft_t_any);
+
+ if (fdc_fifo_locked) {
+ TRACE_ABORT(0, ft_t_warn, "Fifo not enabled because locked");
+ }
+ TRACE_CATCH(fdc_fifo_threshold(ft_fdc_threshold /* bytes */,
+ &fdc_fifo_state,
+ &fdc_lock_state,
+ &fdc_fifo_thr),);
+ TRACE_CATCH(fdc_fifo_threshold(ft_fdc_threshold /* bytes */,
+ NULL, NULL, NULL),);
+ TRACE_EXIT 0;
+}
+
+/* Determine fd controller type
+ */
+static __u8 fdc_save_state[2] = {0, 0};
+
+int fdc_probe(void)
+{
+ __u8 cmd[1];
+ __u8 stat[16]; /* must be able to hold dumpregs & save results */
+ int i;
+ TRACE_FUN(ft_t_any);
+
+ /* Try to find out what kind of fd controller we have to deal with
+ * Scheme borrowed from floppy driver:
+ * first try if FDC_DUMPREGS command works
+ * (this indicates that we have a 82072 or better)
+ * then try the FDC_VERSION command (82072 doesn't support this)
+ * then try the FDC_UNLOCK command (some older 82077's don't support this)
+ * then try the FDC_PARTID command (82078's support this)
+ */
+ cmd[0] = FDC_DUMPREGS;
+ if (fdc_issue_command(cmd, 1, stat, 1) != 0) {
+ TRACE_ABORT(no_fdc, ft_t_bug, "No FDC found");
+ }
+ if (stat[0] == 0x80) {
+ /* invalid command: must be pre 82072 */
+ TRACE_ABORT(i8272,
+ ft_t_warn, "Type 8272A/765A compatible FDC found");
+ }
+ fdc_result(&stat[1], 9);
+ fdc_save_state[0] = stat[7];
+ fdc_save_state[1] = stat[8];
+ cmd[0] = FDC_VERSION;
+ if (fdc_issue_command(cmd, 1, stat, 1) < 0 || stat[0] == 0x80) {
+ TRACE_ABORT(i8272, ft_t_warn, "Type 82072 FDC found");
+ }
+ if (*stat != 0x90) {
+ TRACE_ABORT(i8272, ft_t_warn, "Unknown FDC found");
+ }
+ cmd[0] = FDC_UNLOCK;
+ if(fdc_issue_command(cmd, 1, stat, 1) < 0 || stat[0] != 0x00) {
+ TRACE_ABORT(i8272, ft_t_warn,
+ "Type pre-1991 82077 FDC found, "
+ "treating it like a 82072");
+ }
+ if (fdc_save_state[0] & 0x80) { /* was locked */
+ cmd[0] = FDC_LOCK; /* restore lock */
+ (void)fdc_issue_command(cmd, 1, stat, 1);
+ TRACE(ft_t_warn, "FDC is already locked");
+ }
+ /* Test for a i82078 FDC */
+ cmd[0] = FDC_PARTID;
+ if (fdc_issue_command(cmd, 1, stat, 1) < 0 || stat[0] == 0x80) {
+ /* invalid command: not a i82078xx type FDC */
+ for (i = 0; i < 4; ++i) {
+ outb_p(i, fdc.tdr);
+ if ((inb_p(fdc.tdr) & 0x03) != i) {
+ TRACE_ABORT(i82077,
+ ft_t_warn, "Type 82077 FDC found");
+ }
+ }
+ TRACE_ABORT(i82077AA, ft_t_warn, "Type 82077AA FDC found");
+ }
+ /* FDC_PARTID cmd succeeded */
+ switch (stat[0] >> 5) {
+ case 0x0:
+ /* i82078SL or i82078-1. The SL part cannot run at
+ * 2Mbps (the SL and -1 dies are identical; they are
+ * speed graded after production, according to Intel).
+ * Some SL's can be detected by doing a SAVE cmd and
+ * look at bit 7 of the first byte (the SEL3V# bit).
+ * If it is 0, the part runs off 3Volts, and hence it
+ * is a SL.
+ */
+ cmd[0] = FDC_SAVE;
+ if(fdc_issue_command(cmd, 1, stat, 16) < 0) {
+ TRACE(ft_t_err, "FDC_SAVE failed. Dunno why");
+ /* guess we better claim the fdc to be a i82078 */
+ TRACE_ABORT(i82078,
+ ft_t_warn,
+ "Type i82078 FDC (i suppose) found");
+ }
+ if ((stat[0] & FDC_SEL3V_BIT)) {
+ /* fdc running off 5Volts; Pray that it's a i82078-1
+ */
+ TRACE_ABORT(i82078_1, ft_t_warn,
+ "Type i82078-1 or 5Volt i82078SL FDC found");
+ }
+ TRACE_ABORT(i82078, ft_t_warn,
+ "Type 3Volt i82078SL FDC (1Mbps) found");
+ case 0x1:
+ case 0x2: /* S82078B */
+ /* The '78B isn't '78 compatible. Detect it as a '77AA */
+ TRACE_ABORT(i82077AA, ft_t_warn, "Type i82077AA FDC found");
+ case 0x3: /* NSC PC8744 core; used in several super-IO chips */
+ TRACE_ABORT(i82077AA,
+ ft_t_warn, "Type 82077AA compatible FDC found");
+ default:
+ TRACE(ft_t_warn, "A previously undetected FDC found");
+ TRACE_ABORT(i82077AA, ft_t_warn,
+ "Treating it as a 82077AA. Please report partid= %d",
+ stat[0]);
+ } /* switch(stat[ 0] >> 5) */
+ TRACE_EXIT no_fdc;
+}
+
+static int fdc_request_regions(void)
+{
+ TRACE_FUN(ft_t_flow);
+
+ if (ft_mach2 || ft_probe_fc10) {
+ if (check_region(fdc.sra, 8) < 0) {
+#ifndef BROKEN_FLOPPY_DRIVER
+ TRACE_EXIT -EBUSY;
+#else
+ TRACE(ft_t_warn,
+"address 0x%03x occupied (by floppy driver?), using it anyway", fdc.sra);
+#endif
+ }
+ request_region(fdc.sra, 8, "fdc (ft)");
+ } else {
+ if (check_region(fdc.sra, 6) < 0 ||
+ check_region(fdc.dir, 1) < 0) {
+#ifndef BROKEN_FLOPPY_DRIVER
+ TRACE_EXIT -EBUSY;
+#else
+ TRACE(ft_t_warn,
+"address 0x%03x occupied (by floppy driver?), using it anyway", fdc.sra);
+#endif
+ }
+ request_region(fdc.sra, 6, "fdc (ft)");
+ request_region(fdc.sra + 7, 1, "fdc (ft)");
+ }
+ TRACE_EXIT 0;
+}
+
+void fdc_release_regions(void)
+{
+ TRACE_FUN(ft_t_flow);
+
+ if (fdc.sra != 0) {
+ if (fdc.dor2 != 0) {
+ release_region(fdc.sra, 8);
+ } else {
+ release_region(fdc.sra, 6);
+ release_region(fdc.dir, 1);
+ }
+ }
+ TRACE_EXIT;
+}
+
+static int fdc_config_regs(unsigned int fdc_base,
+ unsigned int fdc_irq,
+ unsigned int fdc_dma)
+{
+ TRACE_FUN(ft_t_flow);
+
+ fdc.irq = fdc_irq;
+ fdc.dma = fdc_dma;
+ fdc.sra = fdc_base;
+ fdc.srb = fdc_base + 1;
+ fdc.dor = fdc_base + 2;
+ fdc.tdr = fdc_base + 3;
+ fdc.msr = fdc.dsr = fdc_base + 4;
+ fdc.fifo = fdc_base + 5;
+ fdc.dir = fdc.ccr = fdc_base + 7;
+ fdc.dor2 = (ft_mach2 || ft_probe_fc10) ? fdc_base + 6 : 0;
+ TRACE_CATCH(fdc_request_regions(), fdc.sra = 0);
+ TRACE_EXIT 0;
+}
+
+static int fdc_config(void)
+{
+ static int already_done = 0;
+ TRACE_FUN(ft_t_any);
+
+ if (already_done) {
+ TRACE_CATCH(fdc_request_regions(),);
+ *(fdc.hook) = fdc_isr; /* hook our handler in */
+ TRACE_EXIT 0;
+ }
+ if (ft_probe_fc10) {
+ int fc_type;
+
+ TRACE_CATCH(fdc_config_regs(ft_fdc_base,
+ ft_fdc_irq, ft_fdc_dma),);
+ fc_type = fc10_enable();
+ if (fc_type != 0) {
+ TRACE(ft_t_warn, "FC-%c0 controller found", '0' + fc_type);
+ fdc.type = fc10;
+ fdc.hook = &do_ftape;
+ *(fdc.hook) = fdc_isr; /* hook our handler in */
+ already_done = 1;
+ TRACE_EXIT 0;
+ } else {
+ TRACE(ft_t_warn, "FC-10/20 controller not found");
+ fdc_release_regions();
+ fdc.type = no_fdc;
+ ft_probe_fc10 = 0;
+ ft_fdc_base = 0x3f0;
+ ft_fdc_irq = 6;
+ ft_fdc_dma = 2;
+ }
+ }
+ TRACE(ft_t_warn, "fdc base: 0x%x, irq: %d, dma: %d",
+ ft_fdc_base, ft_fdc_irq, ft_fdc_dma);
+ TRACE_CATCH(fdc_config_regs(ft_fdc_base, ft_fdc_irq, ft_fdc_dma),);
+ fdc.hook = &do_ftape;
+ *(fdc.hook) = fdc_isr; /* hook our handler in */
+ already_done = 1;
+ TRACE_EXIT 0;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VER(1,3,70)
+static void ftape_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+#else
+static void ftape_interrupt(int irq, struct pt_regs *regs)
+#endif
+{
+ void (*handler) (void) = *fdc.hook;
+ TRACE_FUN(ft_t_any);
+
+ *fdc.hook = NULL;
+ if (handler) {
+ handler();
+ } else {
+ TRACE(ft_t_bug, "Unexpected ftape interrupt");
+ }
+ TRACE_EXIT;
+}
+
+int fdc_grab_irq_and_dma(void)
+{
+ TRACE_FUN(ft_t_any);
+
+ if (fdc.hook == &do_ftape) {
+ /* Get fast interrupt handler.
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VER(1,3,70)
+ if (request_irq(fdc.irq, ftape_interrupt,
+ SA_INTERRUPT, "ft", ftape_id)) {
+ TRACE_ABORT(-EIO, ft_t_bug,
+ "Unable to grab IRQ%d for ftape driver",
+ fdc.irq);
+ }
+#else
+ if (request_irq(fdc.irq, ftape_interrupt, SA_INTERRUPT,
+ ftape_id)) {
+ TRACE_ABORT(-EIO, ft_t_bug,
+ "Unable to grab IRQ%d for ftape driver",
+ fdc.irq);
+ }
+#endif
+ if (request_dma(fdc.dma, ftape_id)) {
+#if LINUX_VERSION_CODE >= KERNEL_VER(1,3,70)
+ free_irq(fdc.irq, ftape_id);
+#else
+ free_irq(fdc.irq);
+#endif
+ TRACE_ABORT(-EIO, ft_t_bug,
+ "Unable to grab DMA%d for ftape driver",
+ fdc.dma);
+ }
+ enable_irq(fdc.irq);
+ }
+ if (ft_fdc_base != 0x3f0 && (ft_fdc_dma == 2 || ft_fdc_irq == 6)) {
+ /* Using same dma channel or irq as standard fdc, need
+ * to disable the dma-gate on the std fdc. This
+ * couldn't be done in the floppy driver as some
+ * laptops are using the dma-gate to enter a low power
+ * or even suspended state :-(
+ */
+ outb_p(FDC_RESET_NOT, 0x3f2);
+ TRACE(ft_t_noise, "DMA-gate on standard fdc disabled");
+ }
+ TRACE_EXIT 0;
+}
+
+int fdc_release_irq_and_dma(void)
+{
+ TRACE_FUN(ft_t_any);
+
+ if (fdc.hook == &do_ftape) {
+ disable_dma(fdc.dma); /* just in case... */
+ free_dma(fdc.dma);
+ disable_irq(fdc.irq);
+#if LINUX_VERSION_CODE >= KERNEL_VER(1,3,70)
+ free_irq(fdc.irq, ftape_id);
+#else
+ free_irq(fdc.irq);
+#endif
+ }
+ if (ft_fdc_base != 0x3f0 && (ft_fdc_dma == 2 || ft_fdc_irq == 6)) {
+ /* Using same dma channel as standard fdc, need to
+ * disable the dma-gate on the std fdc. This couldn't
+ * be done in the floppy driver as some laptops are
+ * using the dma-gate to enter a low power or even
+ * suspended state :-(
+ */
+ outb_p(FDC_RESET_NOT | FDC_DMA_MODE, 0x3f2);
+ TRACE(ft_t_noise, "DMA-gate on standard fdc enabled again");
+ }
+ TRACE_EXIT 0;
+}
+
+int fdc_init(void)
+{
+ TRACE_FUN(ft_t_any);
+
+ /* find a FDC to use */
+ TRACE_CATCH(fdc_config(),);
+ TRACE_CATCH(fdc_grab_irq_and_dma(), fdc_release_regions());
+ ftape_motor = 0;
+ fdc_catch_stray_interrupts(0); /* clear number of awainted
+ * stray interrupte
+ */
+ fdc_catch_stray_interrupts(1); /* one always comes (?) */
+ TRACE(ft_t_flow, "resetting fdc");
+ fdc_set_seek_rate(2); /* use nominal QIC step rate */
+ fdc_reset(); /* init fdc & clear track counters */
+ if (fdc.type == no_fdc) { /* no FC-10 or FC-20 found */
+ fdc.type = fdc_probe();
+ fdc_reset(); /* update with new knowledge */
+ }
+ if (fdc.type == no_fdc) {
+ fdc_release_irq_and_dma();
+ fdc_release_regions();
+ TRACE_EXIT -ENXIO;
+ }
+ if (fdc.type >= i82077) {
+ if (fdc_fifo_enable() < 0) {
+ TRACE(ft_t_warn, "couldn't enable fdc fifo !");
+ } else {
+ TRACE(ft_t_flow, "fdc fifo enabled and locked");
+ }
+ }
+ TRACE_EXIT 0;
+}
diff --git a/drivers/char/ftape/lowlevel/fdc-io.h b/drivers/char/ftape/lowlevel/fdc-io.h
new file mode 100644
index 000000000..77b2f5bb9
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/fdc-io.h
@@ -0,0 +1,257 @@
+#ifndef _FDC_IO_H
+#define _FDC_IO_H
+
+/*
+ * Copyright (C) 1993-1996 Bas Laarhoven,
+ * (C) 1996-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/fdc-io.h,v $
+ * $Revision: 1.3 $
+ * $Date: 1997/10/05 19:18:06 $
+ *
+ * This file contains the declarations for the low level
+ * functions that communicate with the floppy disk controller,
+ * for the QIC-40/80/3010/3020 floppy-tape driver "ftape" for
+ * Linux.
+ */
+
+#include <linux/fdreg.h>
+
+#include "../lowlevel/ftape-bsm.h"
+
+#define FDC_SK_BIT (0x20)
+#define FDC_MT_BIT (0x80)
+
+#define FDC_READ (FD_READ & ~(FDC_SK_BIT | FDC_MT_BIT))
+#define FDC_WRITE (FD_WRITE & ~FDC_MT_BIT)
+#define FDC_READ_DELETED (0x4c)
+#define FDC_WRITE_DELETED (0x49)
+#define FDC_VERIFY (0x56)
+#define FDC_READID (0x4a)
+#define FDC_SENSED (0x04)
+#define FDC_SENSEI (FD_SENSEI)
+#define FDC_FORMAT (FD_FORMAT)
+#define FDC_RECAL (FD_RECALIBRATE)
+#define FDC_SEEK (FD_SEEK)
+#define FDC_SPECIFY (FD_SPECIFY)
+#define FDC_RECALIBR (FD_RECALIBRATE)
+#define FDC_VERSION (FD_VERSION)
+#define FDC_PERPEND (FD_PERPENDICULAR)
+#define FDC_DUMPREGS (FD_DUMPREGS)
+#define FDC_LOCK (FD_LOCK)
+#define FDC_UNLOCK (FD_UNLOCK)
+#define FDC_CONFIGURE (FD_CONFIGURE)
+#define FDC_DRIVE_SPEC (0x8e) /* i82078 has this (any others?) */
+#define FDC_PARTID (0x18) /* i82078 has this */
+#define FDC_SAVE (0x2e) /* i82078 has this (any others?) */
+#define FDC_RESTORE (0x4e) /* i82078 has this (any others?) */
+
+#define FDC_STATUS_MASK (STATUS_BUSY | STATUS_DMA | STATUS_DIR | STATUS_READY)
+#define FDC_DATA_READY (STATUS_READY)
+#define FDC_DATA_OUTPUT (STATUS_DIR)
+#define FDC_DATA_READY_MASK (STATUS_READY | STATUS_DIR)
+#define FDC_DATA_OUT_READY (STATUS_READY | STATUS_DIR)
+#define FDC_DATA_IN_READY (STATUS_READY)
+#define FDC_BUSY (STATUS_BUSY)
+#define FDC_CLK48_BIT (0x80)
+#define FDC_SEL3V_BIT (0x40)
+
+#define ST0_INT_MASK (ST0_INTR)
+#define FDC_INT_NORMAL (ST0_INTR & 0x00)
+#define FDC_INT_ABNORMAL (ST0_INTR & 0x40)
+#define FDC_INT_INVALID (ST0_INTR & 0x80)
+#define FDC_INT_READYCH (ST0_INTR & 0xC0)
+#define ST0_SEEK_END (ST0_SE)
+#define ST3_TRACK_0 (ST3_TZ)
+
+#define FDC_RESET_NOT (0x04)
+#define FDC_DMA_MODE (0x08)
+#define FDC_MOTOR_0 (0x10)
+#define FDC_MOTOR_1 (0x20)
+
+typedef struct {
+ void (**hook) (void); /* our wedge into the isr */
+ enum {
+ no_fdc, i8272, i82077, i82077AA, fc10,
+ i82078, i82078_1
+ } type; /* FDC type */
+ unsigned int irq; /* FDC irq nr */
+ unsigned int dma; /* FDC dma channel nr */
+ __u16 sra; /* Status register A (PS/2 only) */
+ __u16 srb; /* Status register B (PS/2 only) */
+ __u16 dor; /* Digital output register */
+ __u16 tdr; /* Tape Drive Register (82077SL-1 &
+ 82078 only) */
+ __u16 msr; /* Main Status Register */
+ __u16 dsr; /* Datarate Select Register (8207x only) */
+ __u16 fifo; /* Data register / Fifo on 8207x */
+ __u16 dir; /* Digital Input Register */
+ __u16 ccr; /* Configuration Control Register */
+ __u16 dor2; /* Alternate dor on MACH-2 controller,
+ also used with FC-10, meaning unknown */
+} fdc_config_info;
+
+typedef enum {
+ fdc_data_rate_250 = 2,
+ fdc_data_rate_300 = 1, /* any fdc in default configuration */
+ fdc_data_rate_500 = 0,
+ fdc_data_rate_1000 = 3,
+ fdc_data_rate_2000 = 1, /* i82078-1: when using Data Rate Table #2 */
+} fdc_data_rate_type;
+
+typedef enum {
+ fdc_idle = 0,
+ fdc_reading_data = FDC_READ,
+ fdc_seeking = FDC_SEEK,
+ fdc_writing_data = FDC_WRITE,
+ fdc_deleting = FDC_WRITE_DELETED,
+ fdc_reading_id = FDC_READID,
+ fdc_recalibrating = FDC_RECAL,
+ fdc_formatting = FDC_FORMAT,
+ fdc_verifying = FDC_VERIFY
+} fdc_mode_enum;
+
+typedef enum {
+ waiting = 0,
+ reading,
+ writing,
+ formatting,
+ verifying,
+ deleting,
+ done,
+ error,
+ mmapped,
+} buffer_state_enum;
+
+typedef struct {
+ __u8 *address;
+ volatile buffer_state_enum status;
+ volatile __u8 *ptr;
+ volatile unsigned int bytes;
+ volatile unsigned int segment_id;
+
+ /* bitmap for remainder of segment not yet handled.
+ * one bit set for each bad sector that must be skipped.
+ */
+ volatile SectorMap bad_sector_map;
+
+ /* bitmap with bad data blocks in data buffer.
+ * the errors in this map may be retried.
+ */
+ volatile SectorMap soft_error_map;
+
+ /* bitmap with bad data blocks in data buffer
+ * the errors in this map may not be retried.
+ */
+ volatile SectorMap hard_error_map;
+
+ /* retry counter for soft errors.
+ */
+ volatile int retry;
+
+ /* sectors to skip on retry ???
+ */
+ volatile unsigned int skip;
+
+ /* nr of data blocks in data buffer
+ */
+ volatile unsigned int data_offset;
+
+ /* offset in segment for first sector to be handled.
+ */
+ volatile unsigned int sector_offset;
+
+ /* size of cluster of good sectors to be handled.
+ */
+ volatile unsigned int sector_count;
+
+ /* size of remaining part of segment to be handled.
+ */
+ volatile unsigned int remaining;
+
+ /* points to next segment (contiguous) to be handled,
+ * or is zero if no read-ahead is allowed.
+ */
+ volatile unsigned int next_segment;
+
+ /* flag being set if deleted data was read.
+ */
+ volatile int deleted;
+
+ /* floppy coordinates of first sector in segment */
+ volatile __u8 head;
+ volatile __u8 cyl;
+ volatile __u8 sect;
+
+ /* gap to use when formatting */
+ __u8 gap3;
+ /* flag set when buffer is mmaped */
+ int mmapped;
+} buffer_struct;
+
+/*
+ * fdc-io.c defined public variables
+ */
+extern volatile fdc_mode_enum fdc_mode;
+extern int fdc_setup_error; /* outdated ??? */
+extern struct wait_queue *ftape_wait_intr;
+extern int ftape_motor; /* fdc motor line state */
+extern volatile int ftape_current_cylinder; /* track nr FDC thinks we're on */
+extern volatile __u8 fdc_head; /* FDC head */
+extern volatile __u8 fdc_cyl; /* FDC track */
+extern volatile __u8 fdc_sect; /* FDC sector */
+extern fdc_config_info fdc; /* FDC hardware configuration */
+
+extern unsigned int ft_fdc_base;
+extern unsigned int ft_fdc_irq;
+extern unsigned int ft_fdc_dma;
+extern unsigned int ft_fdc_threshold;
+extern unsigned int ft_fdc_rate_limit;
+extern int ft_probe_fc10;
+extern int ft_mach2;
+/*
+ * fdc-io.c defined public functions
+ */
+extern void fdc_catch_stray_interrupts(int count);
+extern int fdc_ready_wait(unsigned int timeout);
+extern int fdc_command(const __u8 * cmd_data, int cmd_len);
+extern int fdc_result(__u8 * res_data, int res_len);
+extern int fdc_issue_command(const __u8 * out_data, int out_count,
+ __u8 * in_data, int in_count);
+extern int fdc_interrupt_wait(unsigned int time);
+extern int fdc_set_seek_rate(int seek_rate);
+extern int fdc_seek(int track);
+extern int fdc_sense_drive_status(int *st3);
+extern void fdc_motor(int motor);
+extern void fdc_reset(void);
+extern int fdc_recalibrate(void);
+extern void fdc_disable(void);
+extern int fdc_fifo_threshold(__u8 threshold,
+ int *fifo_state, int *lock_state, int *fifo_thr);
+extern void fdc_wait_calibrate(void);
+extern int fdc_sense_interrupt_status(int *st0, int *current_cylinder);
+extern void fdc_save_drive_specs(void);
+extern void fdc_restore_drive_specs(void);
+extern int fdc_set_data_rate(int rate);
+extern void fdc_set_write_precomp(int precomp);
+extern int fdc_release_irq_and_dma(void);
+extern void fdc_release_regions(void);
+extern int fdc_init(void);
+extern int fdc_setup_read_write(buffer_struct * buff, __u8 operation);
+extern int fdc_setup_formatting(buffer_struct * buff);
+#endif
diff --git a/drivers/char/ftape/lowlevel/fdc-isr.c b/drivers/char/ftape/lowlevel/fdc-isr.c
new file mode 100644
index 000000000..4c3975add
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/fdc-isr.c
@@ -0,0 +1,1185 @@
+/*
+ * Copyright (C) 1994-1996 Bas Laarhoven,
+ * (C) 1996-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/fdc-isr.c,v $
+ * $Revision: 1.9 $
+ * $Date: 1997/10/17 23:01:53 $
+ *
+ * This file contains the interrupt service routine and
+ * associated code for the QIC-40/80/3010/3020 floppy-tape driver
+ * "ftape" for Linux.
+ */
+
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#define volatile /* */
+
+#include <linux/ftape.h>
+#include <linux/qic117.h>
+#include "../lowlevel/ftape-tracing.h"
+#include "../lowlevel/fdc-isr.h"
+#include "../lowlevel/fdc-io.h"
+#include "../lowlevel/ftape-ctl.h"
+#include "../lowlevel/ftape-rw.h"
+#include "../lowlevel/ftape-io.h"
+#include "../lowlevel/ftape-calibr.h"
+#include "../lowlevel/ftape-bsm.h"
+
+/* Global vars.
+ */
+volatile int ft_expected_stray_interrupts = 0;
+volatile int ft_interrupt_seen = 0;
+volatile int ft_seek_completed = 0;
+volatile int ft_hide_interrupt = 0;
+/* Local vars.
+ */
+typedef enum {
+ no_error = 0, id_am_error = 0x01, id_crc_error = 0x02,
+ data_am_error = 0x04, data_crc_error = 0x08,
+ no_data_error = 0x10, overrun_error = 0x20,
+} error_cause;
+static int stop_read_ahead = 0;
+
+
+static void print_error_cause(int cause)
+{
+ TRACE_FUN(ft_t_any);
+
+ switch (cause) {
+ case no_data_error:
+ TRACE(ft_t_noise, "no data error");
+ break;
+ case id_am_error:
+ TRACE(ft_t_noise, "id am error");
+ break;
+ case id_crc_error:
+ TRACE(ft_t_noise, "id crc error");
+ break;
+ case data_am_error:
+ TRACE(ft_t_noise, "data am error");
+ break;
+ case data_crc_error:
+ TRACE(ft_t_noise, "data crc error");
+ break;
+ case overrun_error:
+ TRACE(ft_t_noise, "overrun error");
+ break;
+ default:
+ }
+ TRACE_EXIT;
+}
+
+static char *fdc_mode_txt(fdc_mode_enum mode)
+{
+ switch (mode) {
+ case fdc_idle:
+ return "fdc_idle";
+ case fdc_reading_data:
+ return "fdc_reading_data";
+ case fdc_seeking:
+ return "fdc_seeking";
+ case fdc_writing_data:
+ return "fdc_writing_data";
+ case fdc_reading_id:
+ return "fdc_reading_id";
+ case fdc_recalibrating:
+ return "fdc_recalibrating";
+ case fdc_formatting:
+ return "fdc_formatting";
+ case fdc_verifying:
+ return "fdc_verifying";
+ default:
+ return "unknown";
+ }
+}
+
+static inline error_cause decode_irq_cause(fdc_mode_enum mode, __u8 st[])
+{
+ error_cause cause = no_error;
+ TRACE_FUN(ft_t_any);
+
+ /* Valid st[], decode cause of interrupt.
+ */
+ switch (st[0] & ST0_INT_MASK) {
+ case FDC_INT_NORMAL:
+ TRACE(ft_t_fdc_dma,"normal completion: %s",fdc_mode_txt(mode));
+ break;
+ case FDC_INT_ABNORMAL:
+ TRACE(ft_t_flow, "abnormal completion %s", fdc_mode_txt(mode));
+ TRACE(ft_t_fdc_dma, "ST0: 0x%02x, ST1: 0x%02x, ST2: 0x%02x",
+ st[0], st[1], st[2]);
+ TRACE(ft_t_fdc_dma,
+ "C: 0x%02x, H: 0x%02x, R: 0x%02x, N: 0x%02x",
+ st[3], st[4], st[5], st[6]);
+ if (st[1] & 0x01) {
+ if (st[2] & 0x01) {
+ cause = data_am_error;
+ } else {
+ cause = id_am_error;
+ }
+ } else if (st[1] & 0x20) {
+ if (st[2] & 0x20) {
+ cause = data_crc_error;
+ } else {
+ cause = id_crc_error;
+ }
+ } else if (st[1] & 0x04) {
+ cause = no_data_error;
+ } else if (st[1] & 0x10) {
+ cause = overrun_error;
+ }
+ print_error_cause(cause);
+ break;
+ case FDC_INT_INVALID:
+ TRACE(ft_t_flow, "invalid completion %s", fdc_mode_txt(mode));
+ break;
+ case FDC_INT_READYCH:
+ if (st[0] & ST0_SEEK_END) {
+ TRACE(ft_t_flow, "drive poll completed");
+ } else {
+ TRACE(ft_t_flow, "ready change %s",fdc_mode_txt(mode));
+ }
+ break;
+ default:
+ break;
+ }
+ TRACE_EXIT cause;
+}
+
+static void update_history(error_cause cause)
+{
+ switch (cause) {
+ case id_am_error:
+ ft_history.id_am_errors++;
+ break;
+ case id_crc_error:
+ ft_history.id_crc_errors++;
+ break;
+ case data_am_error:
+ ft_history.data_am_errors++;
+ break;
+ case data_crc_error:
+ ft_history.data_crc_errors++;
+ break;
+ case overrun_error:
+ ft_history.overrun_errors++;
+ break;
+ case no_data_error:
+ ft_history.no_data_errors++;
+ break;
+ default:
+ }
+}
+
+static void skip_bad_sector(buffer_struct * buff)
+{
+ TRACE_FUN(ft_t_any);
+
+ /* Mark sector as soft error and skip it
+ */
+ if (buff->remaining > 0) {
+ ++buff->sector_offset;
+ ++buff->data_offset;
+ --buff->remaining;
+ buff->ptr += FT_SECTOR_SIZE;
+ buff->bad_sector_map >>= 1;
+ } else {
+ /* Hey, what is this????????????? C code: if we shift
+ * more than 31 bits, we get no shift. That's bad!!!!!!
+ */
+ ++buff->sector_offset; /* hack for error maps */
+ TRACE(ft_t_warn, "skipping last sector in segment");
+ }
+ TRACE_EXIT;
+}
+
+static void update_error_maps(buffer_struct * buff, unsigned int error_offset)
+{
+ int hard = 0;
+ TRACE_FUN(ft_t_any);
+
+ if (buff->retry < FT_SOFT_RETRIES) {
+ buff->soft_error_map |= (1 << error_offset);
+ } else {
+ buff->hard_error_map |= (1 << error_offset);
+ buff->soft_error_map &= ~buff->hard_error_map;
+ buff->retry = -1; /* will be set to 0 in setup_segment */
+ hard = 1;
+ }
+ TRACE(ft_t_noise, "sector %d : %s error\n"
+ KERN_INFO "hard map: 0x%08lx\n"
+ KERN_INFO "soft map: 0x%08lx",
+ FT_SECTOR(error_offset), hard ? "hard" : "soft",
+ (long) buff->hard_error_map, (long) buff->soft_error_map);
+ TRACE_EXIT;
+}
+
+static void print_progress(buffer_struct *buff, error_cause cause)
+{
+ TRACE_FUN(ft_t_any);
+
+ switch (cause) {
+ case no_error:
+ TRACE(ft_t_flow,"%d Sector(s) transfered", buff->sector_count);
+ break;
+ case no_data_error:
+ TRACE(ft_t_flow, "Sector %d not found",
+ FT_SECTOR(buff->sector_offset));
+ break;
+ case overrun_error:
+ /* got an overrun error on the first byte, must be a
+ * hardware problem
+ */
+ TRACE(ft_t_bug,
+ "Unexpected error: failing DMA or FDC controller ?");
+ break;
+ case data_crc_error:
+ TRACE(ft_t_flow, "Error in sector %d",
+ FT_SECTOR(buff->sector_offset - 1));
+ break;
+ case id_crc_error:
+ case id_am_error:
+ case data_am_error:
+ TRACE(ft_t_flow, "Error in sector %d",
+ FT_SECTOR(buff->sector_offset));
+ break;
+ default:
+ TRACE(ft_t_flow, "Unexpected error at sector %d",
+ FT_SECTOR(buff->sector_offset));
+ break;
+ }
+ TRACE_EXIT;
+}
+
+/*
+ * Error cause: Amount xferred: Action:
+ *
+ * id_am_error 0 mark bad and skip
+ * id_crc_error 0 mark bad and skip
+ * data_am_error 0 mark bad and skip
+ * data_crc_error % 1024 mark bad and skip
+ * no_data_error 0 retry on write
+ * mark bad and skip on read
+ * overrun_error [ 0..all-1 ] mark bad and skip
+ * no_error all continue
+ */
+
+/* the arg `sector' is returned by the fdc and tells us at which sector we
+ * are positioned at (relative to starting sector of segment)
+ */
+static void determine_verify_progress(buffer_struct *buff,
+ error_cause cause,
+ __u8 sector)
+{
+ TRACE_FUN(ft_t_any);
+
+ if (cause == no_error && sector == 1) {
+ buff->sector_offset = FT_SECTORS_PER_SEGMENT;
+ buff->remaining = 0;
+ if (TRACE_LEVEL >= ft_t_flow) {
+ print_progress(buff, cause);
+ }
+ } else {
+ buff->sector_offset = sector - buff->sect;
+ buff->remaining = FT_SECTORS_PER_SEGMENT - buff->sector_offset;
+ TRACE(ft_t_noise, "%ssector offset: 0x%04x",
+ (cause == no_error) ? "unexpected " : "",
+ buff->sector_offset);
+ switch (cause) {
+ case overrun_error:
+ break;
+#if 0
+ case no_data_error:
+ buff->retry = FT_SOFT_RETRIES;
+ if (buff->hard_error_map &&
+ buff->sector_offset > 1 &&
+ (buff->hard_error_map &
+ (1 << (buff->sector_offset-2)))) {
+ buff->retry --;
+ }
+ break;
+#endif
+ default:
+ buff->retry = FT_SOFT_RETRIES;
+ break;
+ }
+ if (TRACE_LEVEL >= ft_t_flow) {
+ print_progress(buff, cause);
+ }
+ /* Sector_offset points to the problem area Now adjust
+ * sector_offset so it always points one past he failing
+ * sector. I.e. skip the bad sector.
+ */
+ ++buff->sector_offset;
+ --buff->remaining;
+ update_error_maps(buff, buff->sector_offset - 1);
+ }
+ TRACE_EXIT;
+}
+
+static void determine_progress(buffer_struct *buff,
+ error_cause cause,
+ __u8 sector)
+{
+ unsigned int dma_residue;
+ TRACE_FUN(ft_t_any);
+
+ /* Using less preferred order of disable_dma and
+ * get_dma_residue because this seems to fail on at least one
+ * system if reversed!
+ */
+ dma_residue = get_dma_residue(fdc.dma);
+ disable_dma(fdc.dma);
+ if (cause != no_error || dma_residue != 0) {
+ TRACE(ft_t_noise, "%sDMA residue: 0x%04x",
+ (cause == no_error) ? "unexpected " : "",
+ dma_residue);
+ /* adjust to actual value: */
+ if (dma_residue == 0) {
+ /* this happens sometimes with overrun errors.
+ * I don't know whether we could ignore the
+ * overrun error. Play save.
+ */
+ buff->sector_count --;
+ } else {
+ buff->sector_count -= ((dma_residue +
+ (FT_SECTOR_SIZE - 1)) /
+ FT_SECTOR_SIZE);
+ }
+ }
+ /* Update var's influenced by the DMA operation.
+ */
+ if (buff->sector_count > 0) {
+ buff->sector_offset += buff->sector_count;
+ buff->data_offset += buff->sector_count;
+ buff->ptr += (buff->sector_count *
+ FT_SECTOR_SIZE);
+ buff->remaining -= buff->sector_count;
+ buff->bad_sector_map >>= buff->sector_count;
+ }
+ if (TRACE_LEVEL >= ft_t_flow) {
+ print_progress(buff, cause);
+ }
+ if (cause != no_error) {
+ if (buff->remaining == 0) {
+ TRACE(ft_t_warn, "foo?\n"
+ KERN_INFO "count : %d\n"
+ KERN_INFO "offset: %d\n"
+ KERN_INFO "soft : %08x\n"
+ KERN_INFO "hard : %08x",
+ buff->sector_count,
+ buff->sector_offset,
+ buff->soft_error_map,
+ buff->hard_error_map);
+ }
+ /* Sector_offset points to the problem area, except if we got
+ * a data_crc_error. In that case it points one past the
+ * failing sector.
+ *
+ * Now adjust sector_offset so it always points one past he
+ * failing sector. I.e. skip the bad sector.
+ */
+ if (cause != data_crc_error) {
+ skip_bad_sector(buff);
+ }
+ update_error_maps(buff, buff->sector_offset - 1);
+ }
+ TRACE_EXIT;
+}
+
+static int calc_steps(int cmd)
+{
+ if (ftape_current_cylinder > cmd) {
+ return ftape_current_cylinder - cmd;
+ } else {
+ return ftape_current_cylinder + cmd;
+ }
+}
+
+static void pause_tape(int retry, int mode)
+{
+ int result;
+ __u8 out[3] = {FDC_SEEK, ft_drive_sel, 0};
+ TRACE_FUN(ft_t_any);
+
+ /* We'll use a raw seek command to get the tape to rewind and
+ * stop for a retry.
+ */
+ ++ft_history.rewinds;
+ if (qic117_cmds[ftape_current_command].non_intr) {
+ TRACE(ft_t_warn, "motion command may be issued too soon");
+ }
+ if (retry && (mode == fdc_reading_data ||
+ mode == fdc_reading_id ||
+ mode == fdc_verifying)) {
+ ftape_current_command = QIC_MICRO_STEP_PAUSE;
+ ftape_might_be_off_track = 1;
+ } else {
+ ftape_current_command = QIC_PAUSE;
+ }
+ out[2] = calc_steps(ftape_current_command);
+ result = fdc_command(out, 3); /* issue QIC_117 command */
+ ftape_current_cylinder = out[ 2];
+ if (result < 0) {
+ TRACE(ft_t_noise, "qic-pause failed, status = %d", result);
+ } else {
+ ft_location.known = 0;
+ ft_runner_status = idle;
+ ft_hide_interrupt = 1;
+ ftape_tape_running = 0;
+ }
+ TRACE_EXIT;
+}
+
+static void continue_xfer(buffer_struct *buff,
+ fdc_mode_enum mode,
+ unsigned int skip)
+{
+ int write = 0;
+ TRACE_FUN(ft_t_any);
+
+ if (mode == fdc_writing_data || mode == fdc_deleting) {
+ write = 1;
+ }
+ /* This part can be removed if it never happens
+ */
+ if (skip > 0 &&
+ (ft_runner_status != running ||
+ (write && (buff->status != writing)) ||
+ (!write && (buff->status != reading &&
+ buff->status != verifying)))) {
+ TRACE(ft_t_err, "unexpected runner/buffer state %d/%d",
+ ft_runner_status, buff->status);
+ buff->status = error;
+ /* finish this buffer: */
+ (void)ftape_next_buffer(ft_queue_head);
+ ft_runner_status = aborting;
+ fdc_mode = fdc_idle;
+ } else if (buff->remaining > 0 && ftape_calc_next_cluster(buff) > 0) {
+ /* still sectors left in current segment, continue
+ * with this segment
+ */
+ if (fdc_setup_read_write(buff, mode) < 0) {
+ /* failed, abort operation
+ */
+ buff->bytes = buff->ptr - buff->address;
+ buff->status = error;
+ /* finish this buffer: */
+ (void)ftape_next_buffer(ft_queue_head);
+ ft_runner_status = aborting;
+ fdc_mode = fdc_idle;
+ }
+ } else {
+ /* current segment completed
+ */
+ unsigned int last_segment = buff->segment_id;
+ int eot = ((last_segment + 1) % ft_segments_per_track) == 0;
+ unsigned int next = buff->next_segment; /* 0 means stop ! */
+
+ buff->bytes = buff->ptr - buff->address;
+ buff->status = done;
+ buff = ftape_next_buffer(ft_queue_head);
+ if (eot) {
+ /* finished last segment on current track,
+ * can't continue
+ */
+ ft_runner_status = logical_eot;
+ fdc_mode = fdc_idle;
+ TRACE_EXIT;
+ }
+ if (next <= 0) {
+ /* don't continue with next segment
+ */
+ TRACE(ft_t_noise, "no %s allowed, stopping tape",
+ (write) ? "write next" : "read ahead");
+ pause_tape(0, mode);
+ ft_runner_status = idle; /* not quite true until
+ * next irq
+ */
+ TRACE_EXIT;
+ }
+ /* continue with next segment
+ */
+ if (buff->status != waiting) {
+ TRACE(ft_t_noise, "all input buffers %s, pausing tape",
+ (write) ? "empty" : "full");
+ pause_tape(0, mode);
+ ft_runner_status = idle; /* not quite true until
+ * next irq
+ */
+ TRACE_EXIT;
+ }
+ if (write && next != buff->segment_id) {
+ TRACE(ft_t_noise,
+ "segments out of order, aborting write");
+ ft_runner_status = do_abort;
+ fdc_mode = fdc_idle;
+ TRACE_EXIT;
+ }
+ ftape_setup_new_segment(buff, next, 0);
+ if (stop_read_ahead) {
+ buff->next_segment = 0;
+ stop_read_ahead = 0;
+ }
+ if (ftape_calc_next_cluster(buff) == 0 ||
+ fdc_setup_read_write(buff, mode) != 0) {
+ TRACE(ft_t_err, "couldn't start %s-ahead",
+ write ? "write" : "read");
+ ft_runner_status = do_abort;
+ fdc_mode = fdc_idle;
+ } else {
+ /* keep on going */
+ switch (ft_driver_state) {
+ case reading: buff->status = reading; break;
+ case verifying: buff->status = verifying; break;
+ case writing: buff->status = writing; break;
+ case deleting: buff->status = deleting; break;
+ default:
+ TRACE(ft_t_err,
+ "BUG: ft_driver_state %d should be one out of "
+ "{reading, writing, verifying, deleting}",
+ ft_driver_state);
+ buff->status = write ? writing : reading;
+ break;
+ }
+ }
+ }
+ TRACE_EXIT;
+}
+
+static void retry_sector(buffer_struct *buff,
+ int mode,
+ unsigned int skip)
+{
+ TRACE_FUN(ft_t_any);
+
+ TRACE(ft_t_noise, "%s error, will retry",
+ (mode == fdc_writing_data || mode == fdc_deleting) ? "write" : "read");
+ pause_tape(1, mode);
+ ft_runner_status = aborting;
+ buff->status = error;
+ buff->skip = skip;
+ TRACE_EXIT;
+}
+
+static unsigned int find_resume_point(buffer_struct *buff)
+{
+ int i = 0;
+ SectorMap mask;
+ SectorMap map;
+ TRACE_FUN(ft_t_any);
+
+ /* This function is to be called after all variables have been
+ * updated to point past the failing sector.
+ * If there are any soft errors before the failing sector,
+ * find the first soft error and return the sector offset.
+ * Otherwise find the last hard error.
+ * Note: there should always be at least one hard or soft error !
+ */
+ if (buff->sector_offset < 1 || buff->sector_offset > 32) {
+ TRACE(ft_t_bug, "BUG: sector_offset = %d",
+ buff->sector_offset);
+ TRACE_EXIT 0;
+ }
+ if (buff->sector_offset >= 32) { /* C-limitation on shift ! */
+ mask = 0xffffffff;
+ } else {
+ mask = (1 << buff->sector_offset) - 1;
+ }
+ map = buff->soft_error_map & mask;
+ if (map) {
+ while ((map & (1 << i)) == 0) {
+ ++i;
+ }
+ TRACE(ft_t_noise, "at sector %d", FT_SECTOR(i));
+ } else {
+ map = buff->hard_error_map & mask;
+ i = buff->sector_offset - 1;
+ if (map) {
+ while ((map & (1 << i)) == 0) {
+ --i;
+ }
+ TRACE(ft_t_noise, "after sector %d", FT_SECTOR(i));
+ ++i; /* first sector after last hard error */
+ } else {
+ TRACE(ft_t_bug, "BUG: no soft or hard errors");
+ }
+ }
+ TRACE_EXIT i;
+}
+
+/* check possible dma residue when formatting, update position record in
+ * buffer struct. This is, of course, modelled after determine_progress(), but
+ * we don't need to set up for retries because the format process cannot be
+ * interrupted (except at the end of the tape track).
+ */
+static int determine_fmt_progress(buffer_struct *buff, error_cause cause)
+{
+ unsigned int dma_residue;
+ TRACE_FUN(ft_t_any);
+
+ /* Using less preferred order of disable_dma and
+ * get_dma_residue because this seems to fail on at least one
+ * system if reversed!
+ */
+ dma_residue = get_dma_residue(fdc.dma);
+ disable_dma(fdc.dma);
+ if (cause != no_error || dma_residue != 0) {
+ TRACE(ft_t_info, "DMA residue = 0x%04x", dma_residue);
+ fdc_mode = fdc_idle;
+ switch(cause) {
+ case no_error:
+ ft_runner_status = aborting;
+ buff->status = idle;
+ break;
+ case overrun_error:
+ /* got an overrun error on the first byte, must be a
+ * hardware problem
+ */
+ TRACE(ft_t_bug,
+ "Unexpected error: failing DMA controller ?");
+ ft_runner_status = do_abort;
+ buff->status = error;
+ break;
+ default:
+ TRACE(ft_t_noise, "Unexpected error at segment %d",
+ buff->segment_id);
+ ft_runner_status = do_abort;
+ buff->status = error;
+ break;
+ }
+ TRACE_EXIT -EIO; /* can only retry entire track in format mode
+ */
+ }
+ /* Update var's influenced by the DMA operation.
+ */
+ buff->ptr += FT_SECTORS_PER_SEGMENT * 4;
+ buff->bytes -= FT_SECTORS_PER_SEGMENT * 4;
+ buff->remaining -= FT_SECTORS_PER_SEGMENT;
+ buff->segment_id ++; /* done with segment */
+ TRACE_EXIT 0;
+}
+
+/*
+ * Continue formatting, switch buffers if there is no data left in
+ * current buffer. This is, of course, modelled after
+ * continue_xfer(), but we don't need to set up for retries because
+ * the format process cannot be interrupted (except at the end of the
+ * tape track).
+ */
+static void continue_formatting(buffer_struct *buff)
+{
+ TRACE_FUN(ft_t_any);
+
+ if (buff->remaining <= 0) { /* no space left in dma buffer */
+ unsigned int next = buff->next_segment;
+
+ if (next == 0) { /* end of tape track */
+ buff->status = done;
+ ft_runner_status = logical_eot;
+ fdc_mode = fdc_idle;
+ TRACE(ft_t_noise, "Done formatting track %d",
+ ft_location.track);
+ TRACE_EXIT;
+ }
+ /*
+ * switch to next buffer!
+ */
+ buff->status = done;
+ buff = ftape_next_buffer(ft_queue_head);
+
+ if (buff->status != waiting || next != buff->segment_id) {
+ goto format_setup_error;
+ }
+ }
+ if (fdc_setup_formatting(buff) < 0) {
+ goto format_setup_error;
+ }
+ buff->status = formatting;
+ TRACE(ft_t_fdc_dma, "Formatting segment %d on track %d",
+ buff->segment_id, ft_location.track);
+ TRACE_EXIT;
+ format_setup_error:
+ ft_runner_status = do_abort;
+ fdc_mode = fdc_idle;
+ buff->status = error;
+ TRACE(ft_t_err, "Error setting up for segment %d on track %d",
+ buff->segment_id, ft_location.track);
+ TRACE_EXIT;
+
+}
+
+/* this handles writing, read id, reading and formatting
+ */
+static void handle_fdc_busy(buffer_struct *buff)
+{
+ static int no_data_error_count = 0;
+ int retry = 0;
+ error_cause cause;
+ __u8 in[7];
+ int skip;
+ fdc_mode_enum fmode = fdc_mode;
+ TRACE_FUN(ft_t_any);
+
+ if (fdc_result(in, 7) < 0) { /* better get it fast ! */
+ TRACE(ft_t_err,
+ "Probably fatal error during FDC Result Phase\n"
+ KERN_INFO
+ "drive may hang until (power on) reset :-(");
+ /* what to do next ????
+ */
+ TRACE_EXIT;
+ }
+ cause = decode_irq_cause(fdc_mode, in);
+#ifdef TESTING
+ { int i;
+ for (i = 0; i < (int)ft_nr_buffers; ++i)
+ TRACE(ft_t_any, "buffer[%d] status: %d, segment_id: %d",
+ i, ft_buffer[i]->status, ft_buffer[i]->segment_id);
+ }
+#endif
+ if (fmode == fdc_reading_data && ft_driver_state == verifying) {
+ fmode = fdc_verifying;
+ }
+ switch (fmode) {
+ case fdc_verifying:
+ if (ft_runner_status == aborting ||
+ ft_runner_status == do_abort) {
+ TRACE(ft_t_noise,"aborting %s",fdc_mode_txt(fdc_mode));
+ break;
+ }
+ if (buff->retry > 0) {
+ TRACE(ft_t_flow, "this is retry nr %d", buff->retry);
+ }
+ switch (cause) {
+ case no_error:
+ no_data_error_count = 0;
+ determine_verify_progress(buff, cause, in[5]);
+ if (in[2] & 0x40) {
+ /* This should not happen when verifying
+ */
+ TRACE(ft_t_warn,
+ "deleted data in segment %d/%d",
+ buff->segment_id,
+ FT_SECTOR(buff->sector_offset - 1));
+ buff->remaining = 0; /* abort transfer */
+ buff->hard_error_map = EMPTY_SEGMENT;
+ skip = 1;
+ } else {
+ skip = 0;
+ }
+ continue_xfer(buff, fdc_mode, skip);
+ break;
+ case no_data_error:
+ no_data_error_count ++;
+ case overrun_error:
+ retry ++;
+ case id_am_error:
+ case id_crc_error:
+ case data_am_error:
+ case data_crc_error:
+ determine_verify_progress(buff, cause, in[5]);
+ if (cause == no_data_error) {
+ if (no_data_error_count >= 2) {
+ TRACE(ft_t_warn,
+ "retrying because of successive "
+ "no data errors");
+ no_data_error_count = 0;
+ } else {
+ retry --;
+ }
+ } else {
+ no_data_error_count = 0;
+ }
+ if (retry) {
+ skip = find_resume_point(buff);
+ } else {
+ skip = buff->sector_offset;
+ }
+ if (retry && skip < 32) {
+ retry_sector(buff, fdc_mode, skip);
+ } else {
+ continue_xfer(buff, fdc_mode, skip);
+ }
+ update_history(cause);
+ break;
+ default:
+ /* Don't know why this could happen
+ * but find out.
+ */
+ determine_verify_progress(buff, cause, in[5]);
+ retry_sector(buff, fdc_mode, 0);
+ TRACE(ft_t_err, "Error: unexpected error");
+ break;
+ }
+ break;
+ case fdc_reading_data:
+#ifdef TESTING
+ /* I'm sorry, but: NOBODY ever used this trace
+ * messages for ages. I guess that Bas was the last person
+ * that ever really used this (thank you, between the lines)
+ */
+ if (cause == no_error) {
+ TRACE(ft_t_flow,"reading segment %d",buff->segment_id);
+ } else {
+ TRACE(ft_t_noise, "error reading segment %d",
+ buff->segment_id);
+ TRACE(ft_t_noise, "\n"
+ KERN_INFO
+ "IRQ:C: 0x%02x, H: 0x%02x, R: 0x%02x, N: 0x%02x\n"
+ KERN_INFO
+ "BUF:C: 0x%02x, H: 0x%02x, R: 0x%02x",
+ in[3], in[4], in[5], in[6],
+ buff->cyl, buff->head, buff->sect);
+ }
+#endif
+ if (ft_runner_status == aborting ||
+ ft_runner_status == do_abort) {
+ TRACE(ft_t_noise,"aborting %s",fdc_mode_txt(fdc_mode));
+ break;
+ }
+ if (buff->bad_sector_map == FAKE_SEGMENT) {
+ /* This condition occurs when reading a `fake'
+ * sector that's not accessible. Doesn't
+ * really matter as we would have ignored it
+ * anyway !
+ *
+ * Chance is that we're past the next segment
+ * now, so the next operation may fail and
+ * result in a retry.
+ */
+ buff->remaining = 0; /* skip failing sector */
+ /* buff->ptr = buff->address; */
+ /* fake success: */
+ continue_xfer(buff, fdc_mode, 1);
+ /* trace calls are expensive: place them AFTER
+ * the real stuff has been done.
+ *
+ */
+ TRACE(ft_t_noise, "skipping empty segment %d (read), size? %d",
+ buff->segment_id, buff->ptr - buff->address);
+ TRACE_EXIT;
+ }
+ if (buff->retry > 0) {
+ TRACE(ft_t_flow, "this is retry nr %d", buff->retry);
+ }
+ switch (cause) {
+ case no_error:
+ determine_progress(buff, cause, in[5]);
+ if (in[2] & 0x40) {
+ /* Handle deleted data in header segments.
+ * Skip segment and force read-ahead.
+ */
+ TRACE(ft_t_warn,
+ "deleted data in segment %d/%d",
+ buff->segment_id,
+ FT_SECTOR(buff->sector_offset - 1));
+ buff->deleted = 1;
+ buff->remaining = 0;/*abort transfer */
+ buff->soft_error_map |=
+ (-1L << buff->sector_offset);
+ if (buff->segment_id == 0) {
+ /* stop on next segment */
+ stop_read_ahead = 1;
+ }
+ /* force read-ahead: */
+ buff->next_segment =
+ buff->segment_id + 1;
+ skip = (FT_SECTORS_PER_SEGMENT -
+ buff->sector_offset);
+ } else {
+ skip = 0;
+ }
+ continue_xfer(buff, fdc_mode, skip);
+ break;
+ case no_data_error:
+ /* Tape started too far ahead of or behind the
+ * right sector. This may also happen in the
+ * middle of a segment !
+ *
+ * Handle no-data as soft error. If next
+ * sector fails too, a retry (with needed
+ * reposition) will follow.
+ */
+ retry ++;
+ case id_am_error:
+ case id_crc_error:
+ case data_am_error:
+ case data_crc_error:
+ case overrun_error:
+ retry += (buff->soft_error_map != 0 ||
+ buff->hard_error_map != 0);
+ determine_progress(buff, cause, in[5]);
+#if 1 || defined(TESTING)
+ if (cause == overrun_error) retry ++;
+#endif
+ if (retry) {
+ skip = find_resume_point(buff);
+ } else {
+ skip = buff->sector_offset;
+ }
+ /* Try to resume with next sector on single
+ * errors (let ecc correct it), but retry on
+ * no_data (we'll be past the target when we
+ * get here so we cannot retry) or on
+ * multiple errors (reduce chance on ecc
+ * failure).
+ */
+ /* cH: 23/02/97: if the last sector in the
+ * segment was a hard error, then there is
+ * no sense in a retry. This occasion seldom
+ * occurs but ... @:³²¸`@%&§$
+ */
+ if (retry && skip < 32) {
+ retry_sector(buff, fdc_mode, skip);
+ } else {
+ continue_xfer(buff, fdc_mode, skip);
+ }
+ update_history(cause);
+ break;
+ default:
+ /* Don't know why this could happen
+ * but find out.
+ */
+ determine_progress(buff, cause, in[5]);
+ retry_sector(buff, fdc_mode, 0);
+ TRACE(ft_t_err, "Error: unexpected error");
+ break;
+ }
+ break;
+ case fdc_reading_id:
+ if (cause == no_error) {
+ fdc_cyl = in[3];
+ fdc_head = in[4];
+ fdc_sect = in[5];
+ TRACE(ft_t_fdc_dma,
+ "id read: C: 0x%02x, H: 0x%02x, R: 0x%02x",
+ fdc_cyl, fdc_head, fdc_sect);
+ } else { /* no valid information, use invalid sector */
+ fdc_cyl = fdc_head = fdc_sect = 0;
+ TRACE(ft_t_flow, "Didn't find valid sector Id");
+ }
+ fdc_mode = fdc_idle;
+ break;
+ case fdc_deleting:
+ case fdc_writing_data:
+#ifdef TESTING
+ if (cause == no_error) {
+ TRACE(ft_t_flow, "writing segment %d", buff->segment_id);
+ } else {
+ TRACE(ft_t_noise, "error writing segment %d",
+ buff->segment_id);
+ }
+#endif
+ if (ft_runner_status == aborting ||
+ ft_runner_status == do_abort) {
+ TRACE(ft_t_flow, "aborting %s",fdc_mode_txt(fdc_mode));
+ break;
+ }
+ if (buff->retry > 0) {
+ TRACE(ft_t_flow, "this is retry nr %d", buff->retry);
+ }
+ if (buff->bad_sector_map == FAKE_SEGMENT) {
+ /* This condition occurs when trying to write to a
+ * `fake' sector that's not accessible. Doesn't really
+ * matter as it isn't used anyway ! Might be located
+ * at wrong segment, then we'll fail on the next
+ * segment.
+ */
+ TRACE(ft_t_noise, "skipping empty segment (write)");
+ buff->remaining = 0; /* skip failing sector */
+ /* fake success: */
+ continue_xfer(buff, fdc_mode, 1);
+ break;
+ }
+ switch (cause) {
+ case no_error:
+ determine_progress(buff, cause, in[5]);
+ continue_xfer(buff, fdc_mode, 0);
+ break;
+ case no_data_error:
+ case id_am_error:
+ case id_crc_error:
+ case data_am_error:
+ case overrun_error:
+ update_history(cause);
+ determine_progress(buff, cause, in[5]);
+ skip = find_resume_point(buff);
+ retry_sector(buff, fdc_mode, skip);
+ break;
+ default:
+ if (in[1] & 0x02) {
+ TRACE(ft_t_err, "media not writable");
+ } else {
+ TRACE(ft_t_bug, "unforeseen write error");
+ }
+ fdc_mode = fdc_idle;
+ break;
+ }
+ break; /* fdc_deleting || fdc_writing_data */
+ case fdc_formatting:
+ /* The interrupt comes after formatting a segment. We then
+ * have to set up QUICKLY for the next segment. But
+ * afterwards, there is plenty of time.
+ */
+ switch (cause) {
+ case no_error:
+ /* would like to keep most of the formatting stuff
+ * outside the isr code, but timing is too critical
+ */
+ if (determine_fmt_progress(buff, cause) >= 0) {
+ continue_formatting(buff);
+ }
+ break;
+ case no_data_error:
+ case id_am_error:
+ case id_crc_error:
+ case data_am_error:
+ case overrun_error:
+ default:
+ determine_fmt_progress(buff, cause);
+ update_history(cause);
+ if (in[1] & 0x02) {
+ TRACE(ft_t_err, "media not writable");
+ } else {
+ TRACE(ft_t_bug, "unforeseen write error");
+ }
+ break;
+ } /* cause */
+ break;
+ default:
+ TRACE(ft_t_warn, "Warning: unexpected irq during: %s",
+ fdc_mode_txt(fdc_mode));
+ fdc_mode = fdc_idle;
+ break;
+ }
+ TRACE_EXIT;
+}
+
+/* FDC interrupt service routine.
+ */
+void fdc_isr(void)
+{
+ static int isr_active = 0;
+#ifdef TESTING
+ unsigned int t0 = ftape_timestamp();
+#endif
+ TRACE_FUN(ft_t_any);
+
+ if (isr_active++) {
+ --isr_active;
+ TRACE(ft_t_bug, "BUG: nested interrupt, not good !");
+ *fdc.hook = fdc_isr; /* hook our handler into the fdc
+ * code again
+ */
+ TRACE_EXIT;
+ }
+ sti();
+ if (inb_p(fdc.msr) & FDC_BUSY) { /* Entering Result Phase */
+ ft_hide_interrupt = 0;
+ handle_fdc_busy(ftape_get_buffer(ft_queue_head));
+ if (ft_runner_status == do_abort) {
+ /* cease operation, remember tape position
+ */
+ TRACE(ft_t_flow, "runner aborting");
+ ft_runner_status = aborting;
+ ++ft_expected_stray_interrupts;
+ }
+ } else { /* !FDC_BUSY */
+ /* clear interrupt, cause should be gotten by issuing
+ * a Sense Interrupt Status command.
+ */
+ if (fdc_mode == fdc_recalibrating || fdc_mode == fdc_seeking) {
+ if (ft_hide_interrupt) {
+ int st0;
+ int pcn;
+
+ if (fdc_sense_interrupt_status(&st0, &pcn) < 0)
+ TRACE(ft_t_err,
+ "sense interrupt status failed");
+ ftape_current_cylinder = pcn;
+ TRACE(ft_t_flow, "handled hidden interrupt");
+ }
+ ft_seek_completed = 1;
+ fdc_mode = fdc_idle;
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,0,16)
+ } else if (!waitqueue_active(&ftape_wait_intr)) {
+#else
+ } else if (!ftape_wait_intr) {
+#endif
+ if (ft_expected_stray_interrupts == 0) {
+ TRACE(ft_t_warn, "unexpected stray interrupt");
+ } else {
+ TRACE(ft_t_flow, "expected stray interrupt");
+ --ft_expected_stray_interrupts;
+ }
+ } else {
+ if (fdc_mode == fdc_reading_data ||
+ fdc_mode == fdc_verifying ||
+ fdc_mode == fdc_writing_data ||
+ fdc_mode == fdc_deleting ||
+ fdc_mode == fdc_formatting ||
+ fdc_mode == fdc_reading_id) {
+ if (inb_p(fdc.msr) & FDC_BUSY) {
+ TRACE(ft_t_bug,
+ "***** FDC failure, busy too late");
+ } else {
+ TRACE(ft_t_bug,
+ "***** FDC failure, no busy");
+ }
+ } else {
+ TRACE(ft_t_fdc_dma, "awaited stray interrupt");
+ }
+ }
+ ft_hide_interrupt = 0;
+ }
+ /* Handle sleep code.
+ */
+ if (!ft_hide_interrupt) {
+ ft_interrupt_seen ++;
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,0,16)
+ if (waitqueue_active(&ftape_wait_intr)) {
+ wake_up_interruptible(&ftape_wait_intr);
+ }
+#else
+ if (ftape_wait_intr) {
+ wake_up_interruptible(&ftape_wait_intr);
+ }
+#endif
+ } else {
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,0,16)
+ TRACE(ft_t_flow, "hiding interrupt while %s",
+ waitqueue_active(&ftape_wait_intr) ? "waiting":"active");
+#else
+ TRACE(ft_t_flow, "hiding interrupt while %s",
+ ftape_wait_intr ? "waiting" : "active");
+#endif
+ }
+#ifdef TESTING
+ t0 = ftape_timediff(t0, ftape_timestamp());
+ if (t0 >= 1000) {
+ /* only tell us about long calls */
+ TRACE(ft_t_noise, "isr() duration: %5d usec", t0);
+ }
+#endif
+ *fdc.hook = fdc_isr; /* hook our handler into the fdc code again */
+ --isr_active;
+ TRACE_EXIT;
+}
diff --git a/drivers/char/ftape/fdc-isr.h b/drivers/char/ftape/lowlevel/fdc-isr.h
index 123d4168f..065aa9789 100644
--- a/drivers/char/ftape/fdc-isr.h
+++ b/drivers/char/ftape/lowlevel/fdc-isr.h
@@ -2,7 +2,8 @@
#define _FDC_ISR_H
/*
- * Copyright (C) 1993-1995 Bas Laarhoven.
+ * Copyright (C) 1993-1996 Bas Laarhoven,
+ * (C) 1996-1997 Claus-Justus Heine.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -19,25 +20,23 @@
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
- $Source: /home/bas/distr/ftape-2.03b/RCS/fdc-isr.h,v $
- $Author: bas $
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/fdc-isr.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:07 $
*
- $Revision: 1.8 $
- $Date: 1995/04/22 07:30:15 $
- $State: Beta $
- *
- * This file contains the low level functions
- * that communicate with the floppy disk controller,
- * for the QIC-40/80 floppy-tape driver for Linux.
+ * This file declares the global variables necessary to
+ * synchronize the interrupt service routine (isr) with the
+ * remainder of the QIC-40/80/3010/3020 floppy-tape driver
+ * "ftape" for Linux.
*/
/*
* fdc-isr.c defined public variables
*/
-extern volatile int expected_stray_interrupts; /* masks stray interrupts */
-extern volatile int seek_completed; /* flag set by isr */
-extern volatile int interrupt_seen; /* flag set by isr */
-extern volatile int expect_stray_interrupt;
+extern volatile int ft_expected_stray_interrupts; /* masks stray interrupts */
+extern volatile int ft_seek_completed; /* flag set by isr */
+extern volatile int ft_interrupt_seen; /* flag set by isr */
+extern volatile int ft_hide_interrupt; /* flag set by isr */
/*
* fdc-io.c defined public functions
diff --git a/drivers/char/ftape/lowlevel/ftape-bsm.c b/drivers/char/ftape/lowlevel/ftape-bsm.c
new file mode 100644
index 000000000..a79c468e3
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/ftape-bsm.c
@@ -0,0 +1,490 @@
+/*
+ * Copyright (C) 1994-1996 Bas Laarhoven,
+ * (C) 1996-1997 Claus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-bsm.c,v $
+ * $Revision: 1.3 $
+ * $Date: 1997/10/05 19:15:15 $
+ *
+ * This file contains the bad-sector map handling code for
+ * the QIC-117 floppy tape driver for Linux.
+ * QIC-40, QIC-80, QIC-3010 and QIC-3020 maps are implemented.
+ */
+
+#include <linux/string.h>
+
+#include <linux/ftape.h>
+#include "../lowlevel/ftape-tracing.h"
+#include "../lowlevel/ftape-bsm.h"
+#include "../lowlevel/ftape-ctl.h"
+#include "../lowlevel/ftape-rw.h"
+
+/* Global vars.
+ */
+
+/* Local vars.
+ */
+static __u8 *bad_sector_map = NULL;
+static SectorCount *bsm_hash_ptr = NULL;
+
+typedef enum {
+ forward, backward
+} mode_type;
+
+#if 0
+/* fix_tape converts a normal QIC-80 tape into a 'wide' tape.
+ * For testing purposes only !
+ */
+void fix_tape(__u8 * buffer, ft_format_type new_code)
+{
+ static __u8 list[BAD_SECTOR_MAP_SIZE];
+ SectorMap *src_ptr = (SectorMap *) list;
+ __u8 *dst_ptr = bad_sector_map;
+ SectorMap map;
+ unsigned int sector = 1;
+ int i;
+
+ if (format_code != fmt_var && format_code != fmt_big) {
+ memcpy(list, bad_sector_map, sizeof(list));
+ memset(bad_sector_map, 0, sizeof(bad_sector_map));
+ while ((__u8 *) src_ptr - list < sizeof(list)) {
+ map = *src_ptr++;
+ if (map == EMPTY_SEGMENT) {
+ *(SectorMap *) dst_ptr = 0x800000 + sector;
+ dst_ptr += 3;
+ sector += SECTORS_PER_SEGMENT;
+ } else {
+ for (i = 0; i < SECTORS_PER_SEGMENT; ++i) {
+ if (map & 1) {
+ *(SewctorMap *) dst_ptr = sector;
+ dst_ptr += 3;
+ }
+ map >>= 1;
+ ++sector;
+ }
+ }
+ }
+ }
+ bad_sector_map_changed = 1;
+ *(buffer + 4) = new_code; /* put new format code */
+ if (format_code != fmt_var && new_code == fmt_big) {
+ PUT4(buffer, FT_6_HSEG_1, (__u32)GET2(buffer, 6));
+ PUT4(buffer, FT_6_HSEG_2, (__u32)GET2(buffer, 8));
+ PUT4(buffer, FT_6_FRST_SEG, (__u32)GET2(buffer, 10));
+ PUT4(buffer, FT_6_LAST_SEG, (__u32)GET2(buffer, 12));
+ memset(buffer+6, '\0', 8);
+ }
+ format_code = new_code;
+}
+
+#endif
+
+/* given buffer that contains a header segment, find the end of
+ * of the bsm list
+ */
+__u8 * ftape_find_end_of_bsm_list(__u8 * address)
+{
+ __u8 *ptr = address + FT_HEADER_END; /* start of bsm list */
+ __u8 *limit = address + FT_SEGMENT_SIZE;
+ while (ptr + 2 < limit) {
+ if (ptr[0] || ptr[1] || ptr[2]) {
+ ptr += 3;
+ } else {
+ return ptr;
+ }
+ }
+ return NULL;
+}
+
+static inline void put_sector(SectorCount *ptr, unsigned int sector)
+{
+ ptr->bytes[0] = sector & 0xff;
+ sector >>= 8;
+ ptr->bytes[1] = sector & 0xff;
+ sector >>= 8;
+ ptr->bytes[2] = sector & 0xff;
+}
+
+static inline unsigned int get_sector(SectorCount *ptr)
+{
+#if 1
+ unsigned int sector;
+
+ sector = ptr->bytes[0];
+ sector += ptr->bytes[1] << 8;
+ sector += ptr->bytes[2] << 16;
+
+ return sector;
+#else
+ /* GET4 gets the next four bytes in Intel little endian order
+ * and converts them to host byte order and handles unaligned
+ * access.
+ */
+ return (GET4(ptr, 0) & 0x00ffffff); /* back to host byte order */
+#endif
+}
+
+static void bsm_debug_fake(void)
+{
+ /* for testing of bad sector handling at end of tape
+ */
+#if 0
+ ftape_put_bad_sector_entry(segments_per_track * tracks_per_tape - 3,
+ 0x000003e0;
+ ftape_put_bad_sector_entry(segments_per_track * tracks_per_tape - 2,
+ 0xff3fffff;
+ ftape_put_bad_sector_entry(segments_per_track * tracks_per_tape - 1,
+ 0xffffe000;
+#endif
+ /* Enable to test bad sector handling
+ */
+#if 0
+ ftape_put_bad_sector_entry(30, 0xfffffffe)
+ ftape_put_bad_sector_entry(32, 0x7fffffff);
+ ftape_put_bad_sector_entry(34, 0xfffeffff);
+ ftape_put_bad_sector_entry(36, 0x55555555);
+ ftape_put_bad_sector_entry(38, 0xffffffff);
+ ftape_put_bad_sector_entry(50, 0xffff0000);
+ ftape_put_bad_sector_entry(51, 0xffffffff);
+ ftape_put_bad_sector_entry(52, 0xffffffff);
+ ftape_put_bad_sector_entry(53, 0x0000ffff);
+#endif
+ /* Enable when testing multiple volume tar dumps.
+ */
+#if 0
+ {
+ int i;
+
+ for (i = ft_first_data_segment;
+ i <= ft_last_data_segment - 7; ++i) {
+ ftape_put_bad_sector_entry(i, EMPTY_SEGMENT);
+ }
+ }
+#endif
+ /* Enable when testing bit positions in *_error_map
+ */
+#if 0
+ {
+ int i;
+
+ for (i = first_data_segment; i <= last_data_segment; ++i) {
+ ftape_put_bad_sector_entry(i,
+ ftape_get_bad_sector_entry(i)
+ | 0x00ff00ff);
+ }
+ }
+#endif
+}
+
+static void print_bad_sector_map(void)
+{
+ unsigned int good_sectors;
+ unsigned int total_bad = 0;
+ int i;
+ TRACE_FUN(ft_t_flow);
+
+ if (ft_format_code == fmt_big ||
+ ft_format_code == fmt_var ||
+ ft_format_code == fmt_1100ft) {
+ SectorCount *ptr = (SectorCount *)bad_sector_map;
+ unsigned int sector;
+
+ while((sector = get_sector(ptr++)) != 0) {
+ if ((ft_format_code == fmt_big ||
+ ft_format_code == fmt_var) &&
+ sector & 0x800000) {
+ total_bad += FT_SECTORS_PER_SEGMENT - 3;
+ TRACE(ft_t_noise, "bad segment at sector: %6d",
+ sector & 0x7fffff);
+ } else {
+ ++total_bad;
+ TRACE(ft_t_noise, "bad sector: %6d", sector);
+ }
+ }
+ /* Display old ftape's end-of-file marks
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,0,0)
+ while ((sector = get_unaligned(((__u16*)ptr)++)) != 0) {
+ TRACE(ft_t_noise, "Old ftape eof mark: %4d/%2d",
+ sector, get_unaligned(((__u16*)ptr)++));
+ }
+#else
+ while ((sector = *((__u16*)ptr)++) != 0) {
+ TRACE(ft_t_noise, "Old ftape eof mark: %4d/%2d",
+ sector, *((__u16*)ptr)++);
+ }
+#endif
+ } else { /* fixed size format */
+ for (i = ft_first_data_segment;
+ i < (int)(ft_segments_per_track * ft_tracks_per_tape); ++i) {
+ SectorMap map = ((SectorMap *) bad_sector_map)[i];
+
+ if (map) {
+ TRACE(ft_t_noise,
+ "bsm for segment %4d: 0x%08x", i, (unsigned int)map);
+ total_bad += ((map == EMPTY_SEGMENT)
+ ? FT_SECTORS_PER_SEGMENT - 3
+ : count_ones(map));
+ }
+ }
+ }
+ good_sectors =
+ ((ft_segments_per_track * ft_tracks_per_tape - ft_first_data_segment)
+ * (FT_SECTORS_PER_SEGMENT - 3)) - total_bad;
+ TRACE(ft_t_info, "%d Kb usable on this tape", good_sectors);
+ if (total_bad == 0) {
+ TRACE(ft_t_info,
+ "WARNING: this tape has no bad blocks registered !");
+ } else {
+ TRACE(ft_t_info, "%d bad sectors", total_bad);
+ }
+ TRACE_EXIT;
+}
+
+
+void ftape_extract_bad_sector_map(__u8 * buffer)
+{
+ TRACE_FUN(ft_t_any);
+
+ /* Fill the bad sector map with the contents of buffer.
+ */
+ if (ft_format_code == fmt_var || ft_format_code == fmt_big) {
+ /* QIC-3010/3020 and wide QIC-80 tapes no longer have a failed
+ * sector log but use this area to extend the bad sector map.
+ */
+ bad_sector_map = &buffer[FT_HEADER_END];
+ } else {
+ /* non-wide QIC-80 tapes have a failed sector log area that
+ * mustn't be included in the bad sector map.
+ */
+ bad_sector_map = &buffer[FT_FSL + FT_FSL_SIZE];
+ }
+ if (ft_format_code == fmt_1100ft ||
+ ft_format_code == fmt_var ||
+ ft_format_code == fmt_big) {
+ bsm_hash_ptr = (SectorCount *)bad_sector_map;
+ } else {
+ bsm_hash_ptr = NULL;
+ }
+ bsm_debug_fake();
+ if (TRACE_LEVEL >= ft_t_info) {
+ print_bad_sector_map();
+ }
+ TRACE_EXIT;
+}
+
+static inline SectorMap cvt2map(unsigned int sector)
+{
+ return 1 << (((sector & 0x7fffff) - 1) % FT_SECTORS_PER_SEGMENT);
+}
+
+static inline int cvt2segment(unsigned int sector)
+{
+ return ((sector & 0x7fffff) - 1) / FT_SECTORS_PER_SEGMENT;
+}
+
+static int forward_seek_entry(int segment_id,
+ SectorCount **ptr,
+ SectorMap *map)
+{
+ unsigned int sector;
+ int segment;
+
+ do {
+ sector = get_sector((*ptr)++);
+ segment = cvt2segment(sector);
+ } while (sector != 0 && segment < segment_id);
+ (*ptr) --; /* point to first sector >= segment_id */
+ /* Get all sectors in segment_id
+ */
+ if (sector == 0 || segment != segment_id) {
+ *map = 0;
+ return 0;
+ } else if ((sector & 0x800000) &&
+ (ft_format_code == fmt_var || ft_format_code == fmt_big)) {
+ *map = EMPTY_SEGMENT;
+ return FT_SECTORS_PER_SEGMENT;
+ } else {
+ int count = 1;
+ SectorCount *tmp_ptr = (*ptr) + 1;
+
+ *map = cvt2map(sector);
+ while ((sector = get_sector(tmp_ptr++)) != 0 &&
+ (segment = cvt2segment(sector)) == segment_id) {
+ *map |= cvt2map(sector);
+ ++count;
+ }
+ return count;
+ }
+}
+
+static int backwards_seek_entry(int segment_id,
+ SectorCount **ptr,
+ SectorMap *map)
+{
+ unsigned int sector;
+ int segment; /* max unsigned int */
+
+ if (*ptr <= (SectorCount *)bad_sector_map) {
+ *map = 0;
+ return 0;
+ }
+ do {
+ sector = get_sector(--(*ptr));
+ segment = cvt2segment(sector);
+ } while (*ptr > (SectorCount *)bad_sector_map && segment > segment_id);
+ if (segment > segment_id) { /* at start of list, no entry found */
+ *map = 0;
+ return 0;
+ } else if (segment < segment_id) {
+ /* before smaller entry, adjust for overshoot */
+ (*ptr) ++;
+ *map = 0;
+ return 0;
+ } else if ((sector & 0x800000) &&
+ (ft_format_code == fmt_big || ft_format_code == fmt_var)) {
+ *map = EMPTY_SEGMENT;
+ return FT_SECTORS_PER_SEGMENT;
+ } else { /* get all sectors in segment_id */
+ int count = 1;
+
+ *map = cvt2map(sector);
+ while(*ptr > (SectorCount *)bad_sector_map) {
+ sector = get_sector(--(*ptr));
+ segment = cvt2segment(sector);
+ if (segment != segment_id) {
+ break;
+ }
+ *map |= cvt2map(sector);
+ ++count;
+ }
+ if (segment < segment_id) {
+ (*ptr) ++;
+ }
+ return count;
+ }
+}
+
+void ftape_put_bad_sector_entry(int segment_id, SectorMap new_map)
+{
+ SectorCount *ptr = (SectorCount *)bad_sector_map;
+ int count;
+ int new_count;
+ SectorMap map;
+ TRACE_FUN(ft_t_any);
+
+ if (ft_format_code == fmt_1100ft ||
+ ft_format_code == fmt_var ||
+ ft_format_code == fmt_big) {
+ count = forward_seek_entry(segment_id, &ptr, &map);
+ new_count = count_ones(new_map);
+ /* If format code == 4 put empty segment instead of 32
+ * bad sectors.
+ */
+ if (ft_format_code == fmt_var || ft_format_code == fmt_big) {
+ if (new_count == FT_SECTORS_PER_SEGMENT) {
+ new_count = 1;
+ }
+ if (count == FT_SECTORS_PER_SEGMENT) {
+ count = 1;
+ }
+ }
+ if (count != new_count) {
+ /* insert (or delete if < 0) new_count - count
+ * entries. Move trailing part of list
+ * including terminating 0.
+ */
+ SectorCount *hi_ptr = ptr;
+
+ do {
+ } while (get_sector(hi_ptr++) != 0);
+ /* Note: ptr is of type byte *, and each bad sector
+ * consumes 3 bytes.
+ */
+ memmove(ptr + new_count, ptr + count,
+ (size_t)(hi_ptr - (ptr + count))*sizeof(SectorCount));
+ }
+ TRACE(ft_t_noise, "putting map 0x%08x at %p, segment %d",
+ (unsigned int)new_map, ptr, segment_id);
+ if (new_count == 1 && new_map == EMPTY_SEGMENT) {
+ put_sector(ptr++, (0x800001 +
+ segment_id *
+ FT_SECTORS_PER_SEGMENT));
+ } else {
+ int i = 0;
+
+ while (new_map) {
+ if (new_map & 1) {
+ put_sector(ptr++,
+ 1 + segment_id *
+ FT_SECTORS_PER_SEGMENT + i);
+ }
+ ++i;
+ new_map >>= 1;
+ }
+ }
+ } else {
+ ((SectorMap *) bad_sector_map)[segment_id] = new_map;
+ }
+ TRACE_EXIT;
+}
+
+SectorMap ftape_get_bad_sector_entry(int segment_id)
+{
+ if (ft_used_header_segment == -1) {
+ /* When reading header segment we'll need a blank map.
+ */
+ return 0;
+ } else if (bsm_hash_ptr != NULL) {
+ /* Invariants:
+ * map - mask value returned on last call.
+ * bsm_hash_ptr - points to first sector greater or equal to
+ * first sector in last_referenced segment.
+ * last_referenced - segment id used in the last call,
+ * sector and map belong to this id.
+ * This code is designed for sequential access and retries.
+ * For true random access it may have to be redesigned.
+ */
+ static int last_reference = -1;
+ static SectorMap map = 0;
+
+ if (segment_id > last_reference) {
+ /* Skip all sectors before segment_id
+ */
+ forward_seek_entry(segment_id, &bsm_hash_ptr, &map);
+ } else if (segment_id < last_reference) {
+ /* Skip backwards until begin of buffer or
+ * first sector in segment_id
+ */
+ backwards_seek_entry(segment_id, &bsm_hash_ptr, &map);
+ } /* segment_id == last_reference : keep map */
+ last_reference = segment_id;
+ return map;
+ } else {
+ return ((SectorMap *) bad_sector_map)[segment_id];
+ }
+}
+
+/* This is simply here to prevent us from overwriting other kernel
+ * data. Writes will result in NULL Pointer dereference.
+ */
+void ftape_init_bsm(void)
+{
+ bad_sector_map = NULL;
+ bsm_hash_ptr = NULL;
+}
diff --git a/drivers/char/ftape/ftape-bsm.h b/drivers/char/ftape/lowlevel/ftape-bsm.h
index e84363d45..0dc7805da 100644
--- a/drivers/char/ftape/ftape-bsm.h
+++ b/drivers/char/ftape/lowlevel/ftape-bsm.h
@@ -2,7 +2,8 @@
#define _FTAPE_BSM_H
/*
- * Copyright (C) 1994-1995 Bas Laarhoven.
+ * Copyright (C) 1994-1996 Bas Laarhoven,
+ * (C) 1996-1997 Claus-Justus Heine.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -19,44 +20,48 @@
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
- $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-bsm.h,v $
- $Author: bas $
- *
- $Revision: 1.5 $
- $Date: 1995/04/22 07:30:15 $
- $State: Beta $
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-bsm.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:07 $
*
* This file contains definitions for the bad sector map handling
* routines for the QIC-117 floppy-tape driver for Linux.
*/
+#include <linux/ftape.h>
+#include <linux/ftape-header-segment.h>
+
#define EMPTY_SEGMENT (0xffffffff)
#define FAKE_SEGMENT (0xfffffffe)
-/* failed sector log size (only used if format code != 4).
- */
-#define FAILED_SECTOR_LOG_SIZE (2 * SECTOR_SIZE - 256)
-
/* maximum (format code 4) bad sector map size (bytes).
*/
#define BAD_SECTOR_MAP_SIZE (29 * SECTOR_SIZE - 256)
+/* format code 4 bad sector entry, ftape uses this
+ * internally for all format codes
+ */
+typedef __u32 SectorMap;
+/* variable and 1100 ft bad sector map entry. These three bytes represent
+ * a single sector address measured from BOT.
+ */
+typedef struct NewSectorMap {
+ __u8 bytes[3];
+} SectorCount __attribute__((packed));
+
+
/*
- * ftape-io.c defined global vars.
+ * ftape-bsm.c defined global vars.
*/
-extern bad_sector_map_changed;
/*
- * ftape-io.c defined global functions.
+ * ftape-bsm.c defined global functions.
*/
-extern void update_bad_sector_map(byte * buffer);
-extern void store_bad_sector_map(byte * buffer);
-extern void extract_bad_sector_map(byte * buffer);
-extern unsigned long get_bad_sector_entry(int segment_id);
-extern void put_bad_sector_entry(int segment_id, unsigned long mask);
-extern void add_segment_to_bad_sector_map(unsigned segment);
-extern void clear_bad_sector_map(int count);
-extern byte *find_end_of_bsm_list(byte * ptr, byte * limit);
+extern void update_bad_sector_map(__u8 * buffer);
+extern void ftape_extract_bad_sector_map(__u8 * buffer);
+extern SectorMap ftape_get_bad_sector_entry(int segment_id);
+extern void ftape_put_bad_sector_entry(int segment_id, SectorMap mask);
+extern __u8 *ftape_find_end_of_bsm_list(__u8 * address);
extern void ftape_init_bsm(void);
#endif
diff --git a/drivers/char/ftape/lowlevel/ftape-buffer.c b/drivers/char/ftape/lowlevel/ftape-buffer.c
new file mode 100644
index 000000000..8de980562
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/ftape-buffer.c
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 1997 Claus-Justus Heine
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-buffer.c,v $
+ * $Revision: 1.3 $
+ * $Date: 1997/10/16 23:33:11 $
+ *
+ * This file contains the allocator/dealloctor for ftape's dynamic dma
+ * buffer.
+ */
+
+#include <asm/segment.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/wrapper.h>
+#include <asm/dma.h>
+
+#include <linux/ftape.h>
+#include "../lowlevel/ftape-rw.h"
+#include "../lowlevel/ftape-read.h"
+#include "../lowlevel/ftape-tracing.h"
+
+/* DMA'able memory allocation stuff.
+ */
+
+/* Pure 2^n version of get_order */
+static inline int __get_order(size_t size)
+{
+ unsigned long order;
+
+ size = (size-1) >> (PAGE_SHIFT-1);
+ order = -1;
+ do {
+ size >>= 1;
+ order++;
+ } while (size);
+ return order;
+}
+
+static inline void *dmaalloc(size_t size)
+{
+ unsigned long addr;
+
+ if (size == 0) {
+ return NULL;
+ }
+ addr = __get_dma_pages(GFP_KERNEL, __get_order(size));
+ if (addr) {
+ int i;
+
+ for (i = MAP_NR(addr); i < MAP_NR(addr+size); i++) {
+ mem_map_reserve(i);
+ }
+ }
+ return (void *)addr;
+}
+
+static inline void dmafree(void *addr, size_t size)
+{
+ if (size > 0) {
+ int i;
+
+ for (i = MAP_NR((unsigned long)addr);
+ i < MAP_NR((unsigned long)addr+size); i++) {
+ mem_map_unreserve (i);
+ }
+ free_pages((unsigned long) addr, __get_order(size));
+ }
+}
+
+static int add_one_buffer(void)
+{
+ TRACE_FUN(ft_t_flow);
+
+ if (ft_nr_buffers >= FT_MAX_NR_BUFFERS) {
+ TRACE_EXIT -ENOMEM;
+ }
+ ft_buffer[ft_nr_buffers] = kmalloc(sizeof(buffer_struct), GFP_KERNEL);
+ if (ft_buffer[ft_nr_buffers] == NULL) {
+ TRACE_EXIT -ENOMEM;
+ }
+ memset(ft_buffer[ft_nr_buffers], 0, sizeof(buffer_struct));
+ ft_buffer[ft_nr_buffers]->address = dmaalloc(FT_BUFF_SIZE);
+ if (ft_buffer[ft_nr_buffers]->address == NULL) {
+ kfree(ft_buffer[ft_nr_buffers]);
+ ft_buffer[ft_nr_buffers] = NULL;
+ TRACE_EXIT -ENOMEM;
+ }
+ ft_nr_buffers ++;
+ TRACE(ft_t_info, "buffer nr #%d @ %p, dma area @ %p",
+ ft_nr_buffers,
+ ft_buffer[ft_nr_buffers-1],
+ ft_buffer[ft_nr_buffers-1]->address);
+ TRACE_EXIT 0;
+}
+
+static void del_one_buffer(void)
+{
+ TRACE_FUN(ft_t_flow);
+ if (ft_nr_buffers > 0) {
+ TRACE(ft_t_info, "releasing buffer nr #%d @ %p, dma area @ %p",
+ ft_nr_buffers,
+ ft_buffer[ft_nr_buffers-1],
+ ft_buffer[ft_nr_buffers-1]->address);
+ ft_nr_buffers --;
+ dmafree(ft_buffer[ft_nr_buffers]->address, FT_BUFF_SIZE);
+ kfree(ft_buffer[ft_nr_buffers]);
+ ft_buffer[ft_nr_buffers] = NULL;
+ }
+ TRACE_EXIT;
+}
+
+int ftape_set_nr_buffers(int cnt)
+{
+ int delta = cnt - ft_nr_buffers;
+ TRACE_FUN(ft_t_flow);
+
+ if (delta > 0) {
+ while (delta--) {
+ if (add_one_buffer() < 0) {
+ TRACE_EXIT -ENOMEM;
+ }
+ }
+ } else if (delta < 0) {
+ while (delta++) {
+ del_one_buffer();
+ }
+ }
+ ftape_zap_read_buffers();
+ TRACE_EXIT 0;
+}
diff --git a/drivers/char/ftape/lowlevel/ftape-buffer.h b/drivers/char/ftape/lowlevel/ftape-buffer.h
new file mode 100644
index 000000000..eec99cee8
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/ftape-buffer.h
@@ -0,0 +1,32 @@
+#ifndef _FTAPE_BUFFER_H
+#define _FTAPE_BUFFER_H
+
+/*
+ * Copyright (C) 1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-buffer.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:08 $
+ *
+ * This file contains the allocator/dealloctor for ftape's dynamic dma
+ * buffer.
+ */
+
+extern int ftape_set_nr_buffers(int cnt);
+
+#endif
diff --git a/drivers/char/ftape/lowlevel/ftape-calibr.c b/drivers/char/ftape/lowlevel/ftape-calibr.c
new file mode 100644
index 000000000..77ee01ecf
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/ftape-calibr.c
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 1993-1996 Bas Laarhoven.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-calibr.c,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:08 $
+ *
+ * GP calibration routine for processor speed dependent
+ * functions.
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#if defined(__alpha__)
+# include <asm/hwrpb.h>
+#elif defined(__i386__)
+# include <linux/timex.h>
+#endif
+#include <linux/ftape.h>
+#include "../lowlevel/ftape-tracing.h"
+#include "../lowlevel/ftape-calibr.h"
+#include "../lowlevel/fdc-io.h"
+
+#undef DEBUG
+
+#if !defined(__alpha__) && !defined(__i386__)
+# error Ftape is not implemented for this architecture!
+#endif
+
+#if defined(__alpha__)
+static unsigned long ps_per_cycle = 0;
+#endif
+
+/*
+ * Note: On Intel PCs, the clock ticks at 100 Hz (HZ==100) which is
+ * too slow for certain timeouts (and that clock doesn't even tick
+ * when interrupts are disabled). For that reason, the 8254 timer is
+ * used directly to implement fine-grained timeouts. However, on
+ * Alpha PCs, the 8254 is *not* used to implement the clock tick
+ * (which is 1024 Hz, normally) and the 8254 timer runs at some
+ * "random" frequency (it seems to run at 18Hz, but its not safe to
+ * rely on this value). Instead, we use the Alpha's "rpcc"
+ * instruction to read cycle counts. As this is a 32 bit counter,
+ * it will overflow only once per 30 seconds (on a 200MHz machine),
+ * which is plenty.
+ */
+
+unsigned int ftape_timestamp(void)
+{
+#if defined(__alpha__)
+ unsigned long r;
+
+ asm volatile ("rpcc %0" : "=r" (r));
+ return r;
+#elif defined(__i386__)
+ unsigned long flags;
+ __u16 lo;
+ __u16 hi;
+
+ save_flags(flags);
+ cli();
+ outb_p(0x00, 0x43); /* latch the count ASAP */
+ lo = inb_p(0x40); /* read the latched count */
+ lo |= inb(0x40) << 8;
+ hi = jiffies;
+ restore_flags(flags);
+ return ((hi + 1) * (unsigned int) LATCH) - lo; /* downcounter ! */
+#endif
+}
+
+static unsigned int short_ftape_timestamp(void)
+{
+#if defined(__alpha__)
+ return ftape_timestamp();
+#elif defined(__i386__)
+ unsigned int count;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ outb_p(0x00, 0x43); /* latch the count ASAP */
+ count = inb_p(0x40); /* read the latched count */
+ count |= inb(0x40) << 8;
+ restore_flags(flags);
+ return (LATCH - count); /* normal: downcounter */
+#endif
+}
+
+static unsigned int diff(unsigned int t0, unsigned int t1)
+{
+#if defined(__alpha__)
+ return (t1 <= t0) ? t1 + (1UL << 32) - t0 : t1 - t0;
+#elif defined(__i386__)
+ /*
+ * This is tricky: to work for both short and full ftape_timestamps
+ * we'll have to discriminate between these.
+ * If it _looks_ like short stamps with wrapping around we'll
+ * asume it are. This will generate a small error if it really
+ * was a (very large) delta from full ftape_timestamps.
+ */
+ return (t1 <= t0 && t0 <= LATCH) ? t1 + LATCH - t0 : t1 - t0;
+#endif
+}
+
+static unsigned int usecs(unsigned int count)
+{
+#if defined(__alpha__)
+ return (ps_per_cycle * count) / 1000000UL;
+#elif defined(__i386__)
+ return (10000 * count) / ((CLOCK_TICK_RATE + 50) / 100);
+#endif
+}
+
+unsigned int ftape_timediff(unsigned int t0, unsigned int t1)
+{
+ /*
+ * Calculate difference in usec for ftape_timestamp results t0 & t1.
+ * Note that on the i386 platform with short time-stamps, the
+ * maximum allowed timespan is 1/HZ or we'll lose ticks!
+ */
+ return usecs(diff(t0, t1));
+}
+
+/* To get an indication of the I/O performance,
+ * measure the duration of the inb() function.
+ */
+static void time_inb(void)
+{
+ int i;
+ int t0, t1;
+ unsigned long flags;
+ int status;
+ TRACE_FUN(ft_t_any);
+
+ save_flags(flags);
+ cli();
+ t0 = short_ftape_timestamp();
+ for (i = 0; i < 1000; ++i) {
+ status = inb(fdc.msr);
+ }
+ t1 = short_ftape_timestamp();
+ restore_flags(flags);
+ TRACE(ft_t_info, "inb() duration: %d nsec", ftape_timediff(t0, t1));
+ TRACE_EXIT;
+}
+
+static void init_clock(void)
+{
+#if defined(__i386__)
+ unsigned int t;
+ int i;
+ TRACE_FUN(ft_t_any);
+
+ /* Haven't studied on why, but there sometimes is a problem
+ * with the tick timer readout. The two bytes get swapped.
+ * This hack solves that problem by doing one extra input.
+ */
+ for (i = 0; i < 1000; ++i) {
+ t = short_ftape_timestamp();
+ if (t > LATCH) {
+ inb_p(0x40); /* get in sync again */
+ TRACE(ft_t_warn, "clock counter fixed");
+ break;
+ }
+ }
+#elif defined(__alpha__)
+#if CONFIG_FT_ALPHA_CLOCK == 0
+#error You must define and set CONFIG_FT_ALPHA_CLOCK in the Makefile !
+#endif
+ extern struct hwrpb_struct *hwrpb;
+ TRACE_FUN(ft_t_any);
+
+ if (hwrpb->cycle_freq != 0) {
+ ps_per_cycle = (1000*1000*1000*1000UL) / hwrpb->cycle_freq;
+ } else {
+ /*
+ * HELP: Linux 2.0.x doesn't set cycle_freq on my noname !
+ */
+ ps_per_cycle = (1000*1000*1000*1000UL) / CONFIG_FT_ALPHA_CLOCK;
+ }
+#endif
+ TRACE_EXIT;
+}
+
+/*
+ * Input: function taking int count as parameter.
+ * pointers to calculated calibration variables.
+ */
+void ftape_calibrate(char *name,
+ void (*fun) (unsigned int),
+ unsigned int *calibr_count,
+ unsigned int *calibr_time)
+{
+ static int first_time = 1;
+ int i;
+ unsigned int tc = 0;
+ unsigned int count;
+ unsigned int time;
+#if defined(__i386__)
+ unsigned int old_tc = 0;
+ unsigned int old_count = 1;
+ unsigned int old_time = 1;
+#endif
+ TRACE_FUN(ft_t_flow);
+
+ if (first_time) { /* get idea of I/O performance */
+ init_clock();
+ time_inb();
+ first_time = 0;
+ }
+ /* value of timeout must be set so that on very slow systems
+ * it will give a time less than one jiffy, and on
+ * very fast systems it'll give reasonable precision.
+ */
+
+ count = 40;
+ for (i = 0; i < 15; ++i) {
+ unsigned int t0;
+ unsigned int t1;
+ unsigned int once;
+ unsigned int multiple;
+ unsigned long flags;
+
+ *calibr_count =
+ *calibr_time = count; /* set TC to 1 */
+ save_flags(flags);
+ cli();
+ fun(0); /* dummy, get code into cache */
+ t0 = short_ftape_timestamp();
+ fun(0); /* overhead + one test */
+ t1 = short_ftape_timestamp();
+ once = diff(t0, t1);
+ t0 = short_ftape_timestamp();
+ fun(count); /* overhead + count tests */
+ t1 = short_ftape_timestamp();
+ multiple = diff(t0, t1);
+ restore_flags(flags);
+ time = ftape_timediff(0, multiple - once);
+ tc = (1000 * time) / (count - 1);
+ TRACE(ft_t_any, "once:%3d us,%6d times:%6d us, TC:%5d ns",
+ usecs(once), count - 1, usecs(multiple), tc);
+#if defined(__alpha__)
+ /*
+ * Increase the calibration count exponentially until the
+ * calibration time exceeds 100 ms.
+ */
+ if (time >= 100*1000) {
+ break;
+ }
+#elif defined(__i386__)
+ /*
+ * increase the count until the resulting time nears 2/HZ,
+ * then the tc will drop sharply because we lose LATCH counts.
+ */
+ if (tc <= old_tc / 2) {
+ time = old_time;
+ count = old_count;
+ break;
+ }
+ old_tc = tc;
+ old_count = count;
+ old_time = time;
+#endif
+ count *= 2;
+ }
+ *calibr_count = count - 1;
+ *calibr_time = time;
+ TRACE(ft_t_info, "TC for `%s()' = %d nsec (at %d counts)",
+ name, (1000 * *calibr_time) / *calibr_count, *calibr_count);
+ TRACE_EXIT;
+}
diff --git a/drivers/char/ftape/calibr.h b/drivers/char/ftape/lowlevel/ftape-calibr.h
index 75bebe75f..0c7e75246 100644
--- a/drivers/char/ftape/calibr.h
+++ b/drivers/char/ftape/lowlevel/ftape-calibr.h
@@ -1,8 +1,8 @@
-#ifndef _CALIBRATE_H
-#define _CALIBRATE_H
+#ifndef _FTAPE_CALIBR_H
+#define _FTAPE_CALIBR_H
/*
- * Copyright (C) 1993-1995 Bas Laarhoven.
+ * Copyright (C) 1993-1996 Bas Laarhoven.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -19,21 +19,19 @@
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
- $Source: /home/bas/distr/ftape-2.03b/RCS/calibr.h,v $
- $Author: bas $
- *
- $Revision: 1.20 $
- $Date: 1995/04/22 07:30:15 $
- $State: Beta $
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-calibr.h,v $
+ * $Revision: 1.1 $
+ * $Date: 1997/09/19 09:05:26 $
*
* This file contains a gp calibration routine for
* hardware dependent timeout functions.
*/
-#include <linux/timex.h>
-
-extern int calibrate(char *name, void (*fun) (int), int *calibr_count, int *calibr_time);
-extern unsigned timestamp(void);
-extern int timediff(int t0, int t1);
+extern void ftape_calibrate(char *name,
+ void (*fun) (unsigned int),
+ unsigned int *calibr_count,
+ unsigned int *calibr_time);
+extern unsigned int ftape_timestamp(void);
+extern unsigned int ftape_timediff(unsigned int t0, unsigned int t1);
-#endif
+#endif /* _FTAPE_CALIBR_H */
diff --git a/drivers/char/ftape/lowlevel/ftape-ctl.c b/drivers/char/ftape/lowlevel/ftape-ctl.c
new file mode 100644
index 000000000..1ce4c2843
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/ftape-ctl.c
@@ -0,0 +1,901 @@
+/*
+ * Copyright (C) 1993-1996 Bas Laarhoven,
+ * 1996-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-ctl.c,v $
+ * $Revision: 1.4 $
+ * $Date: 1997/11/11 14:37:44 $
+ *
+ * This file contains the non-read/write ftape functions for the
+ * QIC-40/80/3010/3020 floppy-tape driver "ftape" for Linux.
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/wrapper.h>
+
+#include <linux/ftape.h>
+#include <linux/qic117.h>
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,6)
+#include <asm/uaccess.h>
+#else
+#include <asm/segment.h>
+#endif
+#include <asm/io.h>
+
+#include "../lowlevel/ftape-tracing.h"
+#include "../lowlevel/ftape-io.h"
+#include "../lowlevel/ftape-ctl.h"
+#include "../lowlevel/ftape-write.h"
+#include "../lowlevel/ftape-read.h"
+#include "../lowlevel/ftape-rw.h"
+#include "../lowlevel/ftape-bsm.h"
+
+/* Global vars.
+ */
+ftape_info ftape_status = {
+/* vendor information */
+ { 0, }, /* drive type */
+/* data rates */
+ 500, /* used data rate */
+ 500, /* drive max rate */
+ 500, /* fdc max rate */
+/* drive selection, either FTAPE_SEL_A/B/C/D */
+ -1, /* drive selection */
+/* flags set after decode the drive and tape status */
+ 0, /* formatted */
+ 1, /* no tape */
+ 1, /* write protected */
+ 1, /* new tape */
+/* values of last queried drive/tape status and error */
+ {{0,}}, /* last error code */
+ {{0,}}, /* drive status, configuration, tape status */
+/* cartridge geometry */
+ 20, /* tracks_per_tape */
+ 102, /* segments_per_track */
+/* location of header segments, etc. */
+ -1, /* used_header_segment */
+ -1, /* header_segment_1 */
+ -1, /* header_segment_2 */
+ -1, /* first_data_segment */
+ -1, /* last_data_segment */
+/* the format code as stored in the header segment */
+ fmt_normal, /* format code */
+/* the default for the qic std: unknown */
+ -1,
+/* is tape running? */
+ idle, /* runner_state */
+/* is tape reading/writing/verifying/formatting/deleting */
+ idle, /* driver state */
+/* flags fatal hardware error */
+ 1, /* failure */
+/* history record */
+ { 0, } /* history record */
+};
+
+int ftape_segments_per_head = 1020;
+int ftape_segments_per_cylinder = 4;
+int ftape_init_drive_needed = 1; /* need to be global for ftape_reset_drive()
+ * in ftape-io.c
+ */
+
+/* Local vars.
+ */
+static const vendor_struct vendors[] = QIC117_VENDORS;
+static const wakeup_method methods[] = WAKEUP_METHODS;
+
+const ftape_info *ftape_get_status(void)
+{
+#if defined(STATUS_PARANOYA)
+ static ftape_info get_status;
+
+ get_status = ftape_status;
+ return &get_status;
+#else
+ return &ftape_status; /* maybe return only a copy of it to assure
+ * read only access
+ */
+#endif
+}
+
+void ftape_set_status(const ftape_info *status)
+{
+ ftape_status = *status;
+}
+
+static int ftape_not_operational(int status)
+{
+ /* return true if status indicates tape can not be used.
+ */
+ return ((status ^ QIC_STATUS_CARTRIDGE_PRESENT) &
+ (QIC_STATUS_ERROR |
+ QIC_STATUS_CARTRIDGE_PRESENT |
+ QIC_STATUS_NEW_CARTRIDGE));
+}
+
+int ftape_seek_to_eot(void)
+{
+ int status;
+ TRACE_FUN(ft_t_any);
+
+ TRACE_CATCH(ftape_ready_wait(ftape_timeout.pause, &status),);
+ while ((status & QIC_STATUS_AT_EOT) == 0) {
+ if (ftape_not_operational(status)) {
+ TRACE_EXIT -EIO;
+ }
+ TRACE_CATCH(ftape_command_wait(QIC_PHYSICAL_FORWARD,
+ ftape_timeout.rewind,&status),);
+ }
+ TRACE_EXIT 0;
+}
+
+int ftape_seek_to_bot(void)
+{
+ int status;
+ TRACE_FUN(ft_t_any);
+
+ TRACE_CATCH(ftape_ready_wait(ftape_timeout.pause, &status),);
+ while ((status & QIC_STATUS_AT_BOT) == 0) {
+ if (ftape_not_operational(status)) {
+ TRACE_EXIT -EIO;
+ }
+ TRACE_CATCH(ftape_command_wait(QIC_PHYSICAL_REVERSE,
+ ftape_timeout.rewind,&status),);
+ }
+ TRACE_EXIT 0;
+}
+
+static int ftape_new_cartridge(void)
+{
+ ft_location.track = -1; /* force seek on first access */
+ ftape_zap_read_buffers();
+ ftape_zap_write_buffers();
+ return 0;
+}
+
+int ftape_abort_operation(void)
+{
+ int result = 0;
+ int status;
+ TRACE_FUN(ft_t_flow);
+
+ if (ft_runner_status == running) {
+ TRACE(ft_t_noise, "aborting runner, waiting");
+
+ ft_runner_status = do_abort;
+ /* set timeout so that the tape will run to logical EOT
+ * if we missed the last sector and there are no queue pulses.
+ */
+ result = ftape_dumb_stop();
+ }
+ if (ft_runner_status != idle) {
+ if (ft_runner_status == do_abort) {
+ TRACE(ft_t_noise, "forcing runner abort");
+ }
+ TRACE(ft_t_noise, "stopping tape");
+ result = ftape_stop_tape(&status);
+ ft_location.known = 0;
+ ft_runner_status = idle;
+ }
+ ftape_reset_buffer();
+ ftape_zap_read_buffers();
+ ftape_set_state(idle);
+ TRACE_EXIT result;
+}
+
+static int lookup_vendor_id(unsigned int vendor_id)
+{
+ int i = 0;
+
+ while (vendors[i].vendor_id != vendor_id) {
+ if (++i >= NR_ITEMS(vendors)) {
+ return -1;
+ }
+ }
+ return i;
+}
+
+void ftape_detach_drive(void)
+{
+ TRACE_FUN(ft_t_any);
+
+ TRACE(ft_t_flow, "disabling tape drive and fdc");
+ ftape_put_drive_to_sleep(ft_drive_type.wake_up);
+ fdc_catch_stray_interrupts(1); /* one always comes */
+ fdc_disable();
+ fdc_release_irq_and_dma();
+ fdc_release_regions();
+ TRACE_EXIT;
+}
+
+static void clear_history(void)
+{
+ ft_history.used = 0;
+ ft_history.id_am_errors =
+ ft_history.id_crc_errors =
+ ft_history.data_am_errors =
+ ft_history.data_crc_errors =
+ ft_history.overrun_errors =
+ ft_history.no_data_errors =
+ ft_history.retries =
+ ft_history.crc_errors =
+ ft_history.crc_failures =
+ ft_history.ecc_failures =
+ ft_history.corrected =
+ ft_history.defects =
+ ft_history.rewinds = 0;
+}
+
+int ftape_activate_drive(vendor_struct * drive_type)
+{
+ int result = 0;
+ TRACE_FUN(ft_t_flow);
+
+ /* If we already know the drive type, wake it up.
+ * Else try to find out what kind of drive is attached.
+ */
+ if (drive_type->wake_up != unknown_wake_up) {
+ TRACE(ft_t_flow, "enabling tape drive and fdc");
+ result = ftape_wakeup_drive(drive_type->wake_up);
+ if (result < 0) {
+ TRACE(ft_t_err, "known wakeup method failed");
+ }
+ } else {
+ wake_up_types method;
+ const ft_trace_t old_tracing = TRACE_LEVEL;
+ if (TRACE_LEVEL < ft_t_flow) {
+ SET_TRACE_LEVEL(ft_t_bug);
+ }
+
+ /* Try to awaken the drive using all known methods.
+ * Lower tracing for a while.
+ */
+ for (method=no_wake_up; method < NR_ITEMS(methods); ++method) {
+ drive_type->wake_up = method;
+#ifdef CONFIG_FT_TWO_DRIVES
+ /* Test setup for dual drive configuration.
+ * /dev/rft2 uses mountain wakeup
+ * /dev/rft3 uses colorado wakeup
+ * Other systems will use the normal scheme.
+ */
+ if ((ft_drive_sel < 2) ||
+ (ft_drive_sel == 2 && method == FT_WAKE_UP_1) ||
+ (ft_drive_sel == 3 && method == FT_WAKE_UP_2)) {
+ result=ftape_wakeup_drive(drive_type->wake_up);
+ } else {
+ result = -EIO;
+ }
+#else
+ result = ftape_wakeup_drive(drive_type->wake_up);
+#endif
+ if (result >= 0) {
+ TRACE(ft_t_warn, "drive wakeup method: %s",
+ methods[drive_type->wake_up].name);
+ break;
+ }
+ }
+ SET_TRACE_LEVEL(old_tracing);
+
+ if (method >= NR_ITEMS(methods)) {
+ /* no response at all, cannot open this drive */
+ drive_type->wake_up = unknown_wake_up;
+ TRACE(ft_t_err, "no tape drive found !");
+ result = -ENODEV;
+ }
+ }
+ TRACE_EXIT result;
+}
+
+int ftape_get_drive_status(void)
+{
+ int result;
+ int status;
+ TRACE_FUN(ft_t_flow);
+
+ ft_no_tape = ft_write_protected = 0;
+ /* Tape drive is activated now.
+ * First clear error status if present.
+ */
+ do {
+ result = ftape_ready_wait(ftape_timeout.reset, &status);
+ if (result < 0) {
+ if (result == -ETIME) {
+ TRACE(ft_t_err, "ftape_ready_wait timeout");
+ } else if (result == -EINTR) {
+ TRACE(ft_t_err, "ftape_ready_wait aborted");
+ } else {
+ TRACE(ft_t_err, "ftape_ready_wait failed");
+ }
+ TRACE_EXIT -EIO;
+ }
+ /* Clear error condition (drive is ready !)
+ */
+ if (status & QIC_STATUS_ERROR) {
+ unsigned int error;
+ qic117_cmd_t command;
+
+ TRACE(ft_t_err, "error status set");
+ result = ftape_report_error(&error, &command, 1);
+ if (result < 0) {
+ TRACE(ft_t_err,
+ "report_error_code failed: %d", result);
+ /* hope it's working next time */
+ ftape_reset_drive();
+ TRACE_EXIT -EIO;
+ } else if (error != 0) {
+ TRACE(ft_t_noise, "error code : %d", error);
+ TRACE(ft_t_noise, "error command: %d", command);
+ }
+ }
+ if (status & QIC_STATUS_NEW_CARTRIDGE) {
+ unsigned int error;
+ qic117_cmd_t command;
+ const ft_trace_t old_tracing = TRACE_LEVEL;
+ SET_TRACE_LEVEL(ft_t_bug);
+
+ /* Undocumented feature: Must clear (not present!)
+ * error here or we'll fail later.
+ */
+ ftape_report_error(&error, &command, 1);
+
+ SET_TRACE_LEVEL(old_tracing);
+ TRACE(ft_t_info, "status: new cartridge");
+ ft_new_tape = 1;
+ } else {
+ ft_new_tape = 0;
+ }
+ FT_SIGNAL_EXIT(_DONT_BLOCK);
+ } while (status & QIC_STATUS_ERROR);
+
+ ft_no_tape = !(status & QIC_STATUS_CARTRIDGE_PRESENT);
+ ft_write_protected = (status & QIC_STATUS_WRITE_PROTECT) != 0;
+ if (ft_no_tape) {
+ TRACE(ft_t_warn, "no cartridge present");
+ } else {
+ if (ft_write_protected) {
+ TRACE(ft_t_noise, "Write protected cartridge");
+ }
+ }
+ TRACE_EXIT 0;
+}
+
+void ftape_log_vendor_id(void)
+{
+ int vendor_index;
+ TRACE_FUN(ft_t_flow);
+
+ ftape_report_vendor_id(&ft_drive_type.vendor_id);
+ vendor_index = lookup_vendor_id(ft_drive_type.vendor_id);
+ if (ft_drive_type.vendor_id == UNKNOWN_VENDOR &&
+ ft_drive_type.wake_up == wake_up_colorado) {
+ vendor_index = 0;
+ /* hack to get rid of all this mail */
+ ft_drive_type.vendor_id = 0;
+ }
+ if (vendor_index < 0) {
+ /* Unknown vendor id, first time opening device. The
+ * drive_type remains set to type found at wakeup
+ * time, this will probably keep the driver operating
+ * for this new vendor.
+ */
+ TRACE(ft_t_warn, "\n"
+ KERN_INFO "============ unknown vendor id ===========\n"
+ KERN_INFO "A new, yet unsupported tape drive is found\n"
+ KERN_INFO "Please report the following values:\n"
+ KERN_INFO " Vendor id : 0x%04x\n"
+ KERN_INFO " Wakeup method : %s\n"
+ KERN_INFO "And a description of your tape drive\n"
+ KERN_INFO "to "THE_FTAPE_MAINTAINER"\n"
+ KERN_INFO "==========================================",
+ ft_drive_type.vendor_id,
+ methods[ft_drive_type.wake_up].name);
+ ft_drive_type.speed = 0; /* unknown */
+ } else {
+ ft_drive_type.name = vendors[vendor_index].name;
+ ft_drive_type.speed = vendors[vendor_index].speed;
+ TRACE(ft_t_info, "tape drive type: %s", ft_drive_type.name);
+ /* scan all methods for this vendor_id in table */
+ while(ft_drive_type.wake_up != vendors[vendor_index].wake_up) {
+ if (vendor_index < NR_ITEMS(vendors) - 1 &&
+ vendors[vendor_index + 1].vendor_id
+ ==
+ ft_drive_type.vendor_id) {
+ ++vendor_index;
+ } else {
+ break;
+ }
+ }
+ if (ft_drive_type.wake_up != vendors[vendor_index].wake_up) {
+ TRACE(ft_t_warn, "\n"
+ KERN_INFO "==========================================\n"
+ KERN_INFO "wakeup type mismatch:\n"
+ KERN_INFO "found: %s, expected: %s\n"
+ KERN_INFO "please report this to "THE_FTAPE_MAINTAINER"\n"
+ KERN_INFO "==========================================",
+ methods[ft_drive_type.wake_up].name,
+ methods[vendors[vendor_index].wake_up].name);
+ }
+ }
+ TRACE_EXIT;
+}
+
+void ftape_calc_timeouts(unsigned int qic_std,
+ unsigned int data_rate,
+ unsigned int tape_len)
+{
+ int speed; /* deci-ips ! */
+ int ff_speed;
+ int length;
+ TRACE_FUN(ft_t_any);
+
+ /* tape transport speed
+ * data rate: QIC-40 QIC-80 QIC-3010 QIC-3020
+ *
+ * 250 Kbps 25 ips n/a n/a n/a
+ * 500 Kbps 50 ips 34 ips 22.6 ips n/a
+ * 1 Mbps n/a 68 ips 45.2 ips 22.6 ips
+ * 2 Mbps n/a n/a n/a 45.2 ips
+ *
+ * fast tape transport speed is at least 68 ips.
+ */
+ switch (qic_std) {
+ case QIC_TAPE_QIC40:
+ speed = (data_rate == 250) ? 250 : 500;
+ break;
+ case QIC_TAPE_QIC80:
+ speed = (data_rate == 500) ? 340 : 680;
+ break;
+ case QIC_TAPE_QIC3010:
+ speed = (data_rate == 500) ? 226 : 452;
+ break;
+ case QIC_TAPE_QIC3020:
+ speed = (data_rate == 1000) ? 226 : 452;
+ break;
+ default:
+ TRACE(ft_t_bug, "Unknown qic_std (bug) ?");
+ speed = 500;
+ break;
+ }
+ if (ft_drive_type.speed == 0) {
+ unsigned long t0;
+ static int dt = 0; /* keep gcc from complaining */
+ static int first_time = 1;
+
+ /* Measure the time it takes to wind to EOT and back to BOT.
+ * If the tape length is known, calculate the rewind speed.
+ * Else keep the time value for calculation of the rewind
+ * speed later on, when the length _is_ known.
+ * Ask for a report only when length and speed are both known.
+ */
+ if (first_time) {
+ ftape_seek_to_bot();
+ t0 = jiffies;
+ ftape_seek_to_eot();
+ ftape_seek_to_bot();
+ dt = (int) (((jiffies - t0) * FT_USPT) / 1000);
+ if (dt < 1) {
+ dt = 1; /* prevent div by zero on failures */
+ }
+ first_time = 0;
+ TRACE(ft_t_info,
+ "trying to determine seek timeout, got %d msec",
+ dt);
+ }
+ if (tape_len != 0) {
+ ft_drive_type.speed =
+ (2 * 12 * tape_len * 1000) / dt;
+ TRACE(ft_t_warn, "\n"
+ KERN_INFO "==========================================\n"
+ KERN_INFO "drive type: %s\n"
+ KERN_INFO "delta time = %d ms, length = %d ft\n"
+ KERN_INFO "has a maximum tape speed of %d ips\n"
+ KERN_INFO "please report this to "THE_FTAPE_MAINTAINER"\n"
+ KERN_INFO "==========================================",
+ ft_drive_type.name, dt, tape_len,
+ ft_drive_type.speed);
+ }
+ }
+ /* Handle unknown length tapes as very long ones. We'll
+ * determine the actual length from a header segment later.
+ * This is normal for all modern (Wide,TR1/2/3) formats.
+ */
+ if (tape_len <= 0) {
+ TRACE(ft_t_noise,
+ "Unknown tape length, using maximal timeouts");
+ length = QIC_TOP_TAPE_LEN; /* use worst case values */
+ } else {
+ length = tape_len; /* use actual values */
+ }
+ if (ft_drive_type.speed == 0) {
+ ff_speed = speed;
+ } else {
+ ff_speed = ft_drive_type.speed;
+ }
+ /* time to go from bot to eot at normal speed (data rate):
+ * time = (1+delta) * length (ft) * 12 (inch/ft) / speed (ips)
+ * delta = 10 % for seek speed, 20 % for rewind speed.
+ */
+ ftape_timeout.seek = (length * 132 * FT_SECOND) / speed;
+ ftape_timeout.rewind = (length * 144 * FT_SECOND) / (10 * ff_speed);
+ ftape_timeout.reset = 20 * FT_SECOND + ftape_timeout.rewind;
+ TRACE(ft_t_noise, "timeouts for speed = %d, length = %d\n"
+ KERN_INFO "seek timeout : %d sec\n"
+ KERN_INFO "rewind timeout: %d sec\n"
+ KERN_INFO "reset timeout : %d sec",
+ speed, length,
+ (ftape_timeout.seek + 500) / 1000,
+ (ftape_timeout.rewind + 500) / 1000,
+ (ftape_timeout.reset + 500) / 1000);
+ TRACE_EXIT;
+}
+
+/* This function calibrates the datarate (i.e. determines the maximal
+ * usable data rate) and sets the global variable ft_qic_std to qic_std
+ *
+ */
+int ftape_calibrate_data_rate(unsigned int qic_std)
+{
+ int rate = ft_fdc_rate_limit;
+ int result;
+ TRACE_FUN(ft_t_flow);
+
+ ft_qic_std = qic_std;
+
+ if (ft_qic_std == -1) {
+ TRACE_ABORT(-EIO, ft_t_err,
+ "Unable to determine data rate if QIC standard is unknown");
+ }
+
+ /* Select highest rate supported by both fdc and drive.
+ * Start with highest rate supported by the fdc.
+ */
+ while (fdc_set_data_rate(rate) < 0 && rate > 250) {
+ rate /= 2;
+ }
+ TRACE(ft_t_info,
+ "Highest FDC supported data rate: %d Kbps", rate);
+ ft_fdc_max_rate = rate;
+ do {
+ result = ftape_set_data_rate(rate, ft_qic_std);
+ } while (result == -EINVAL && (rate /= 2) > 250);
+ if (result < 0) {
+ TRACE_ABORT(-EIO, ft_t_err, "set datarate failed");
+ }
+ ft_data_rate = rate;
+ TRACE_EXIT 0;
+}
+
+int ftape_init_drive(void)
+{
+ int status;
+ qic_model model;
+ unsigned int qic_std;
+ unsigned int data_rate;
+ TRACE_FUN(ft_t_flow);
+
+ ftape_init_drive_needed = 0; /* don't retry if this fails ? */
+ TRACE_CATCH(ftape_report_raw_drive_status(&status),);
+ if (status & QIC_STATUS_CARTRIDGE_PRESENT) {
+ if (!(status & QIC_STATUS_AT_BOT)) {
+ /* Antique drives will get here after a soft reset,
+ * modern ones only if the driver is loaded when the
+ * tape wasn't rewound properly.
+ */
+ /* Tape should be at bot if new cartridge ! */
+ ftape_seek_to_bot();
+ }
+ if (!(status & QIC_STATUS_REFERENCED)) {
+ TRACE(ft_t_flow, "starting seek_load_point");
+ TRACE_CATCH(ftape_command_wait(QIC_SEEK_LOAD_POINT,
+ ftape_timeout.reset,
+ &status),);
+ }
+ }
+ ft_formatted = (status & QIC_STATUS_REFERENCED) != 0;
+ if (!ft_formatted) {
+ TRACE(ft_t_warn, "Warning: tape is not formatted !");
+ }
+
+ /* report configuration aborts when ftape_tape_len == -1
+ * unknown qic_std is okay if not formatted.
+ */
+ TRACE_CATCH(ftape_report_configuration(&model,
+ &data_rate,
+ &qic_std,
+ &ftape_tape_len),);
+
+ /* Maybe add the following to the /proc entry
+ */
+ TRACE(ft_t_info, "%s drive @ %d Kbps",
+ (model == prehistoric) ? "prehistoric" :
+ ((model == pre_qic117c) ? "pre QIC-117C" :
+ ((model == post_qic117b) ? "post QIC-117B" :
+ "post QIC-117D")), data_rate);
+
+ if (ft_formatted) {
+ /* initialize ft_used_data_rate to maximum value
+ * and set ft_qic_std
+ */
+ TRACE_CATCH(ftape_calibrate_data_rate(qic_std),);
+ if (ftape_tape_len == 0) {
+ TRACE(ft_t_info, "unknown length QIC-%s tape",
+ (ft_qic_std == QIC_TAPE_QIC40) ? "40" :
+ ((ft_qic_std == QIC_TAPE_QIC80) ? "80" :
+ ((ft_qic_std == QIC_TAPE_QIC3010)
+ ? "3010" : "3020")));
+ } else {
+ TRACE(ft_t_info, "%d ft. QIC-%s tape", ftape_tape_len,
+ (ft_qic_std == QIC_TAPE_QIC40) ? "40" :
+ ((ft_qic_std == QIC_TAPE_QIC80) ? "80" :
+ ((ft_qic_std == QIC_TAPE_QIC3010)
+ ? "3010" : "3020")));
+ }
+ ftape_calc_timeouts(ft_qic_std, ft_data_rate, ftape_tape_len);
+ /* soft write-protect QIC-40/QIC-80 cartridges used with a
+ * Colorado T3000 drive. Buggy hardware!
+ */
+ if ((ft_drive_type.vendor_id == 0x011c6) &&
+ ((ft_qic_std == QIC_TAPE_QIC40 ||
+ ft_qic_std == QIC_TAPE_QIC80) &&
+ !ft_write_protected)) {
+ TRACE(ft_t_warn, "\n"
+ KERN_INFO "The famous Colorado T3000 bug:\n"
+ KERN_INFO "%s drives can't write QIC40 and QIC80\n"
+ KERN_INFO "cartridges but don't set the write-protect flag!",
+ ft_drive_type.name);
+ ft_write_protected = 1;
+ }
+ } else {
+ /* Doesn't make too much sense to set the data rate
+ * because we don't know what to use for the write
+ * precompensation.
+ * Need to do this again when formatting the cartridge.
+ */
+ ft_data_rate = data_rate;
+ ftape_calc_timeouts(QIC_TAPE_QIC40,
+ data_rate,
+ ftape_tape_len);
+ }
+ ftape_new_cartridge();
+ TRACE_EXIT 0;
+}
+
+static void ftape_munmap(void)
+{
+ int i;
+ TRACE_FUN(ft_t_flow);
+
+ for (i = 0; i < ft_nr_buffers; i++) {
+ ft_buffer[i]->mmapped = 0;
+ }
+ TRACE_EXIT;
+}
+
+/* Map the dma buffers into the virtual address range given by vma.
+ * We only check the caller doesn't map non-existent buffers. We
+ * don't check for multiple mappings.
+ */
+int ftape_mmap(struct vm_area_struct *vma)
+{
+ int num_buffers;
+ int i;
+ TRACE_FUN(ft_t_flow);
+
+ if (ft_failure) {
+ TRACE_EXIT -ENODEV;
+ }
+ if ((vma_get_flags(vma) & (VM_READ|VM_WRITE)) == 0) {
+ TRACE_ABORT(-EINVAL, ft_t_err, "Undefined mmap() access");
+ }
+ if (vma_get_offset (vma) != 0) {
+ TRACE_ABORT(-EINVAL, ft_t_err, "offset must be 0");
+ }
+ if ((vma_get_end (vma) - vma_get_start (vma)) % FT_BUFF_SIZE != 0) {
+ TRACE_ABORT(-EINVAL, ft_t_err,
+ "size = %ld, should be a multiple of %d",
+ vma_get_end (vma) - vma_get_start (vma),
+ FT_BUFF_SIZE);
+ }
+ num_buffers = (vma_get_end (vma) - vma_get_start (vma)) / FT_BUFF_SIZE;
+ if (num_buffers > ft_nr_buffers) {
+ TRACE_ABORT(-EINVAL,
+ ft_t_err, "size = %ld, should be less than %d",
+ vma_get_end (vma) - vma_get_start (vma),
+ ft_nr_buffers * FT_BUFF_SIZE);
+ }
+ if (ft_driver_state != idle) {
+ /* this also clears the buffer states
+ */
+ ftape_abort_operation();
+ } else {
+ ftape_reset_buffer();
+ }
+ for (i = 0; i < num_buffers; i++) {
+ TRACE_CATCH(remap_page_range(vma_get_start (vma) +
+ i * FT_BUFF_SIZE,
+ virt_to_phys(ft_buffer[i]->address),
+ FT_BUFF_SIZE,
+ vma_get_page_prot (vma)),
+ _res = -EAGAIN);
+ TRACE(ft_t_noise, "remapped dma buffer @ %p to location @ %p",
+ ft_buffer[i]->address,
+ (void *)(vma_get_start(vma) + i * FT_BUFF_SIZE));
+ }
+ for (i = 0; i < num_buffers; i++) {
+ memset(ft_buffer[i]->address, 0xAA, FT_BUFF_SIZE);
+ ft_buffer[i]->mmapped++;
+ }
+ TRACE_EXIT 0;
+}
+
+static void ftape_init_driver(void); /* forward declaration */
+
+/* OPEN routine called by kernel-interface code
+ */
+int ftape_enable(int drive_selection)
+{
+ TRACE_FUN(ft_t_any);
+
+ if (ft_drive_sel == -1 || ft_drive_sel != drive_selection) {
+ /* Other selection than last time
+ */
+ ftape_init_driver();
+ }
+ ft_drive_sel = FTAPE_SEL(drive_selection);
+ ft_failure = 0;
+ TRACE_CATCH(fdc_init(),); /* init & detect fdc */
+ TRACE_CATCH(ftape_activate_drive(&ft_drive_type),
+ fdc_disable();
+ fdc_release_irq_and_dma();
+ fdc_release_regions());
+ TRACE_CATCH(ftape_get_drive_status(), ftape_detach_drive());
+ if (ft_drive_type.vendor_id == UNKNOWN_VENDOR) {
+ ftape_log_vendor_id();
+ }
+ if (ft_new_tape) {
+ ftape_init_drive_needed = 1;
+ }
+ if (!ft_no_tape && ftape_init_drive_needed) {
+ TRACE_CATCH(ftape_init_drive(), ftape_detach_drive());
+ }
+ ftape_munmap(); /* clear the mmap flag */
+ clear_history();
+ TRACE_EXIT 0;
+}
+
+/* release routine called by the high level interface modules
+ * zftape or sftape.
+ */
+void ftape_disable(void)
+{
+ int i;
+ TRACE_FUN(ft_t_any);
+
+ for (i = 0; i < ft_nr_buffers; i++) {
+ if (ft_buffer[i]->mmapped) {
+ TRACE(ft_t_noise, "first byte of buffer %d: 0x%02x",
+ i, *ft_buffer[i]->address);
+ }
+ }
+ if ((current->signal & _DONT_BLOCK) &&
+ !(current->signal & _NEVER_BLOCK) &&
+ ftape_tape_running) {
+ TRACE(ft_t_warn,
+ "Interrupted by fatal signal and tape still running");
+ ftape_dumb_stop();
+ ftape_abort_operation(); /* it's annoying */
+ } else {
+ ftape_set_state(idle);
+ }
+ ftape_detach_drive();
+ if (ft_history.used) {
+ TRACE(ft_t_info, "== Non-fatal errors this run: ==");
+ TRACE(ft_t_info, "fdc isr statistics:\n"
+ KERN_INFO " id_am_errors : %3d\n"
+ KERN_INFO " id_crc_errors : %3d\n"
+ KERN_INFO " data_am_errors : %3d\n"
+ KERN_INFO " data_crc_errors : %3d\n"
+ KERN_INFO " overrun_errors : %3d\n"
+ KERN_INFO " no_data_errors : %3d\n"
+ KERN_INFO " retries : %3d",
+ ft_history.id_am_errors, ft_history.id_crc_errors,
+ ft_history.data_am_errors, ft_history.data_crc_errors,
+ ft_history.overrun_errors, ft_history.no_data_errors,
+ ft_history.retries);
+ if (ft_history.used & 1) {
+ TRACE(ft_t_info, "ecc statistics:\n"
+ KERN_INFO " crc_errors : %3d\n"
+ KERN_INFO " crc_failures : %3d\n"
+ KERN_INFO " ecc_failures : %3d\n"
+ KERN_INFO " sectors corrected: %3d",
+ ft_history.crc_errors, ft_history.crc_failures,
+ ft_history.ecc_failures, ft_history.corrected);
+ }
+ if (ft_history.defects > 0) {
+ TRACE(ft_t_warn, "Warning: %d media defects!",
+ ft_history.defects);
+ }
+ if (ft_history.rewinds > 0) {
+ TRACE(ft_t_info, "tape motion statistics:\n"
+ KERN_INFO "repositions : %3d",
+ ft_history.rewinds);
+ }
+ }
+ ft_failure = 1;
+ TRACE_EXIT;
+}
+
+static void ftape_init_driver(void)
+{
+ TRACE_FUN(ft_t_flow);
+
+ ft_drive_type.vendor_id = UNKNOWN_VENDOR;
+ ft_drive_type.speed = 0;
+ ft_drive_type.wake_up = unknown_wake_up;
+ ft_drive_type.name = "Unknown";
+
+ ftape_timeout.seek = 650 * FT_SECOND;
+ ftape_timeout.reset = 670 * FT_SECOND;
+ ftape_timeout.rewind = 650 * FT_SECOND;
+ ftape_timeout.head_seek = 15 * FT_SECOND;
+ ftape_timeout.stop = 5 * FT_SECOND;
+ ftape_timeout.pause = 16 * FT_SECOND;
+
+ ft_qic_std = -1;
+ ftape_tape_len = 0; /* unknown */
+ ftape_current_command = 0;
+ ftape_current_cylinder = -1;
+
+ ft_segments_per_track = 102;
+ ftape_segments_per_head = 1020;
+ ftape_segments_per_cylinder = 4;
+ ft_tracks_per_tape = 20;
+
+ ft_failure = 1;
+
+ ft_formatted = 0;
+ ft_no_tape = 1;
+ ft_write_protected = 1;
+ ft_new_tape = 1;
+
+ ft_driver_state = idle;
+
+ ft_data_rate =
+ ft_fdc_max_rate = 500;
+ ft_drive_max_rate = 0; /* triggers set_rate_test() */
+
+ ftape_init_drive_needed = 1;
+
+ ft_header_segment_1 = -1;
+ ft_header_segment_2 = -1;
+ ft_used_header_segment = -1;
+ ft_first_data_segment = -1;
+ ft_last_data_segment = -1;
+
+ ft_location.track = -1;
+ ft_location.known = 0;
+
+ ftape_tape_running = 0;
+ ftape_might_be_off_track = 1;
+
+ ftape_new_cartridge(); /* init some tape related variables */
+ ftape_init_bsm();
+ TRACE_EXIT;
+}
diff --git a/drivers/char/ftape/lowlevel/ftape-ctl.h b/drivers/char/ftape/lowlevel/ftape-ctl.h
new file mode 100644
index 000000000..ab7c535d7
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/ftape-ctl.h
@@ -0,0 +1,172 @@
+#ifndef _FTAPE_CTL_H
+#define _FTAPE_CTL_H
+
+/*
+ * Copyright (C) 1993-1996 Bas Laarhoven,
+ * (C) 1996-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-ctl.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:09 $
+ *
+ * This file contains the non-standard IOCTL related definitions
+ * for the QIC-40/80/3010/3020 floppy-tape driver "ftape" for
+ * Linux.
+ */
+
+#include <linux/ioctl.h>
+#include <linux/mtio.h>
+#include <linux/ftape-vendors.h>
+
+#include "../lowlevel/ftape-rw.h"
+#include <linux/ftape-header-segment.h>
+
+typedef struct {
+ int used; /* any reading or writing done */
+ /* isr statistics */
+ unsigned int id_am_errors; /* id address mark not found */
+ unsigned int id_crc_errors; /* crc error in id address mark */
+ unsigned int data_am_errors; /* data address mark not found */
+ unsigned int data_crc_errors; /* crc error in data field */
+ unsigned int overrun_errors; /* fdc access timing problem */
+ unsigned int no_data_errors; /* sector not found */
+ unsigned int retries; /* number of tape retries */
+ /* ecc statistics */
+ unsigned int crc_errors; /* crc error in data */
+ unsigned int crc_failures; /* bad data without crc error */
+ unsigned int ecc_failures; /* failed to correct */
+ unsigned int corrected; /* total sectors corrected */
+ /* general statistics */
+ unsigned int rewinds; /* number of tape rewinds */
+ unsigned int defects; /* bad sectors due to media defects */
+} history_record;
+
+/* this structure contains * ALL * information that we want
+ * our child modules to know about, but don't want them to
+ * modify.
+ */
+typedef struct {
+ /* vendor information */
+ vendor_struct fti_drive_type;
+ /* data rates */
+ unsigned int fti_used_data_rate;
+ unsigned int fti_drive_max_rate;
+ unsigned int fti_fdc_max_rate;
+ /* drive selection, either FTAPE_SEL_A/B/C/D */
+ int fti_drive_sel;
+ /* flags set after decode the drive and tape status */
+ unsigned int fti_formatted :1;
+ unsigned int fti_no_tape :1;
+ unsigned int fti_write_protected:1;
+ unsigned int fti_new_tape :1;
+ /* values of last queried drive/tape status and error */
+ ft_drive_error fti_last_error;
+ ft_drive_status fti_last_status;
+ /* cartridge geometry */
+ unsigned int fti_tracks_per_tape;
+ unsigned int fti_segments_per_track;
+ /* location of header segments, etc. */
+ int fti_used_header_segment;
+ int fti_header_segment_1;
+ int fti_header_segment_2;
+ int fti_first_data_segment;
+ int fti_last_data_segment;
+ /* the format code as stored in the header segment */
+ ft_format_type fti_format_code;
+ /* the following is the sole reason for the ftape_set_status() call */
+ unsigned int fti_qic_std;
+ /* is tape running? */
+ volatile enum runner_status_enum fti_runner_status;
+ /* is tape reading/writing/verifying/formatting/deleting */
+ buffer_state_enum fti_state;
+ /* flags fatal hardware error */
+ unsigned int fti_failure:1;
+ /* history record */
+ history_record fti_history;
+} ftape_info;
+
+/* vendor information */
+#define ft_drive_type ftape_status.fti_drive_type
+/* data rates */
+#define ft_data_rate ftape_status.fti_used_data_rate
+#define ft_drive_max_rate ftape_status.fti_drive_max_rate
+#define ft_fdc_max_rate ftape_status.fti_fdc_max_rate
+/* drive selection, either FTAPE_SEL_A/B/C/D */
+#define ft_drive_sel ftape_status.fti_drive_sel
+/* flags set after decode the drive and tape status */
+#define ft_formatted ftape_status.fti_formatted
+#define ft_no_tape ftape_status.fti_no_tape
+#define ft_write_protected ftape_status.fti_write_protected
+#define ft_new_tape ftape_status.fti_new_tape
+/* values of last queried drive/tape status and error */
+#define ft_last_error ftape_status.fti_last_error
+#define ft_last_status ftape_status.fti_last_status
+/* cartridge geometry */
+#define ft_tracks_per_tape ftape_status.fti_tracks_per_tape
+#define ft_segments_per_track ftape_status.fti_segments_per_track
+/* the format code as stored in the header segment */
+#define ft_format_code ftape_status.fti_format_code
+/* the qic status as returned by report drive configuration */
+#define ft_qic_std ftape_status.fti_qic_std
+#define ft_used_header_segment ftape_status.fti_used_header_segment
+#define ft_header_segment_1 ftape_status.fti_header_segment_1
+#define ft_header_segment_2 ftape_status.fti_header_segment_2
+#define ft_first_data_segment ftape_status.fti_first_data_segment
+#define ft_last_data_segment ftape_status.fti_last_data_segment
+/* is tape running? */
+#define ft_runner_status ftape_status.fti_runner_status
+/* is tape reading/writing/verifying/formatting/deleting */
+#define ft_driver_state ftape_status.fti_state
+/* flags fatal hardware error */
+#define ft_failure ftape_status.fti_failure
+/* history record */
+#define ft_history ftape_status.fti_history
+
+/* compatibility with old kernel versions
+ */
+#if LINUX_VERSION_CODE <= KERNEL_VER(1,2,13)
+#define _IOC_SIZE(cmd) (((cmd) & IOCSIZE_MASK) >> IOCSIZE_SHIFT)
+#define _IOC_DIR(cmd) (cmd)
+#define _IOC_WRITE IOC_IN
+#define _IOC_READ IOC_OUT
+#endif
+
+/*
+ * ftape-ctl.c defined global vars.
+ */
+extern ftape_info ftape_status;
+extern int ftape_segments_per_head;
+extern int ftape_segments_per_cylinder;
+extern int ftape_init_drive_needed;
+
+/*
+ * ftape-ctl.c defined global functions.
+ */
+extern int ftape_mmap(struct vm_area_struct *vma);
+extern int ftape_enable(int drive_selection);
+extern void ftape_disable(void);
+extern int ftape_seek_to_bot(void);
+extern int ftape_seek_to_eot(void);
+extern int ftape_abort_operation(void);
+extern void ftape_calc_timeouts(unsigned int qic_std,
+ unsigned int data_rate,
+ unsigned int tape_len);
+extern int ftape_calibrate_data_rate(unsigned int qic_std);
+extern int ftape_init_drive(void);
+extern const ftape_info *ftape_get_status(void);
+#endif
diff --git a/drivers/char/ftape/ecc.c b/drivers/char/ftape/lowlevel/ftape-ecc.c
index 19708ee68..e5632f674 100644
--- a/drivers/char/ftape/ecc.c
+++ b/drivers/char/ftape/lowlevel/ftape-ecc.c
@@ -1,72 +1,57 @@
-/* Yo, Emacs! we're -*- Linux-C -*-
+/*
*
* Copyright (c) 1993 Ning and David Mosberger.
+
+ This is based on code originally written by Bas Laarhoven (bas@vimec.nl)
+ and David L. Brown, Jr., and incorporates improvements suggested by
+ Kai Harrekilde-Petersen.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139,
+ USA.
+
*
- * This is based on code originally written by Bas Laarhoven (bas@vimec.nl)
- * and David L. Brown, Jr., and incorporates improvements suggested by
- * Kai Harrekilde-Petersen.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2, or (at
- * your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139,
- * USA.
- *
- * $Source: /home/bas/distr/ftape-2.03b/RCS/ecc.c,v $
- * $Author: bas $
- *
- * $Revision: 1.32 $
- * $Date: 1995/04/22 07:30:15 $
- * $State: Beta $
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-ecc.c,v $
+ * $Revision: 1.3 $
+ * $Date: 1997/10/05 19:18:10 $
*
* This file contains the Reed-Solomon error correction code
* for the QIC-40/80 floppy-tape driver for Linux.
*/
#include <linux/ftape.h>
-#include <asm/byteorder.h>
-
-#include "tracing.h"
-#include "ecc.h"
-
-#ifdef TEST
-
-#undef TRACE()
-#undef TRACE_()
-#undef TRACE()
-#undef TRACEi()
-#undef TRACElx()
-#undef TRACE_FUN()
-#undef TRACE_EXIT
-#define printk printf
-#define TRACE_FUN( level, name) char __fun[] = name
-#define TRACE_EXIT
-#define TRACE_(l,m) { if (ftape_ecc_tracing >= (l) && (l) <= TOP_LEVEL) { \
- printk( "[%03d] " __FILE__ " (%s) - ", (int)ftape_trace_id++, __fun); \
- m; } }
-#define TRACE(l,m) TRACE_(l,printk(m".\n"))
-#define TRACEi(l,m,i) TRACE_(l,printk(m" %d.\n",i))
-#define TRACElx(l,m,i) TRACE_(l,printk(m" 0x%08lx.\n",i))
-
-int ftape_ecc_tracing = 1;
-unsigned char ftape_trace_id = 0;
-
-#endif /* TEST */
-/*
- * Notice: to minimize the potential for confusion, we use r to
- * denote the independent variable of the polynomials
- * in the Galois Field GF(2^8). We reserve x for polynomials
- * that that have coefficients in GF(2^8).
+#include "../lowlevel/ftape-tracing.h"
+#include "../lowlevel/ftape-ecc.h"
+
+/* Machines that are big-endian should define macro BIG_ENDIAN.
+ * Unfortunately, there doesn't appear to be a standard include file
+ * that works for all OSs.
+ */
+
+#if defined(__sparc__) || defined(__hppa)
+#define BIG_ENDIAN
+#endif /* __sparc__ || __hppa */
+
+#if defined(__mips__)
+#error Find a smart way to determine the Endianness of the MIPS CPU
+#endif
+
+/* Notice: to minimize the potential for confusion, we use r to
+ * denote the independent variable of the polynomials in the
+ * Galois Field GF(2^8). We reserve x for polynomials that
+ * that have coefficients in GF(2^8).
*
* The Galois Field in which coefficient arithmetic is performed are
* the polynomials over Z_2 (i.e., 0 and 1) modulo the irreducible
@@ -96,22 +81,22 @@ unsigned char ftape_trace_id = 0;
* Notice that r^-1 = r^254 as exponent arithmetic is performed
* modulo 2^8-1 = 255.
*
- * For more information on Galois Fields and Reed-Solomon codes,
- * refer to any good book. I found _An Introduction to Error
- * Correcting Codes with Applications_ by S. A. Vanstone and
- * P. C. van Oorschot to be a good introduction into the former.
- * _CODING THEORY: The Essentials_ I found very useful for its
- * concise description of Reed-Solomon encoding/decoding.
+ * For more information on Galois Fields and Reed-Solomon codes, refer
+ * to any good book. I found _An Introduction to Error Correcting
+ * Codes with Applications_ by S. A. Vanstone and P. C. van Oorschot
+ * to be a good introduction into the former. _CODING THEORY: The
+ * Essentials_ I found very useful for its concise description of
+ * Reed-Solomon encoding/decoding.
*
*/
-typedef unsigned char Matrix[3][3];
+typedef __u8 Matrix[3][3];
/*
* gfpow[] is defined such that gfpow[i] returns r^i if
* i is in the range [0..255].
*/
-static const unsigned char gfpow[] =
+static const __u8 gfpow[] =
{
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80,
0x87, 0x89, 0x95, 0xad, 0xdd, 0x3d, 0x7a, 0xf4,
@@ -151,7 +136,7 @@ static const unsigned char gfpow[] =
* This is a log table. That is, gflog[r^i] returns i (modulo f(r)).
* gflog[0] is undefined and the first element is therefore not valid.
*/
-static const unsigned char gflog[256] =
+static const __u8 gflog[256] =
{
0xff, 0x00, 0x01, 0x63, 0x02, 0xc6, 0x64, 0x6a,
0x03, 0xcd, 0xc7, 0xbc, 0x65, 0x7e, 0x6b, 0x2a,
@@ -187,12 +172,10 @@ static const unsigned char gflog[256] =
0xf6, 0x87, 0xa5, 0x17, 0x3a, 0xa3, 0x3c, 0xb7
};
-/*
- * This is a multiplication table for the factor
- * 0xc0 (i.e., r^105 (modulo f(r)).
+/* This is a multiplication table for the factor 0xc0 (i.e., r^105 (mod f(r)).
* gfmul_c0[f] returns r^105 * f(r) (modulo f(r)).
*/
-static const unsigned char gfmul_c0[256] =
+static const __u8 gfmul_c0[256] =
{
0x00, 0xc0, 0x07, 0xc7, 0x0e, 0xce, 0x09, 0xc9,
0x1c, 0xdc, 0x1b, 0xdb, 0x12, 0xd2, 0x15, 0xd5,
@@ -229,10 +212,9 @@ static const unsigned char gfmul_c0[256] =
};
-/*
- * Returns V modulo 255 provided V is in the range -255,-254,...,509.
+/* Returns V modulo 255 provided V is in the range -255,-254,...,509.
*/
-static inline unsigned char mod255(int v)
+static inline __u8 mod255(int v)
{
if (v > 0) {
if (v < 255) {
@@ -246,19 +228,17 @@ static inline unsigned char mod255(int v)
}
-/*
- * Add two numbers in the field. Addition in this field is
- * equivalent to a bit-wise exclusive OR operation---subtraction
- * is therefore identical to addition.
+/* Add two numbers in the field. Addition in this field is equivalent
+ * to a bit-wise exclusive OR operation---subtraction is therefore
+ * identical to addition.
*/
-static inline unsigned char gfadd(unsigned char a, unsigned char b)
+static inline __u8 gfadd(__u8 a, __u8 b)
{
return a ^ b;
}
-/*
- * Add two vectors of numbers in the field. Each byte in A and B get
+/* Add two vectors of numbers in the field. Each byte in A and B gets
* added individually.
*/
static inline unsigned long gfadd_long(unsigned long a, unsigned long b)
@@ -267,10 +247,9 @@ static inline unsigned long gfadd_long(unsigned long a, unsigned long b)
}
-/*
- * Multiply two numbers in the field:
+/* Multiply two numbers in the field:
*/
-static inline unsigned char gfmul(unsigned char a, unsigned char b)
+static inline __u8 gfmul(__u8 a, __u8 b)
{
if (a && b) {
return gfpow[mod255(gflog[a] + gflog[b])];
@@ -280,11 +259,10 @@ static inline unsigned char gfmul(unsigned char a, unsigned char b)
}
-/*
- * Just like gfmul, except we have already looked up the log
- * of the second number.
+/* Just like gfmul, except we have already looked up the log of the
+ * second number.
*/
-static inline unsigned char gfmul_exp(unsigned char a, int b)
+static inline __u8 gfmul_exp(__u8 a, int b)
{
if (a) {
return gfpow[mod255(gflog[a] + b)];
@@ -294,162 +272,159 @@ static inline unsigned char gfmul_exp(unsigned char a, int b)
}
-/*
- * Just like gfmul_exp, except that A is a vector of numbers. That is,
- * each byte in A gets multiplied by gfpow[mod255(B)].
+/* Just like gfmul_exp, except that A is a vector of numbers. That
+ * is, each byte in A gets multiplied by gfpow[mod255(B)].
*/
static inline unsigned long gfmul_exp_long(unsigned long a, int b)
{
- TRACE_FUN(8, "gfmul_exp_long");
- unsigned char t;
+ __u8 t;
if (sizeof(long) == 4) {
- TRACE_EXIT;
- return
- ((t = a >> 24 & 0xff) ? (((unsigned long) gfpow[mod255(gflog[t] + b)]) << 24) : 0) |
- ((t = a >> 16 & 0xff) ? (((unsigned long) gfpow[mod255(gflog[t] + b)]) << 16) : 0) |
- ((t = a >> 8 & 0xff) ? (((unsigned long) gfpow[mod255(gflog[t] + b)]) << 8) : 0) |
- ((t = a >> 0 & 0xff) ? (((unsigned long) gfpow[mod255(gflog[t] + b)]) << 0) : 0);
-#if !defined(linux)
+ return (
+ ((t = (__u32)a >> 24 & 0xff) ?
+ (((__u32) gfpow[mod255(gflog[t] + b)]) << 24) : 0) |
+ ((t = (__u32)a >> 16 & 0xff) ?
+ (((__u32) gfpow[mod255(gflog[t] + b)]) << 16) : 0) |
+ ((t = (__u32)a >> 8 & 0xff) ?
+ (((__u32) gfpow[mod255(gflog[t] + b)]) << 8) : 0) |
+ ((t = (__u32)a >> 0 & 0xff) ?
+ (((__u32) gfpow[mod255(gflog[t] + b)]) << 0) : 0));
} else if (sizeof(long) == 8) {
- TRACE_EXIT;
- return
- ((t = a >> 56 & 0xff) ? (((unsigned long) gfpow[mod255(gflog[t] + b)]) << 56) : 0) |
- ((t = a >> 48 & 0xff) ? (((unsigned long) gfpow[mod255(gflog[t] + b)]) << 48) : 0) |
- ((t = a >> 40 & 0xff) ? (((unsigned long) gfpow[mod255(gflog[t] + b)]) << 40) : 0) |
- ((t = a >> 32 & 0xff) ? (((unsigned long) gfpow[mod255(gflog[t] + b)]) << 32) : 0) |
- ((t = a >> 24 & 0xff) ? (((unsigned long) gfpow[mod255(gflog[t] + b)]) << 24) : 0) |
- ((t = a >> 16 & 0xff) ? (((unsigned long) gfpow[mod255(gflog[t] + b)]) << 16) : 0) |
- ((t = a >> 8 & 0xff) ? (((unsigned long) gfpow[mod255(gflog[t] + b)]) << 8) : 0) |
- ((t = a >> 0 & 0xff) ? (((unsigned long) gfpow[mod255(gflog[t] + b)]) << 0) : 0);
-#endif
+ return (
+ ((t = (__u64)a >> 56 & 0xff) ?
+ (((__u64) gfpow[mod255(gflog[t] + b)]) << 56) : 0) |
+ ((t = (__u64)a >> 48 & 0xff) ?
+ (((__u64) gfpow[mod255(gflog[t] + b)]) << 48) : 0) |
+ ((t = (__u64)a >> 40 & 0xff) ?
+ (((__u64) gfpow[mod255(gflog[t] + b)]) << 40) : 0) |
+ ((t = (__u64)a >> 32 & 0xff) ?
+ (((__u64) gfpow[mod255(gflog[t] + b)]) << 32) : 0) |
+ ((t = (__u64)a >> 24 & 0xff) ?
+ (((__u64) gfpow[mod255(gflog[t] + b)]) << 24) : 0) |
+ ((t = (__u64)a >> 16 & 0xff) ?
+ (((__u64) gfpow[mod255(gflog[t] + b)]) << 16) : 0) |
+ ((t = (__u64)a >> 8 & 0xff) ?
+ (((__u64) gfpow[mod255(gflog[t] + b)]) << 8) : 0) |
+ ((t = (__u64)a >> 0 & 0xff) ?
+ (((__u64) gfpow[mod255(gflog[t] + b)]) << 0) : 0));
} else {
- TRACEx1(1, "Error: size of long is %d bytes", (int) sizeof(long));
+ TRACE_FUN(ft_t_any);
+ TRACE_ABORT(-1, ft_t_err, "Error: size of long is %d bytes",
+ (int)sizeof(long));
}
- TRACE_EXIT;
- return -1;
}
-/*
- * Divide two numbers in the field. Returns a/b (modulo f(x)).
+/* Divide two numbers in the field. Returns a/b (modulo f(x)).
*/
-static inline unsigned char gfdiv(unsigned char a, unsigned char b)
+static inline __u8 gfdiv(__u8 a, __u8 b)
{
- TRACE_FUN(8, "gfdiv");
if (!b) {
- TRACE(-1, "Error: division by zero");
- return 0xff;
+ TRACE_FUN(ft_t_any);
+ TRACE_ABORT(0xff, ft_t_bug, "Error: division by zero");
} else if (a == 0) {
return 0;
} else {
return gfpow[mod255(gflog[a] - gflog[b])];
}
- TRACE_EXIT;
}
-/*
- * The following functions return the inverse of the matrix of the
+/* The following functions return the inverse of the matrix of the
* linear system that needs to be solved to determine the error
* magnitudes. The first deals with matrices of rank 3, while the
* second deals with matrices of rank 2. The error indices are passed
- * in arguments L0,..,L2 (0=first sector, 31=last sector). The
- * error indices must be sorted in ascending order, i.e., L0<L1<L2.
+ * in arguments L0,..,L2 (0=first sector, 31=last sector). The error
+ * indices must be sorted in ascending order, i.e., L0<L1<L2.
*
- * The linear system that needs to be solved for the error
- * magnitudes is A * b = s, where s is the known vector of
- * syndromes, b is the vector of error magnitudes and A in
- * the ORDER=3 case:
+ * The linear system that needs to be solved for the error magnitudes
+ * is A * b = s, where s is the known vector of syndromes, b is the
+ * vector of error magnitudes and A in the ORDER=3 case:
*
* A_3 = {{1/r^L[0], 1/r^L[1], 1/r^L[2]},
* { 1, 1, 1},
- * { r^L[0], r^L[1], r^L[2]}}
+ * { r^L[0], r^L[1], r^L[2]}}
*/
-static inline int gfinv3(unsigned char l0, unsigned char l1, unsigned char l2, Matrix Ainv)
+static inline int gfinv3(__u8 l0,
+ __u8 l1,
+ __u8 l2,
+ Matrix Ainv)
{
- TRACE_FUN(8, "gfinv3");
- unsigned char det;
- unsigned char t20, t10, t21, t12, t01, t02;
+ __u8 det;
+ __u8 t20, t10, t21, t12, t01, t02;
int log_det;
/* compute some intermediate results: */
- t20 = gfpow[l2 - l0]; /* t20 = r^l2/r^l0 */
- t10 = gfpow[l1 - l0]; /* t10 = r^l1/r^l0 */
- t21 = gfpow[l2 - l1]; /* t21 = r^l2/r^l1 */
+ t20 = gfpow[l2 - l0]; /* t20 = r^l2/r^l0 */
+ t10 = gfpow[l1 - l0]; /* t10 = r^l1/r^l0 */
+ t21 = gfpow[l2 - l1]; /* t21 = r^l2/r^l1 */
t12 = gfpow[l1 - l2 + 255]; /* t12 = r^l1/r^l2 */
t01 = gfpow[l0 - l1 + 255]; /* t01 = r^l0/r^l1 */
t02 = gfpow[l0 - l2 + 255]; /* t02 = r^l0/r^l2 */
- /*
- * Calculate the determinant of matrix A_3^-1 (sometimes called
- * the Vandermonde determinant):
+ /* Calculate the determinant of matrix A_3^-1 (sometimes
+ * called the Vandermonde determinant):
*/
det = gfadd(t20, gfadd(t10, gfadd(t21, gfadd(t12, gfadd(t01, t02)))));
if (!det) {
- TRACE(1, "Inversion failed (3 CRC errors, >0 CRC failures)");
- TRACE_EXIT;
- return 0;
+ TRACE_FUN(ft_t_any);
+ TRACE_ABORT(0, ft_t_err,
+ "Inversion failed (3 CRC errors, >0 CRC failures)");
}
log_det = 255 - gflog[det];
- /*
- * Now, calculate all of the coefficients:
+ /* Now, calculate all of the coefficients:
*/
- Ainv[0][0] = gfmul_exp(gfadd(gfpow[l1], gfpow[l2]), log_det);
- Ainv[0][1] = gfmul_exp(gfadd(t21, t12), log_det);
- Ainv[0][2] = gfmul_exp(gfadd(gfpow[255 - l1], gfpow[255 - l2]), log_det);
+ Ainv[0][0]= gfmul_exp(gfadd(gfpow[l1], gfpow[l2]), log_det);
+ Ainv[0][1]= gfmul_exp(gfadd(t21, t12), log_det);
+ Ainv[0][2]= gfmul_exp(gfadd(gfpow[255 - l1], gfpow[255 - l2]),log_det);
- Ainv[1][0] = gfmul_exp(gfadd(gfpow[l0], gfpow[l2]), log_det);
- Ainv[1][1] = gfmul_exp(gfadd(t20, t02), log_det);
- Ainv[1][2] = gfmul_exp(gfadd(gfpow[255 - l0], gfpow[255 - l2]), log_det);
+ Ainv[1][0]= gfmul_exp(gfadd(gfpow[l0], gfpow[l2]), log_det);
+ Ainv[1][1]= gfmul_exp(gfadd(t20, t02), log_det);
+ Ainv[1][2]= gfmul_exp(gfadd(gfpow[255 - l0], gfpow[255 - l2]),log_det);
- Ainv[2][0] = gfmul_exp(gfadd(gfpow[l0], gfpow[l1]), log_det);
- Ainv[2][1] = gfmul_exp(gfadd(t10, t01), log_det);
- Ainv[2][2] = gfmul_exp(gfadd(gfpow[255 - l0], gfpow[255 - l1]), log_det);
+ Ainv[2][0]= gfmul_exp(gfadd(gfpow[l0], gfpow[l1]), log_det);
+ Ainv[2][1]= gfmul_exp(gfadd(t10, t01), log_det);
+ Ainv[2][2]= gfmul_exp(gfadd(gfpow[255 - l0], gfpow[255 - l1]),log_det);
- TRACE_EXIT;
return 1;
}
-static inline int gfinv2(unsigned char l0, unsigned char l1, Matrix Ainv)
+static inline int gfinv2(__u8 l0, __u8 l1, Matrix Ainv)
{
- TRACE_FUN(8, "gfinv2");
- unsigned char det;
- unsigned char t1, t2;
+ __u8 det;
+ __u8 t1, t2;
int log_det;
t1 = gfpow[255 - l0];
t2 = gfpow[255 - l1];
det = gfadd(t1, t2);
if (!det) {
- TRACE(1, "Inversion failed (2 CRC errors, >0 CRC failures)");
- TRACE_EXIT;
- return 0;
+ TRACE_FUN(ft_t_any);
+ TRACE_ABORT(0, ft_t_err,
+ "Inversion failed (2 CRC errors, >0 CRC failures)");
}
log_det = 255 - gflog[det];
- /*
- * Now, calculate all of the coefficients:
+ /* Now, calculate all of the coefficients:
*/
Ainv[0][0] = Ainv[1][0] = gfpow[log_det];
Ainv[0][1] = gfmul_exp(t2, log_det);
Ainv[1][1] = gfmul_exp(t1, log_det);
- TRACE_EXIT;
return 1;
}
-/*
- * Multiply matrix A by vector S and return result in vector B.
- * M is assumed to be of order NxN, S and B of order Nx1.
+/* Multiply matrix A by vector S and return result in vector B. M is
+ * assumed to be of order NxN, S and B of order Nx1.
*/
-static inline void gfmat_mul(int n, Matrix A, unsigned char *s, unsigned char *b)
+static inline void gfmat_mul(int n, Matrix A,
+ __u8 *s, __u8 *b)
{
int i, j;
- unsigned char dot_prod;
+ __u8 dot_prod;
for (i = 0; i < n; ++i) {
dot_prod = 0;
@@ -462,20 +437,18 @@ static inline void gfmat_mul(int n, Matrix A, unsigned char *s, unsigned char *b
-/*
- * The Reed Solomon ECC codes are computed over the N-th byte of each
+/* The Reed Solomon ECC codes are computed over the N-th byte of each
* block, where N=SECTOR_SIZE. There are up to 29 blocks of data, and
- * 3 blocks of ECC. The blocks are stored contiguously in memory.
- * A segment, consequently, is assumed to have at least 4 blocks:
- * one or more data blocks plus three ECC blocks.
+ * 3 blocks of ECC. The blocks are stored contiguously in memory. A
+ * segment, consequently, is assumed to have at least 4 blocks: one or
+ * more data blocks plus three ECC blocks.
*
* Notice: In QIC-80 speak, a CRC error is a sector with an incorrect
* CRC. A CRC failure is a sector with incorrect data, but
* a valid CRC. In the error control literature, the former
* is usually called "erasure", the latter "error."
*/
-/*
- * Compute the parity bytes for C columns of data, where C is the
+/* Compute the parity bytes for C columns of data, where C is the
* number of bytes that fit into a long integer. We use a linear
* feed-back register to do this. The parity bytes P[0], P[STRIDE],
* P[2*STRIDE] are computed such that:
@@ -485,19 +458,21 @@ static inline void gfmat_mul(int n, Matrix A, unsigned char *s, unsigned char *b
* where k = NBLOCKS,
* p(x) = P[0] + P[STRIDE]*x + P[2*STRIDE]*x^2, and
* m(x) = sum_{i=0}^k m_i*x^i.
- * m_i = DATA[i*SECTOR_SIZE]
+ * m_i = DATA[i*SECTOR_SIZE]
*/
-static inline void set_parity(unsigned long *data, int nblocks, unsigned long *p, int stride)
+static inline void set_parity(unsigned long *data,
+ int nblocks,
+ unsigned long *p,
+ int stride)
{
- TRACE_FUN(8, "set_parity");
unsigned long p0, p1, p2, t1, t2, *end;
- end = data + nblocks * (SECTOR_SIZE / sizeof(long));
+ end = data + nblocks * (FT_SECTOR_SIZE / sizeof(long));
p0 = p1 = p2 = 0;
while (data < end) {
- /*
- * The new parity bytes p0_i, p1_i, p2_i are computed from the old
- * values p0_{i-1}, p1_{i-1}, p2_{i-1} recursively as:
+ /* The new parity bytes p0_i, p1_i, p2_i are computed
+ * from the old values p0_{i-1}, p1_{i-1}, p2_{i-1}
+ * recursively as:
*
* p0_i = p1_{i-1} + r^105 * (m_{i-1} - p0_{i-1})
* p1_i = p2_{i-1} + r^105 * (m_{i-1} - p0_{i-1})
@@ -510,43 +485,43 @@ static inline void set_parity(unsigned long *data, int nblocks, unsigned long *p
* Multiply each byte in t1 by 0xc0:
*/
if (sizeof(long) == 4) {
- t2 = ((unsigned long) gfmul_c0[t1 >> 24 & 0xff]) << 24 |
- ((unsigned long) gfmul_c0[t1 >> 16 & 0xff]) << 16 |
- ((unsigned long) gfmul_c0[t1 >> 8 & 0xff]) << 8 |
- ((unsigned long) gfmul_c0[t1 >> 0 & 0xff]) << 0;
-#if !defined(linux)
+ t2= (((__u32) gfmul_c0[(__u32)t1 >> 24 & 0xff]) << 24 |
+ ((__u32) gfmul_c0[(__u32)t1 >> 16 & 0xff]) << 16 |
+ ((__u32) gfmul_c0[(__u32)t1 >> 8 & 0xff]) << 8 |
+ ((__u32) gfmul_c0[(__u32)t1 >> 0 & 0xff]) << 0);
} else if (sizeof(long) == 8) {
- t2 = ((unsigned long) gfmul_c0[t1 >> 56 & 0xff]) << 56 |
- ((unsigned long) gfmul_c0[t1 >> 48 & 0xff]) << 48 |
- ((unsigned long) gfmul_c0[t1 >> 40 & 0xff]) << 40 |
- ((unsigned long) gfmul_c0[t1 >> 32 & 0xff]) << 32 |
- ((unsigned long) gfmul_c0[t1 >> 24 & 0xff]) << 24 |
- ((unsigned long) gfmul_c0[t1 >> 16 & 0xff]) << 16 |
- ((unsigned long) gfmul_c0[t1 >> 8 & 0xff]) << 8 |
- ((unsigned long) gfmul_c0[t1 >> 0 & 0xff]) << 0;
-#endif
+ t2= (((__u64) gfmul_c0[(__u64)t1 >> 56 & 0xff]) << 56 |
+ ((__u64) gfmul_c0[(__u64)t1 >> 48 & 0xff]) << 48 |
+ ((__u64) gfmul_c0[(__u64)t1 >> 40 & 0xff]) << 40 |
+ ((__u64) gfmul_c0[(__u64)t1 >> 32 & 0xff]) << 32 |
+ ((__u64) gfmul_c0[(__u64)t1 >> 24 & 0xff]) << 24 |
+ ((__u64) gfmul_c0[(__u64)t1 >> 16 & 0xff]) << 16 |
+ ((__u64) gfmul_c0[(__u64)t1 >> 8 & 0xff]) << 8 |
+ ((__u64) gfmul_c0[(__u64)t1 >> 0 & 0xff]) << 0);
} else {
- TRACEx1(1, "Error: long is of size %d", (int) sizeof(long));
+ TRACE_FUN(ft_t_any);
+ TRACE(ft_t_err, "Error: long is of size %d",
+ (int) sizeof(long));
+ TRACE_EXIT;
}
p0 = gfadd_long(t2, p1);
p1 = gfadd_long(t2, p2);
p2 = t1;
- data += SECTOR_SIZE / sizeof(long);
+ data += FT_SECTOR_SIZE / sizeof(long);
}
*p = p0;
p += stride;
*p = p1;
p += stride;
*p = p2;
- TRACE_EXIT;
+ return;
}
-/*
- * Compute the 3 syndrome values. DATA should point to the first byte
+/* Compute the 3 syndrome values. DATA should point to the first byte
* of the column for which the syndromes are desired. The syndromes
- * are computed over the first NBLOCKS of rows. The three bytes will be
- * placed in S[0], S[1], and S[2].
+ * are computed over the first NBLOCKS of rows. The three bytes will
+ * be placed in S[0], S[1], and S[2].
*
* S[i] is the value of the "message" polynomial m(x) evaluated at the
* i-th root of the generator polynomial g(x).
@@ -561,8 +536,8 @@ static inline void set_parity(unsigned long *data, int nblocks, unsigned long *p
* slower if there are errors. The latter is hopefully the infrequent
* case.
*
- * To understand the alternative algorithm, notice that
- * set_parity(m, k, p) computes parity bytes such that:
+ * To understand the alternative algorithm, notice that set_parity(m,
+ * k, p) computes parity bytes such that:
*
* x^k * p(x) = m(x) (modulo g(x)).
*
@@ -580,16 +555,26 @@ static int compute_syndromes(unsigned long *data, int nblocks, unsigned long *s)
set_parity(data, nblocks, p, 1);
if (p[0] | p[1] | p[2]) {
- /*
- * Some of the checked columns do not have a zero syndrome. For
- * simplicity, we compute the syndromes for all columns that we
- * have computed the remainders for.
+ /* Some of the checked columns do not have a zero
+ * syndrome. For simplicity, we compute the syndromes
+ * for all columns that we have computed the
+ * remainders for.
*/
- s[0] = gfmul_exp_long(gfadd_long(p[0], gfmul_exp_long(gfadd_long(p[1],
- gfmul_exp_long(p[2], -1)), -1)), -nblocks);
+ s[0] = gfmul_exp_long(
+ gfadd_long(p[0],
+ gfmul_exp_long(
+ gfadd_long(p[1],
+ gfmul_exp_long(p[2], -1)),
+ -1)),
+ -nblocks);
s[1] = gfadd_long(gfadd_long(p[2], p[1]), p[0]);
- s[2] = gfmul_exp_long(gfadd_long(p[0], gfmul_exp_long(gfadd_long(p[1],
- gfmul_exp_long(p[2], 1)), 1)), nblocks);
+ s[2] = gfmul_exp_long(
+ gfadd_long(p[0],
+ gfmul_exp_long(
+ gfadd_long(p[1],
+ gfmul_exp_long(p[2], 1)),
+ 1)),
+ nblocks);
return 0;
} else {
return 1;
@@ -597,42 +582,40 @@ static int compute_syndromes(unsigned long *data, int nblocks, unsigned long *s)
}
-/*
- * Correct the block in the column pointed to by DATA. There are NBAD
+/* Correct the block in the column pointed to by DATA. There are NBAD
* CRC errors and their indices are in BAD_LOC[0], up to
* BAD_LOC[NBAD-1]. If NBAD>1, Ainv holds the inverse of the matrix
* of the linear system that needs to be solved to determine the error
* magnitudes. S[0], S[1], and S[2] are the syndrome values. If row
* j gets corrected, then bit j will be set in CORRECTION_MAP.
*/
-static inline int correct_block(unsigned char *data, int nblocks,
+static inline int correct_block(__u8 *data, int nblocks,
int nbad, int *bad_loc, Matrix Ainv,
- unsigned char *s,
- BAD_SECTOR * correction_map)
+ __u8 *s,
+ SectorMap * correction_map)
{
- TRACE_FUN(8, "correct_block");
int ncorrected = 0;
int i;
- unsigned char t1, t2;
- unsigned char c0, c1, c2; /* check bytes */
- unsigned char error_mag[3], log_error_mag;
- unsigned char *dp, l, e;
+ __u8 t1, t2;
+ __u8 c0, c1, c2; /* check bytes */
+ __u8 error_mag[3], log_error_mag;
+ __u8 *dp, l, e;
+ TRACE_FUN(ft_t_any);
switch (nbad) {
case 0:
/* might have a CRC failure: */
if (s[0] == 0) {
/* more than one error */
- TRACE(1, "ECC failed (0 CRC errors, >1 CRC failures)");
- TRACE_EXIT;
- return -1;
- } /* if */
+ TRACE_ABORT(-1, ft_t_err,
+ "ECC failed (0 CRC errors, >1 CRC failures)");
+ }
t1 = gfdiv(s[1], s[0]);
if ((bad_loc[nbad++] = gflog[t1]) >= nblocks) {
- TRACE(1, "ECC failed (0 CRC errors, >1 CRC failures): ");
- TRACEi(1, "attempt to correct data at ", bad_loc[0]);
- TRACE_EXIT;
- return -1;
+ TRACE(ft_t_err,
+ "ECC failed (0 CRC errors, >1 CRC failures)");
+ TRACE_ABORT(-1, ft_t_err,
+ "attempt to correct data at %d", bad_loc[0]);
}
error_mag[0] = s[1];
break;
@@ -644,25 +627,26 @@ static inline int correct_block(unsigned char *data, int nblocks,
Ainv[0][0] = gfpow[bad_loc[0]];
} else if (t1 == 0 || t2 == 0) {
/* one erasure and more than one error: */
- TRACE(1, "ECC failed (1 erasure, >1 error)");
- TRACE_EXIT;
- return -1;
+ TRACE_ABORT(-1, ft_t_err,
+ "ECC failed (1 erasure, >1 error)");
} else {
/* one erasure, one error: */
- if ((bad_loc[nbad++] = gflog[gfdiv(t1, t2)]) >= nblocks) {
- TRACE(1, "ECC failed (1 CRC errors, >1 CRC failures): ");
- TRACEi(1, "attempt to correct data at ", bad_loc[1]);
- TRACE_EXIT;
- return -1;
- } /* if */
+ if ((bad_loc[nbad++] = gflog[gfdiv(t1, t2)])
+ >= nblocks) {
+ TRACE(ft_t_err, "ECC failed "
+ "(1 CRC errors, >1 CRC failures)");
+ TRACE_ABORT(-1, ft_t_err,
+ "attempt to correct data at %d",
+ bad_loc[1]);
+ }
if (!gfinv2(bad_loc[0], bad_loc[1], Ainv)) {
- /* inversion failed---must have more than one error */
- TRACE_EXIT;
- return -1;
+ /* inversion failed---must have more
+ * than one error
+ */
+ TRACE_EXIT -1;
}
}
- /*
- * FALL THROUGH TO ERROR MAGNITUDE COMPUTATION:
+ /* FALL THROUGH TO ERROR MAGNITUDE COMPUTATION:
*/
case 2:
case 3:
@@ -671,17 +655,15 @@ static inline int correct_block(unsigned char *data, int nblocks,
break;
default:
- TRACE(1, "Internal Error: number of CRC errors > 3");
- TRACE_EXIT;
- return -1;
+ TRACE_ABORT(-1, ft_t_err,
+ "Internal Error: number of CRC errors > 3");
}
- /*
- * Perform correction by adding ERROR_MAG[i] to the byte at offset
- * BAD_LOC[i]. Also add the value of the computed error polynomial
- * to the syndrome values. If the correction was successful, the
- * resulting check bytes should be zero (i.e., the corrected data
- * is a valid code word).
+ /* Perform correction by adding ERROR_MAG[i] to the byte at
+ * offset BAD_LOC[i]. Also add the value of the computed
+ * error polynomial to the syndrome values. If the correction
+ * was successful, the resulting check bytes should be zero
+ * (i.e., the corrected data is a valid code word).
*/
c0 = s[0];
c1 = s[1];
@@ -691,7 +673,7 @@ static inline int correct_block(unsigned char *data, int nblocks,
if (e) {
/* correct the byte at offset L by magnitude E: */
l = bad_loc[i];
- dp = &data[l * SECTOR_SIZE];
+ dp = &data[l * FT_SECTOR_SIZE];
*dp = gfadd(*dp, e);
*correction_map |= 1 << l;
++ncorrected;
@@ -703,53 +685,46 @@ static inline int correct_block(unsigned char *data, int nblocks,
}
}
if (c0 || c1 || c2) {
- TRACE(1, "ECC self-check failed, too many errors");
- TRACE_EXIT;
- return -1;
+ TRACE_ABORT(-1, ft_t_err,
+ "ECC self-check failed, too many errors");
}
- TRACE_EXIT;
- return ncorrected;
+ TRACE_EXIT ncorrected;
}
#if defined(ECC_SANITY_CHECK) || defined(ECC_PARANOID)
-/*
- * Perform a sanity check on the computed parity bytes:
+/* Perform a sanity check on the computed parity bytes:
*/
static int sanity_check(unsigned long *data, int nblocks)
{
- TRACE_FUN(8, "sanity_check");
+ TRACE_FUN(ft_t_any);
unsigned long s[3];
if (!compute_syndromes(data, nblocks, s)) {
- TRACE(-1, "Internal Error: syndrome self-check failed");
- TRACE_EXIT;
- return 0;
+ TRACE_ABORT(0, ft_bug,
+ "Internal Error: syndrome self-check failed");
}
- TRACE_EXIT;
- return 1;
+ TRACE_EXIT 1;
}
-#endif /* defined(ECC_SANITY_CHECK) || defined(ECC_PARANOID) */
-
-
+#endif /* defined(ECC_SANITY_CHECK) || defined(ECC_PARANOID) */
-/*
- * Compute the parity for an entire segment of data.
+/* Compute the parity for an entire segment of data.
*/
-int ecc_set_segment_parity(struct memory_segment *mseg)
+int ftape_ecc_set_segment_parity(struct memory_segment *mseg)
{
int i;
- unsigned char *parity_bytes;
+ __u8 *parity_bytes;
- parity_bytes = &mseg->data[(mseg->blocks - 3) * SECTOR_SIZE];
- for (i = 0; i < SECTOR_SIZE; i += sizeof(long)) {
+ parity_bytes = &mseg->data[(mseg->blocks - 3) * FT_SECTOR_SIZE];
+ for (i = 0; i < FT_SECTOR_SIZE; i += sizeof(long)) {
set_parity((unsigned long *) &mseg->data[i], mseg->blocks - 3,
(unsigned long *) &parity_bytes[i],
- SECTOR_SIZE / sizeof(long));
+ FT_SECTOR_SIZE / sizeof(long));
#ifdef ECC_PARANOID
- if (!sanity_check((unsigned long *) &mseg->data[i], mseg->blocks)) {
+ if (!sanity_check((unsigned long *) &mseg->data[i],
+ mseg->blocks)) {
return -1;
}
#endif /* ECC_PARANOID */
@@ -758,35 +733,33 @@ int ecc_set_segment_parity(struct memory_segment *mseg)
}
-/*
- * Checks and corrects (if possible) the segment MSEG. Returns one of
+/* Checks and corrects (if possible) the segment MSEG. Returns one of
* ECC_OK, ECC_CORRECTED, and ECC_FAILED.
*/
-int ecc_correct_data(struct memory_segment *mseg)
+int ftape_ecc_correct_data(struct memory_segment *mseg)
{
- TRACE_FUN(5, "ecc_correct_data");
int col, i, result;
int ncorrected = 0;
int nerasures = 0; /* # of erasures (CRC errors) */
int erasure_loc[3]; /* erasure locations */
unsigned long ss[3];
- unsigned char s[3];
+ __u8 s[3];
Matrix Ainv;
+ TRACE_FUN(ft_t_flow);
mseg->corrected = 0;
/* find first column that has non-zero syndromes: */
- for (col = 0; col < SECTOR_SIZE; col += sizeof(long)) {
+ for (col = 0; col < FT_SECTOR_SIZE; col += sizeof(long)) {
if (!compute_syndromes((unsigned long *) &mseg->data[col],
mseg->blocks, ss)) {
/* something is wrong---have to fix things */
break;
}
}
- if (col >= SECTOR_SIZE) {
+ if (col >= FT_SECTOR_SIZE) {
/* all syndromes are ok, therefore nothing to correct */
- TRACE_EXIT;
- return ECC_OK;
+ TRACE_EXIT ECC_OK;
}
/* count the number of CRC errors if there were any: */
if (mseg->read_bad) {
@@ -794,29 +767,27 @@ int ecc_correct_data(struct memory_segment *mseg)
if (BAD_CHECK(mseg->read_bad, i)) {
if (nerasures >= 3) {
/* this is too much for ECC */
- TRACE(1, "ECC failed (>3 CRC errors)");
- TRACE_EXIT;
- return ECC_FAILED;
+ TRACE_ABORT(ECC_FAILED, ft_t_err,
+ "ECC failed (>3 CRC errors)");
} /* if */
erasure_loc[nerasures++] = i;
}
}
}
/*
- * If there are at least 2 CRC errors, determine inverse of matrix
- * of linear system to be solved:
+ * If there are at least 2 CRC errors, determine inverse of matrix
+ * of linear system to be solved:
*/
switch (nerasures) {
case 2:
if (!gfinv2(erasure_loc[0], erasure_loc[1], Ainv)) {
- TRACE_EXIT;
- return ECC_FAILED;
+ TRACE_EXIT ECC_FAILED;
}
break;
case 3:
- if (!gfinv3(erasure_loc[0], erasure_loc[1], erasure_loc[2], Ainv)) {
- TRACE_EXIT;
- return ECC_FAILED;
+ if (!gfinv3(erasure_loc[0], erasure_loc[1],
+ erasure_loc[2], Ainv)) {
+ TRACE_EXIT ECC_FAILED;
}
break;
default:
@@ -830,21 +801,26 @@ int ecc_correct_data(struct memory_segment *mseg)
s[1] = ss[1];
s[2] = ss[2];
if (s[0] | s[1] | s[2]) {
-#ifdef __BIG_ENDIAN
- result = correct_block(&mseg->data[col + sizeof(long) - 1 - i],
+#ifdef BIG_ENDIAN
+ result = correct_block(
+ &mseg->data[col + sizeof(long) - 1 - i],
+ mseg->blocks,
+ nerasures,
+ erasure_loc,
+ Ainv,
+ s,
+ &mseg->corrected);
+#else
+ result = correct_block(&mseg->data[col + i],
mseg->blocks,
- nerasures, erasure_loc, Ainv, s,
- &mseg->corrected);
-#elif defined(__LITTLE_ENDIAN)
- result = correct_block(&mseg->data[col + i], mseg->blocks,
- nerasures, erasure_loc, Ainv, s,
+ nerasures,
+ erasure_loc,
+ Ainv,
+ s,
&mseg->corrected);
-#else
-#error "Huh? Neither __BIG_ENDIAN nor __LITTLE_ENDIAN?"
#endif
if (result < 0) {
- TRACE_EXIT;
- return ECC_FAILED;
+ TRACE_EXIT ECC_FAILED;
}
ncorrected += result;
}
@@ -854,27 +830,24 @@ int ecc_correct_data(struct memory_segment *mseg)
}
#ifdef ECC_SANITY_CHECK
- if (!sanity_check((unsigned long *) &mseg->data[col], mseg->blocks)) {
- TRACE_EXIT;
- return ECC_FAILED;
+ if (!sanity_check((unsigned long *) &mseg->data[col],
+ mseg->blocks)) {
+ TRACE_EXIT ECC_FAILED;
}
#endif /* ECC_SANITY_CHECK */
/* find next column with non-zero syndromes: */
- while ((col += sizeof(long)) < SECTOR_SIZE) {
- if (!compute_syndromes((unsigned long *) &mseg->data[col],
- mseg->blocks, ss)) {
+ while ((col += sizeof(long)) < FT_SECTOR_SIZE) {
+ if (!compute_syndromes((unsigned long *)
+ &mseg->data[col], mseg->blocks, ss)) {
/* something is wrong---have to fix things */
break;
}
}
- } while (col < SECTOR_SIZE);
+ } while (col < FT_SECTOR_SIZE);
if (ncorrected && nerasures == 0) {
- TRACE(2, "block contained error not caught by CRC");
+ TRACE(ft_t_warn, "block contained error not caught by CRC");
}
- TRACEi((ncorrected > 0) ? 4 : 8, "number of corrections:", ncorrected);
- TRACE_EXIT;
- return ncorrected ? ECC_CORRECTED : ECC_OK;
+ TRACE((ncorrected > 0) ? ft_t_noise : ft_t_any, "number of corrections: %d", ncorrected);
+ TRACE_EXIT ncorrected ? ECC_CORRECTED : ECC_OK;
}
-
-/*** end of ecc.c ***/
diff --git a/drivers/char/ftape/ecc.h b/drivers/char/ftape/lowlevel/ftape-ecc.h
index 890543ceb..4829146fe 100644
--- a/drivers/char/ftape/ecc.h
+++ b/drivers/char/ftape/lowlevel/ftape-ecc.h
@@ -1,40 +1,39 @@
+#ifndef _FTAPE_ECC_H_
+#define _FTAPE_ECC_H_
+
/*
* Copyright (C) 1993 Ning and David Mosberger.
* Original:
* Copyright (C) 1993 Bas Laarhoven.
* Copyright (C) 1992 David L. Brown, Jr.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139,
+ USA.
+
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2, or (at
- * your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139,
- * USA.
- *
- *
- * $Source: /home/bas/distr/ftape-2.03b/RCS/ecc.h,v $
- * $Author: bas $
- *
- * $Revision: 1.20 $
- * $Date: 1995/01/08 14:16:21 $
- * $State: Beta $
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-ecc.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:11 $
*
* This file contains the definitions for the
* Reed-Solomon error correction code
* for the QIC-40/80 tape streamer device driver.
*/
-#ifndef _ecc_h_
-#define _ecc_h_
-typedef unsigned long BAD_SECTOR;
+#include "../lowlevel/ftape-bsm.h"
+
#define BAD_CLEAR(entry) ((entry)=0)
#define BAD_SET(entry,sector) ((entry)|=(1<<(sector)))
#define BAD_CHECK(entry,sector) ((entry)&(1<<(sector)))
@@ -61,13 +60,13 @@ enum {
* is needed. DATA is the actual sector packed data from (or to) the
* tape.
*/
-struct memory_segment {
- BAD_SECTOR marked_bad;
- BAD_SECTOR read_bad;
- int blocks;
- unsigned char *data;
- BAD_SECTOR corrected;
-};
+ struct memory_segment {
+ SectorMap marked_bad;
+ SectorMap read_bad;
+ int blocks;
+ __u8 *data;
+ SectorMap corrected;
+ };
/*
* ecc.c defined global variables:
@@ -79,7 +78,7 @@ extern int ftape_ecc_tracing;
/*
* ecc.c defined global functions:
*/
-extern int ecc_correct_data(struct memory_segment *data);
-extern int ecc_set_segment_parity(struct memory_segment *data);
+extern int ftape_ecc_correct_data(struct memory_segment *data);
+extern int ftape_ecc_set_segment_parity(struct memory_segment *data);
-#endif /* _ecc_h_ */
+#endif /* _FTAPE_ECC_H_ */
diff --git a/drivers/char/ftape/lowlevel/ftape-format.c b/drivers/char/ftape/lowlevel/ftape-format.c
new file mode 100644
index 000000000..015911918
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/ftape-format.c
@@ -0,0 +1,342 @@
+/*
+ * Copyright (C) 1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-format.c,v $
+ * $Revision: 1.2.4.1 $
+ * $Date: 1997/11/14 16:05:39 $
+ *
+ * This file contains the code to support formatting of floppy
+ * tape cartridges with the QIC-40/80/3010/3020 floppy-tape
+ * driver "ftape" for Linux.
+ */
+
+#include <linux/string.h>
+#include <linux/errno.h>
+
+#include <linux/ftape.h>
+#include <linux/qic117.h>
+#include "../lowlevel/ftape-tracing.h"
+#include "../lowlevel/ftape-io.h"
+#include "../lowlevel/ftape-ctl.h"
+#include "../lowlevel/ftape-rw.h"
+#include "../lowlevel/ftape-ecc.h"
+#include "../lowlevel/ftape-bsm.h"
+#include "../lowlevel/ftape-format.h"
+
+#if defined(TESTING)
+#define FT_FMT_SEGS_PER_BUF 50
+#else
+#define FT_FMT_SEGS_PER_BUF (FT_BUFF_SIZE/(4*FT_SECTORS_PER_SEGMENT))
+#endif
+
+/*
+ * first segment of the new buffer
+ */
+static int switch_segment = 0;
+
+/*
+ * at most 256 segments fit into one 32 kb buffer. Even TR-1 cartridges have
+ * more than this many segments per track, so better be careful.
+ *
+ * buffer_struct *buff: buffer to store the formatting coordinates in
+ * int start: starting segment for this buffer.
+ * int spt: segments per track
+ *
+ * Note: segment ids are relative to the start of the track here.
+ */
+static void setup_format_buffer(buffer_struct *buff, int start, int spt,
+ __u8 gap3)
+{
+ int to_do = spt - start;
+ TRACE_FUN(ft_t_flow);
+
+ if (to_do > FT_FMT_SEGS_PER_BUF) {
+ to_do = FT_FMT_SEGS_PER_BUF;
+ }
+ buff->ptr = buff->address;
+ buff->remaining = to_do * FT_SECTORS_PER_SEGMENT; /* # sectors */
+ buff->bytes = buff->remaining * 4; /* need 4 bytes per sector */
+ buff->gap3 = gap3;
+ buff->segment_id = start;
+ buff->next_segment = start + to_do;
+ if (buff->next_segment >= spt) {
+ buff->next_segment = 0; /* 0 means: stop runner */
+ }
+ buff->status = waiting; /* tells the isr that it can use
+ * this buffer
+ */
+ TRACE_EXIT;
+}
+
+
+/*
+ * start formatting a new track.
+ */
+int ftape_format_track(const unsigned int track, const __u8 gap3)
+{
+ unsigned long flags;
+ buffer_struct *tail, *head;
+ int status;
+ TRACE_FUN(ft_t_flow);
+
+ TRACE_CATCH(ftape_ready_wait(ftape_timeout.pause, &status),);
+ if (track & 1) {
+ if (!(status & QIC_STATUS_AT_EOT)) {
+ TRACE_CATCH(ftape_seek_to_eot(),);
+ }
+ } else {
+ if (!(status & QIC_STATUS_AT_BOT)) {
+ TRACE_CATCH(ftape_seek_to_bot(),);
+ }
+ }
+ ftape_abort_operation(); /* this sets ft_head = ft_tail = 0 */
+ ftape_set_state(formatting);
+
+ TRACE(ft_t_noise,
+ "Formatting track %d, logical: from segment %d to %d",
+ track, track * ft_segments_per_track,
+ (track + 1) * ft_segments_per_track - 1);
+
+ /*
+ * initialize the buffer switching protocol for this track
+ */
+ head = ftape_get_buffer(ft_queue_head); /* tape isn't running yet */
+ tail = ftape_get_buffer(ft_queue_tail); /* tape isn't running yet */
+ switch_segment = 0;
+ do {
+ FT_SIGNAL_EXIT(_DONT_BLOCK);
+ setup_format_buffer(tail, switch_segment,
+ ft_segments_per_track, gap3);
+ switch_segment = tail->next_segment;
+ } while ((switch_segment != 0) &&
+ ((tail = ftape_next_buffer(ft_queue_tail)) != head));
+ /* go */
+ head->status = formatting;
+ TRACE_CATCH(ftape_seek_head_to_track(track),);
+ TRACE_CATCH(ftape_command(QIC_LOGICAL_FORWARD),);
+ save_flags(flags); cli();
+ TRACE_CATCH(fdc_setup_formatting(head), restore_flags(flags));
+ restore_flags(flags);
+ TRACE_EXIT 0;
+}
+
+/* return segment id of segment currently being formatted and do the
+ * buffer switching stuff.
+ */
+int ftape_format_status(unsigned int *segment_id)
+{
+ buffer_struct *tail = ftape_get_buffer(ft_queue_tail);
+ int result;
+ TRACE_FUN(ft_t_flow);
+
+ while (switch_segment != 0 &&
+ ftape_get_buffer(ft_queue_head) != tail) {
+ FT_SIGNAL_EXIT(_DONT_BLOCK);
+ /* need more buffers, first wait for empty buffer
+ */
+ TRACE_CATCH(ftape_wait_segment(formatting),);
+ /* don't worry for gap3. If we ever hit this piece of code,
+ * then all buffer already have the correct gap3 set!
+ */
+ setup_format_buffer(tail, switch_segment,
+ ft_segments_per_track, tail->gap3);
+ switch_segment = tail->next_segment;
+ if (switch_segment != 0) {
+ tail = ftape_next_buffer(ft_queue_tail);
+ }
+ }
+ /* should runner stop ?
+ */
+ if (ft_runner_status == aborting || ft_runner_status == do_abort) {
+ buffer_struct *head = ftape_get_buffer(ft_queue_head);
+ TRACE(ft_t_warn, "Error formatting segment %d",
+ ftape_get_buffer(ft_queue_head)->segment_id);
+ (void)ftape_abort_operation();
+ TRACE_EXIT (head->status != error) ? -EAGAIN : -EIO;
+ }
+ /*
+ * don't care if the timer expires, this is just kind of a
+ * "select" operation that lets the calling process sleep
+ * until something has happened
+ */
+ if (fdc_interrupt_wait(5 * FT_SECOND) < 0) {
+ TRACE(ft_t_noise, "End of track %d at segment %d",
+ ft_location.track,
+ ftape_get_buffer(ft_queue_head)->segment_id);
+ result = 1; /* end of track, unlock module */
+ } else {
+ result = 0;
+ }
+ /*
+ * the calling process should use the seg id to determine
+ * which parts of the dma buffers can be safely overwritten
+ * with new data.
+ */
+ *segment_id = ftape_get_buffer(ft_queue_head)->segment_id;
+ /*
+ * Internally we start counting segment ids from the start of
+ * each track when formatting, but externally we keep them
+ * relative to the start of the tape:
+ */
+ *segment_id += ft_location.track * ft_segments_per_track;
+ TRACE_EXIT result;
+}
+
+/*
+ * The segment id is relative to the start of the tape
+ */
+int ftape_verify_segment(const unsigned int segment_id, SectorMap *bsm)
+{
+ int result;
+ int verify_done = 0;
+ TRACE_FUN(ft_t_flow);
+
+ TRACE(ft_t_noise, "Verifying segment %d", segment_id);
+
+ if (ft_driver_state != verifying) {
+ TRACE(ft_t_noise, "calling ftape_abort_operation");
+ if (ftape_abort_operation() < 0) {
+ TRACE(ft_t_err, "ftape_abort_operation failed");
+ TRACE_EXIT -EIO;
+ }
+ }
+ *bsm = 0x00000000;
+ ftape_set_state(verifying);
+ for (;;) {
+ buffer_struct *tail;
+ /*
+ * Allow escape from this loop on signal
+ */
+ FT_SIGNAL_EXIT(_DONT_BLOCK);
+ /*
+ * Search all full buffers for the first matching the
+ * wanted segment. Clear other buffers on the fly.
+ */
+ tail = ftape_get_buffer(ft_queue_tail);
+ while (!verify_done && tail->status == done) {
+ /*
+ * Allow escape from this loop on signal !
+ */
+ FT_SIGNAL_EXIT(_DONT_BLOCK);
+ if (tail->segment_id == segment_id) {
+ /* If out buffer is already full,
+ * return its contents.
+ */
+ TRACE(ft_t_flow, "found segment in cache: %d",
+ segment_id);
+ if ((tail->soft_error_map |
+ tail->hard_error_map) != 0) {
+ TRACE(ft_t_info,"bsm[%d] = 0x%08lx",
+ segment_id,
+ (unsigned long)
+ (tail->soft_error_map |
+ tail->hard_error_map));
+ *bsm = (tail->soft_error_map |
+ tail->hard_error_map);
+ }
+ verify_done = 1;
+ } else {
+ TRACE(ft_t_flow,"zapping segment in cache: %d",
+ tail->segment_id);
+ }
+ tail->status = waiting;
+ tail = ftape_next_buffer(ft_queue_tail);
+ }
+ if (!verify_done && tail->status == verifying) {
+ if (tail->segment_id == segment_id) {
+ switch(ftape_wait_segment(verifying)) {
+ case 0:
+ break;
+ case -EINTR:
+ TRACE_ABORT(-EINTR, ft_t_warn,
+ "interrupted by "
+ "non-blockable signal");
+ break;
+ default:
+ ftape_abort_operation();
+ ftape_set_state(verifying);
+ /* be picky */
+ TRACE_ABORT(-EIO, ft_t_warn,
+ "wait_segment failed");
+ }
+ } else {
+ /* We're reading the wrong segment,
+ * stop runner.
+ */
+ TRACE(ft_t_noise, "verifying wrong segment");
+ ftape_abort_operation();
+ ftape_set_state(verifying);
+ }
+ }
+ /* should runner stop ?
+ */
+ if (ft_runner_status == aborting) {
+ buffer_struct *head = ftape_get_buffer(ft_queue_head);
+ if (head->status == error ||
+ head->status == verifying) {
+ /* no data or overrun error */
+ head->status = waiting;
+ }
+ TRACE_CATCH(ftape_dumb_stop(),);
+ } else {
+ /* If just passed last segment on tape: wait
+ * for BOT or EOT mark. Sets ft_runner_status to
+ * idle if at lEOT and successful
+ */
+ TRACE_CATCH(ftape_handle_logical_eot(),);
+ }
+ if (verify_done) {
+ TRACE_EXIT 0;
+ }
+ /* Now at least one buffer is idle!
+ * Restart runner & tape if needed.
+ */
+ /* We could optimize the following a little bit. We know that
+ * the bad sector map is empty.
+ */
+ tail = ftape_get_buffer(ft_queue_tail);
+ if (tail->status == waiting) {
+ buffer_struct *head = ftape_get_buffer(ft_queue_head);
+
+ ftape_setup_new_segment(head, segment_id, -1);
+ ftape_calc_next_cluster(head);
+ if (ft_runner_status == idle) {
+ result = ftape_start_tape(segment_id,
+ head->sector_offset);
+ switch(result) {
+ case 0:
+ break;
+ case -ETIME:
+ case -EINTR:
+ TRACE_ABORT(result, ft_t_err, "Error: "
+ "segment %d unreachable",
+ segment_id);
+ break;
+ default:
+ *bsm = EMPTY_SEGMENT;
+ TRACE_EXIT 0;
+ break;
+ }
+ }
+ head->status = verifying;
+ fdc_setup_read_write(head, FDC_VERIFY);
+ }
+ }
+ /* not reached */
+ TRACE_EXIT -EIO;
+}
diff --git a/drivers/char/ftape/lowlevel/ftape-format.h b/drivers/char/ftape/lowlevel/ftape-format.h
new file mode 100644
index 000000000..f15161566
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/ftape-format.h
@@ -0,0 +1,37 @@
+#ifndef _FTAPE_FORMAT_H
+#define _FTAPE_FORMAT_H
+
+/*
+ * Copyright (C) 1996-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-format.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:13 $
+ *
+ * This file contains the low level definitions for the
+ * formatting support for the QIC-40/80/3010/3020 floppy-tape
+ * driver "ftape" for Linux.
+ */
+
+#ifdef __KERNEL__
+extern int ftape_format_track(const unsigned int track, const __u8 gap3);
+extern int ftape_format_status(unsigned int *segment_id);
+extern int ftape_verify_segment(const unsigned int segment_id, SectorMap *bsm);
+#endif /* __KERNEL__ */
+
+#endif
diff --git a/drivers/char/ftape/lowlevel/ftape-init.c b/drivers/char/ftape/lowlevel/ftape-init.c
new file mode 100644
index 000000000..7a8d15f5e
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/ftape-init.c
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 1993-1996 Bas Laarhoven,
+ * (C) 1996-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * This file contains the code that interfaces the kernel
+ * for the QIC-40/80/3010/3020 floppy-tape driver for Linux.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/major.h>
+
+#include <linux/ftape.h>
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,16)
+#include <linux/init.h>
+#else
+#define __initdata
+#define __initfunc(__arg) __arg
+#endif
+#include <linux/qic117.h>
+#ifdef CONFIG_ZFTAPE
+#include <linux/zftape.h>
+#endif
+
+#include "../lowlevel/ftape-init.h"
+#include "../lowlevel/ftape_syms.h"
+#include "../lowlevel/ftape-io.h"
+#include "../lowlevel/ftape-read.h"
+#include "../lowlevel/ftape-write.h"
+#include "../lowlevel/ftape-ctl.h"
+#include "../lowlevel/ftape-rw.h"
+#include "../lowlevel/fdc-io.h"
+#include "../lowlevel/ftape-buffer.h"
+#include "../lowlevel/ftape-proc.h"
+#include "../lowlevel/ftape-tracing.h"
+
+/* Global vars.
+ */
+char ft_src[] __initdata = "$Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-init.c,v $";
+char ft_rev[] __initdata = "$Revision: 1.8 $";
+char ft_dat[] __initdata = "$Date: 1997/11/06 00:38:08 $";
+
+
+/* Called by modules package when installing the driver
+ * or by kernel during the initialization phase
+ */
+__initfunc(int ftape_init(void))
+{
+ TRACE_FUN(ft_t_flow);
+
+#ifdef MODULE
+ printk(KERN_INFO FTAPE_VERSION "\n");
+ if (TRACE_LEVEL >= ft_t_info) {
+ printk(
+KERN_INFO "(c) 1993-1996 Bas Laarhoven (bas@vimec.nl)\n"
+KERN_INFO "(c) 1995-1996 Kai Harrekilde-Petersen (khp@dolphinics.no)\n"
+KERN_INFO "(c) 1996-1997 Claus-Justus Heine (claus@momo.math.rwth-aachen.de)\n"
+KERN_INFO "QIC-117 driver for QIC-40/80/3010/3020 floppy tape drives\n"
+KERN_INFO "Compiled for Linux version %s"
+#ifdef MODVERSIONS
+ " with versioned symbols"
+#endif
+ "\n", UTS_RELEASE);
+ }
+#else /* !MODULE */
+ /* print a short no-nonsense boot message */
+ printk(KERN_INFO FTAPE_VERSION " for Linux " UTS_RELEASE "\n");
+#endif /* MODULE */
+ TRACE(ft_t_info, "installing QIC-117 floppy tape hardware drive ... ");
+ TRACE(ft_t_info, "ftape_init @ 0x%p", ftape_init);
+ /* Allocate the DMA buffers. They are deallocated at cleanup() time.
+ */
+#if TESTING
+#ifdef MODULE
+ while (ftape_set_nr_buffers(CONFIG_FT_NR_BUFFERS) < 0) {
+ ftape_sleep(FT_SECOND/20);
+ if (current->signal & ~current->blocked) {
+ (void)ftape_set_nr_buffers(0);
+ TRACE(ft_t_bug,
+ "Killed by signal while allocating buffers.");
+ TRACE_ABORT(-EINTR,
+ ft_t_bug, "Free up memory and retry");
+ }
+ }
+#else
+ TRACE_CATCH(ftape_set_nr_buffers(CONFIG_FT_NR_BUFFERS),
+ (void)ftape_set_nr_buffers(0));
+#endif
+#else
+ TRACE_CATCH(ftape_set_nr_buffers(CONFIG_FT_NR_BUFFERS),
+ (void)ftape_set_nr_buffers(0));
+#endif
+ ft_drive_sel = -1;
+ ft_failure = 1; /* inhibit any operation but open */
+ ftape_udelay_calibrate(); /* must be before fdc_wait_calibrate ! */
+ fdc_wait_calibrate();
+#if (LINUX_VERSION_CODE >= KERNEL_VER(1,2,0) && \
+ LINUX_VERSION_CODE < KERNEL_VER(2,1,18))
+ register_symtab(&ftape_symbol_table); /* add global ftape symbols */
+#endif
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_FT_PROC_FS)
+ (void)ftape_proc_init();
+#endif
+#ifdef CONFIG_ZFTAPE
+ (void)zft_init();
+#endif
+ TRACE_EXIT 0;
+}
+
+#ifdef MODULE
+
+#ifndef CONFIG_FT_NO_TRACE_AT_ALL
+static int ft_tracing = -1;
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,18)
+#define FT_MOD_PARM(var,type,desc) \
+ MODULE_PARM(var,type); MODULE_PARM_DESC(var,desc)
+
+FT_MOD_PARM(ft_fdc_base, "i", "Base address of FDC controller.");
+FT_MOD_PARM(ft_fdc_irq, "i", "IRQ (interrupt channel) to use.");
+FT_MOD_PARM(ft_fdc_dma, "i", "DMA channel to use.");
+FT_MOD_PARM(ft_fdc_threshold, "i", "Threshold of the FDC Fifo.");
+FT_MOD_PARM(ft_fdc_rate_limit, "i", "Maximal data rate for FDC.");
+FT_MOD_PARM(ft_probe_fc10, "i",
+ "If non-zero, probe for a Colorado FC-10/FC-20 controller.");
+FT_MOD_PARM(ft_mach2, "i",
+ "If non-zero, probe for a Mountain MACH-2 controller.");
+FT_MOD_PARM(ft_tracing, "i",
+ "Amount of debugging output, 0 <= tracing <= 8, default 3.");
+MODULE_AUTHOR(
+ "(c) 1993-1996 Bas Laarhoven (bas@vimec.nl), "
+ "(c) 1995-1996 Kai Harrekilde-Petersen (khp@dolphinics.no), "
+ "(c) 1996, 1997 Claus-Justus Heine (claus@momo.math.rwth-aachen.de)");
+MODULE_DESCRIPTION(
+ "QIC-117 driver for QIC-40/80/3010/3020 floppy tape drives.");
+#endif
+
+#if LINUX_VERSION_CODE <= KERNEL_VER(1,2,13)
+char kernel_version[] = UTS_RELEASE;
+#endif
+
+/* Called by modules package when installing the driver
+ */
+int init_module(void)
+{
+#ifndef CONFIG_FT_NO_TRACE_AT_ALL
+ if (ft_tracing != -1) {
+ ftape_tracing = ft_tracing;
+ }
+#endif
+ return ftape_init();
+}
+
+/* Called by modules package when removing the driver
+ */
+void cleanup_module(void)
+{
+ TRACE_FUN(ft_t_flow);
+
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_FT_PROC_FS)
+ ftape_proc_destroy();
+#endif
+ (void)ftape_set_nr_buffers(0);
+ printk(KERN_INFO "ftape: unloaded.\n");
+ TRACE_EXIT;
+}
+#endif /* MODULE */
diff --git a/drivers/char/ftape/kernel-interface.h b/drivers/char/ftape/lowlevel/ftape-init.h
index 9843390c7..eae86b3dc 100644
--- a/drivers/char/ftape/kernel-interface.h
+++ b/drivers/char/ftape/lowlevel/ftape-init.h
@@ -1,8 +1,9 @@
-#ifndef _KERNEL_INTERFACE_H
-#define _KERNEL_INTERFACE_H
+#ifndef _FTAPE_INIT_H
+#define _FTAPE_INIT_H
/*
- * Copyright (C) 1993-1995 Bas Laarhoven.
+ * Copyright (C) 1993-1996 Bas Laarhoven,
+ * (C) 1996-1997 Claus-Justus Heine.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -19,14 +20,12 @@
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
- $Source: /home/bas/distr/ftape-2.03b/RCS/kernel-interface.h,v $
- $Author: bas $
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-init.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:16 $
*
- $Revision: 1.24 $
- $Date: 1995/04/30 13:15:14 $
- $State: Beta $
- *
- * ----Description----
+ * This file contains the definitions for the interface to
+ * the Linux kernel for floppy tape driver ftape.
*
*/
@@ -44,22 +43,17 @@
#define QIC117_TAPE_MAJOR 27
#endif
-#define FTAPE_NO_REWIND 4 /* mask for minor nr */
-
-/* kernel-interface.c defined global variables.
+/* ftape-init.c defined global variables.
*/
-extern byte *tape_buffer[];
+#if defined(MODULE) && LINUX_VERSION_CODE <= KERNEL_VER(1,2,13)
extern char kernel_version[];
+#endif
-/* kernel-interface.c defined global functions.
+/* ftape-init.c defined global functions not defined in ftape.h
*/
-asmlinkage extern int init_module(void);
+#ifdef MODULE
+asmlinkage extern int init_module (void);
asmlinkage extern void cleanup_module(void);
-
-/* kernel global functions not (yet) standard accessible
- * (linked at load time by modules package).
- */
-asmlinkage extern sys_sgetmask(void);
-asmlinkage extern sys_ssetmask(int);
+#endif
#endif
diff --git a/drivers/char/ftape/lowlevel/ftape-io.c b/drivers/char/ftape/lowlevel/ftape-io.c
new file mode 100644
index 000000000..af81d1802
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/ftape-io.c
@@ -0,0 +1,1018 @@
+/*
+ * Copyright (C) 1993-1996 Bas Laarhoven,
+ * (C) 1996 Kai Harrekilde-Petersen,
+ * (C) 1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-io.c,v $
+ * $Revision: 1.4 $
+ * $Date: 1997/11/11 14:02:36 $
+ *
+ * This file contains the general control functions for the
+ * QIC-40/80/3010/3020 floppy-tape driver "ftape" for Linux.
+ */
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <linux/ioctl.h>
+#include <linux/mtio.h>
+
+#include <linux/ftape.h>
+#include <linux/qic117.h>
+#include "../lowlevel/ftape-tracing.h"
+#include "../lowlevel/fdc-io.h"
+#include "../lowlevel/ftape-io.h"
+#include "../lowlevel/ftape-ctl.h"
+#include "../lowlevel/ftape-rw.h"
+#include "../lowlevel/ftape-write.h"
+#include "../lowlevel/ftape-read.h"
+#include "../lowlevel/ftape-init.h"
+#include "../lowlevel/ftape-calibr.h"
+
+/* Global vars.
+ */
+/* NOTE: sectors start numbering at 1, all others at 0 ! */
+ft_timeout_table ftape_timeout;
+unsigned int ftape_tape_len = 0;
+volatile qic117_cmd_t ftape_current_command;
+const struct qic117_command_table qic117_cmds[] = QIC117_COMMANDS;
+int ftape_might_be_off_track;
+
+/* Local vars.
+ */
+static int diagnostic_mode = 0;
+static unsigned int ftape_udelay_count;
+static unsigned int ftape_udelay_time;
+
+void ftape_udelay(unsigned int usecs)
+{
+ volatile int count = (ftape_udelay_count * usecs +
+ ftape_udelay_count - 1) / ftape_udelay_time;
+ volatile int i;
+
+ while (count-- > 0) {
+ for (i = 0; i < 20; ++i);
+ }
+}
+
+void ftape_udelay_calibrate(void)
+{
+ ftape_calibrate("ftape_udelay",
+ ftape_udelay, &ftape_udelay_count, &ftape_udelay_time);
+}
+
+/* Delay (msec) routine.
+ */
+void ftape_sleep(unsigned int time)
+{
+ TRACE_FUN(ft_t_any);
+
+ time *= 1000; /* msecs -> usecs */
+ if (time < FT_USPT) {
+ /* Time too small for scheduler, do a busy wait ! */
+ ftape_udelay(time);
+ } else {
+ unsigned long flags;
+ unsigned int ticks = (time + FT_USPT - 1) / FT_USPT;
+
+ TRACE(ft_t_any, "%d msec, %d ticks", time/1000, ticks);
+ current->timeout = jiffies + ticks;
+ current->state = TASK_INTERRUPTIBLE;
+ save_flags(flags);
+ sti();
+ do {
+ while (current->state != TASK_RUNNING) {
+ schedule();
+ }
+ /* Mmm. Isn't current->blocked == 0xffffffff ?
+ */
+ if (current->signal & ~current->blocked) {
+ TRACE(ft_t_err,
+ "awoken by non-blocked signal :-(");
+ break; /* exit on signal */
+ }
+ } while (current->timeout > 0);
+ restore_flags(flags);
+ }
+ TRACE_EXIT;
+}
+
+/* send a command or parameter to the drive
+ * Generates # of step pulses.
+ */
+static inline int ft_send_to_drive(int arg)
+{
+ /* Always wait for a command_timeout period to separate
+ * individuals commands and/or parameters.
+ */
+ ftape_sleep(3 * FT_MILLISECOND);
+ /* Keep cylinder nr within range, step towards home if possible.
+ */
+ if (ftape_current_cylinder >= arg) {
+ return fdc_seek(ftape_current_cylinder - arg);
+ } else {
+ return fdc_seek(ftape_current_cylinder + arg);
+ }
+}
+
+/* forward */ int ftape_report_raw_drive_status(int *status);
+
+static int ft_check_cmd_restrictions(qic117_cmd_t command)
+{
+ int status = -1;
+ TRACE_FUN(ft_t_any);
+
+ TRACE(ft_t_flow, "%s", qic117_cmds[command].name);
+ /* A new motion command during an uninterruptible (motion)
+ * command requires a ready status before the new command can
+ * be issued. Otherwise a new motion command needs to be
+ * checked against required status.
+ */
+ if (qic117_cmds[command].cmd_type == motion &&
+ qic117_cmds[ftape_current_command].non_intr) {
+ ftape_report_raw_drive_status(&status);
+ if ((status & QIC_STATUS_READY) == 0) {
+ TRACE(ft_t_noise,
+ "motion cmd (%d) during non-intr cmd (%d)",
+ command, ftape_current_command);
+ TRACE(ft_t_noise, "waiting until drive gets ready");
+ ftape_ready_wait(ftape_timeout.seek,
+ &status);
+ }
+ }
+ if (qic117_cmds[command].mask != 0) {
+ __u8 difference;
+ /* Some commands do require a certain status:
+ */
+ if (status == -1) { /* not yet set */
+ ftape_report_raw_drive_status(&status);
+ }
+ difference = ((status ^ qic117_cmds[command].state) &
+ qic117_cmds[command].mask);
+ /* Wait until the drive gets
+ * ready. This may last forever if
+ * the drive never gets ready...
+ */
+ while ((difference & QIC_STATUS_READY) != 0) {
+ TRACE(ft_t_noise, "command %d issued while not ready",
+ command);
+ TRACE(ft_t_noise, "waiting until drive gets ready");
+ if (ftape_ready_wait(ftape_timeout.seek,
+ &status) == -EINTR) {
+ /* Bail out on signal !
+ */
+ TRACE_ABORT(-EINTR, ft_t_warn,
+ "interrupted by non-blockable signal");
+ }
+ difference = ((status ^ qic117_cmds[command].state) &
+ qic117_cmds[command].mask);
+ }
+ while ((difference & QIC_STATUS_ERROR) != 0) {
+ int err;
+ qic117_cmd_t cmd;
+
+ TRACE(ft_t_noise,
+ "command %d issued while error pending",
+ command);
+ TRACE(ft_t_noise, "clearing error status");
+ ftape_report_error(&err, &cmd, 1);
+ ftape_report_raw_drive_status(&status);
+ difference = ((status ^ qic117_cmds[command].state) &
+ qic117_cmds[command].mask);
+ if ((difference & QIC_STATUS_ERROR) != 0) {
+ /* Bail out on fatal signal !
+ */
+ FT_SIGNAL_EXIT(_NEVER_BLOCK);
+ }
+ }
+ if (difference) {
+ /* Any remaining difference can't be solved
+ * here.
+ */
+ if (difference & (QIC_STATUS_CARTRIDGE_PRESENT |
+ QIC_STATUS_NEW_CARTRIDGE |
+ QIC_STATUS_REFERENCED)) {
+ TRACE(ft_t_warn,
+ "Fatal: tape removed or reinserted !");
+ ft_failure = 1;
+ } else {
+ TRACE(ft_t_err, "wrong state: 0x%02x should be: 0x%02x",
+ status & qic117_cmds[command].mask,
+ qic117_cmds[command].state);
+ }
+ TRACE_EXIT -EIO;
+ }
+ if (~status & QIC_STATUS_READY & qic117_cmds[command].mask) {
+ TRACE_ABORT(-EBUSY, ft_t_err, "Bad: still busy!");
+ }
+ }
+ TRACE_EXIT 0;
+}
+
+/* Issue a tape command:
+ */
+int ftape_command(qic117_cmd_t command)
+{
+ int result = 0;
+ static int level = 0;
+ TRACE_FUN(ft_t_any);
+
+ if ((unsigned int)command > NR_ITEMS(qic117_cmds)) {
+ /* This is a bug we'll want to know about too.
+ */
+ TRACE_ABORT(-EIO, ft_t_bug, "bug - bad command: %d", command);
+ }
+ if (++level > 5) { /* This is a bug we'll want to know about. */
+ --level;
+ TRACE_ABORT(-EIO, ft_t_bug, "bug - recursion for command: %d",
+ command);
+ }
+ /* disable logging and restriction check for some commands,
+ * check all other commands that have a prescribed starting
+ * status.
+ */
+ if (diagnostic_mode) {
+ TRACE(ft_t_flow, "diagnostic command %d", command);
+ } else if (command == QIC_REPORT_DRIVE_STATUS ||
+ command == QIC_REPORT_NEXT_BIT) {
+ TRACE(ft_t_any, "%s", qic117_cmds[command].name);
+ } else {
+ TRACE_CATCH(ft_check_cmd_restrictions(command), --level);
+ }
+ /* Now all conditions are met or result was < 0.
+ */
+ result = ft_send_to_drive((unsigned int)command);
+ if (qic117_cmds[command].cmd_type == motion &&
+ command != QIC_LOGICAL_FORWARD && command != QIC_STOP_TAPE) {
+ ft_location.known = 0;
+ }
+ ftape_current_command = command;
+ --level;
+ TRACE_EXIT result;
+}
+
+/* Send a tape command parameter:
+ * Generates command # of step pulses.
+ * Skips tape-status call !
+ */
+int ftape_parameter(unsigned int parameter)
+{
+ TRACE_FUN(ft_t_any);
+
+ TRACE(ft_t_flow, "called with parameter = %d", parameter);
+ TRACE_EXIT ft_send_to_drive(parameter + 2);
+}
+
+/* Wait for the drive to get ready.
+ * timeout time in milli-seconds
+ * Returned status is valid if result != -EIO
+ *
+ * Should we allow to be killed by SIGINT? (^C)
+ * Would be nice at least for large timeouts.
+ */
+int ftape_ready_wait(unsigned int timeout, int *status)
+{
+ unsigned long t0;
+ unsigned int poll_delay;
+ int signal_retries;
+ TRACE_FUN(ft_t_any);
+
+ /* the following ** REALLY ** reduces the system load when
+ * e.g. one simply rewinds or retensions. The tape is slow
+ * anyway. It is really not necessary to detect error
+ * conditions with 1/10 seconds granularity
+ *
+ * On my AMD 133MHZ 486: 100 ms: 23% system load
+ * 1 sec: 5%
+ * 5 sec: 0.6%, yeah
+ */
+ if (timeout <= FT_SECOND) {
+ poll_delay = 100 * FT_MILLISECOND;
+ signal_retries = 20; /* two seconds */
+ } else if (timeout < 20 * FT_SECOND) {
+ TRACE(ft_t_flow, "setting poll delay to 1 second");
+ poll_delay = FT_SECOND;
+ signal_retries = 2; /* two seconds */
+ } else {
+ TRACE(ft_t_flow, "setting poll delay to 5 seconds");
+ poll_delay = 5 * FT_SECOND;
+ signal_retries = 1; /* five seconds */
+ }
+ for (;;) {
+ t0 = jiffies;
+ TRACE_CATCH(ftape_report_raw_drive_status(status),);
+ if (*status & QIC_STATUS_READY) {
+ TRACE_EXIT 0;
+ }
+ if (!signal_retries--) {
+ FT_SIGNAL_EXIT(_NEVER_BLOCK);
+ }
+ if ((int)timeout >= 0) {
+ /* this will fail when jiffies wraps around about
+ * once every year :-)
+ */
+ timeout -= ((jiffies - t0) * FT_SECOND) / HZ;
+ if (timeout <= 0) {
+ TRACE_ABORT(-ETIME, ft_t_err, "timeout");
+ }
+ ftape_sleep(poll_delay);
+ timeout -= poll_delay;
+ } else {
+ ftape_sleep(poll_delay);
+ }
+ }
+ TRACE_EXIT -ETIME;
+}
+
+/* Issue command and wait up to timeout milli seconds for drive ready
+ */
+int ftape_command_wait(qic117_cmd_t command, unsigned int timeout, int *status)
+{
+ int result;
+
+ /* Drive should be ready, issue command
+ */
+ result = ftape_command(command);
+ if (result >= 0) {
+ result = ftape_ready_wait(timeout, status);
+ }
+ return result;
+}
+
+int ftape_parameter_wait(unsigned int parm, unsigned int timeout, int *status)
+{
+ int result;
+
+ /* Drive should be ready, issue command
+ */
+ result = ftape_parameter(parm);
+ if (result >= 0) {
+ result = ftape_ready_wait(timeout, status);
+ }
+ return result;
+}
+
+/*--------------------------------------------------------------------------
+ * Report operations
+ */
+
+/* Query the drive about its status. The command is sent and
+ result_length bits of status are returned (2 extra bits are read
+ for start and stop). */
+
+int ftape_report_operation(int *status,
+ qic117_cmd_t command,
+ int result_length)
+{
+ int i, st3;
+ unsigned int t0;
+ unsigned int dt;
+ TRACE_FUN(ft_t_any);
+
+ TRACE_CATCH(ftape_command(command),);
+ t0 = ftape_timestamp();
+ i = 0;
+ do {
+ ++i;
+ ftape_sleep(3 * FT_MILLISECOND); /* see remark below */
+ TRACE_CATCH(fdc_sense_drive_status(&st3),);
+ dt = ftape_timediff(t0, ftape_timestamp());
+ /* Ack should be asserted within Ttimout + Tack = 6 msec.
+ * Looks like some drives fail to do this so extend this
+ * period to 300 msec.
+ */
+ } while (!(st3 & ST3_TRACK_0) && dt < 300000);
+ if (!(st3 & ST3_TRACK_0)) {
+ TRACE(ft_t_err,
+ "No acknowledge after %u msec. (%i iter)", dt / 1000, i);
+ TRACE_ABORT(-EIO, ft_t_err, "timeout on Acknowledge");
+ }
+ /* dt may be larger than expected because of other tasks
+ * scheduled while we were sleeping.
+ */
+ if (i > 1 && dt > 6000) {
+ TRACE(ft_t_err, "Acknowledge after %u msec. (%i iter)",
+ dt / 1000, i);
+ }
+ *status = 0;
+ for (i = 0; i < result_length + 1; i++) {
+ TRACE_CATCH(ftape_command(QIC_REPORT_NEXT_BIT),);
+ TRACE_CATCH(fdc_sense_drive_status(&st3),);
+ if (i < result_length) {
+ *status |= ((st3 & ST3_TRACK_0) ? 1 : 0) << i;
+ } else if ((st3 & ST3_TRACK_0) == 0) {
+ TRACE_ABORT(-EIO, ft_t_err, "missing status stop bit");
+ }
+ }
+ /* this command will put track zero and index back into normal state */
+ (void)ftape_command(QIC_REPORT_NEXT_BIT);
+ TRACE_EXIT 0;
+}
+
+/* Report the current drive status. */
+
+int ftape_report_raw_drive_status(int *status)
+{
+ int result;
+ int count = 0;
+ TRACE_FUN(ft_t_any);
+
+ do {
+ result = ftape_report_operation(status,
+ QIC_REPORT_DRIVE_STATUS, 8);
+ } while (result < 0 && ++count <= 3);
+ if (result < 0) {
+ TRACE_ABORT(-EIO, ft_t_err,
+ "report_operation failed after %d trials", count);
+ }
+ if ((*status & 0xff) == 0xff) {
+ TRACE_ABORT(-EIO, ft_t_err,
+ "impossible drive status 0xff");
+ }
+ if (*status & QIC_STATUS_READY) {
+ ftape_current_command = QIC_NO_COMMAND; /* completed */
+ }
+ ft_last_status.status.drive_status = (__u8)(*status & 0xff);
+ TRACE_EXIT 0;
+}
+
+int ftape_report_drive_status(int *status)
+{
+ TRACE_FUN(ft_t_any);
+
+ TRACE_CATCH(ftape_report_raw_drive_status(status),);
+ if (*status & QIC_STATUS_NEW_CARTRIDGE ||
+ !(*status & QIC_STATUS_CARTRIDGE_PRESENT)) {
+ ft_failure = 1; /* will inhibit further operations */
+ TRACE_EXIT -EIO;
+ }
+ if (*status & QIC_STATUS_READY && *status & QIC_STATUS_ERROR) {
+ /* Let caller handle all errors */
+ TRACE_ABORT(1, ft_t_warn, "warning: error status set!");
+ }
+ TRACE_EXIT 0;
+}
+
+int ftape_report_error(unsigned int *error,
+ qic117_cmd_t *command, int report)
+{
+ static const ftape_error ftape_errors[] = QIC117_ERRORS;
+ int code;
+ TRACE_FUN(ft_t_any);
+
+ TRACE_CATCH(ftape_report_operation(&code, QIC_REPORT_ERROR_CODE, 16),);
+ *error = (unsigned int)(code & 0xff);
+ *command = (qic117_cmd_t)((code>>8)&0xff);
+ /* remember hardware status, maybe useful for status ioctls
+ */
+ ft_last_error.error.command = (__u8)*command;
+ ft_last_error.error.error = (__u8)*error;
+ if (!report) {
+ TRACE_EXIT 0;
+ }
+ if (*error == 0) {
+ TRACE_ABORT(0, ft_t_info, "No error");
+ }
+ TRACE(ft_t_info, "errorcode: %d", *error);
+ if (*error < NR_ITEMS(ftape_errors)) {
+ TRACE(ft_t_noise, "%sFatal ERROR:",
+ (ftape_errors[*error].fatal ? "" : "Non-"));
+ TRACE(ft_t_noise, "%s ...", ftape_errors[*error].message);
+ } else {
+ TRACE(ft_t_noise, "Unknown ERROR !");
+ }
+ if ((unsigned int)*command < NR_ITEMS(qic117_cmds) &&
+ qic117_cmds[*command].name != NULL) {
+ TRACE(ft_t_noise, "... caused by command \'%s\'",
+ qic117_cmds[*command].name);
+ } else {
+ TRACE(ft_t_noise, "... caused by unknown command %d",
+ *command);
+ }
+ TRACE_EXIT 0;
+}
+
+int ftape_in_error_state(int status)
+{
+ TRACE_FUN(ft_t_any);
+
+ if ((status & QIC_STATUS_READY) && (status & QIC_STATUS_ERROR)) {
+ TRACE_ABORT(1, ft_t_warn, "warning: error status set!");
+ }
+ TRACE_EXIT 0;
+}
+
+int ftape_report_configuration(qic_model *model,
+ unsigned int *rate,
+ int *qic_std,
+ int *tape_len)
+{
+ int result;
+ int config;
+ int status;
+ static const unsigned int qic_rates[ 4] = { 250, 2000, 500, 1000 };
+ TRACE_FUN(ft_t_any);
+
+ result = ftape_report_operation(&config,
+ QIC_REPORT_DRIVE_CONFIGURATION, 8);
+ if (result < 0) {
+ ft_last_status.status.drive_config = (__u8)0x00;
+ *model = prehistoric;
+ *rate = 500;
+ *qic_std = QIC_TAPE_QIC40;
+ *tape_len = 205;
+ TRACE_EXIT 0;
+ } else {
+ ft_last_status.status.drive_config = (__u8)(config & 0xff);
+ }
+ *rate = qic_rates[(config & QIC_CONFIG_RATE_MASK) >> QIC_CONFIG_RATE_SHIFT];
+ result = ftape_report_operation(&status, QIC_REPORT_TAPE_STATUS, 8);
+ if (result < 0) {
+ ft_last_status.status.tape_status = (__u8)0x00;
+ /* pre- QIC117 rev C spec. drive, QIC_CONFIG_80 bit is valid.
+ */
+ *qic_std = (config & QIC_CONFIG_80) ?
+ QIC_TAPE_QIC80 : QIC_TAPE_QIC40;
+ /* ?? how's about 425ft tapes? */
+ *tape_len = (config & QIC_CONFIG_LONG) ? 307 : 0;
+ *model = pre_qic117c;
+ result = 0;
+ } else {
+ ft_last_status.status.tape_status = (__u8)(status & 0xff);
+ *model = post_qic117b;
+ TRACE(ft_t_any, "report tape status result = %02x", status);
+ /* post- QIC117 rev C spec. drive, QIC_CONFIG_80 bit is
+ * invalid.
+ */
+ switch (status & QIC_TAPE_STD_MASK) {
+ case QIC_TAPE_QIC40:
+ case QIC_TAPE_QIC80:
+ case QIC_TAPE_QIC3020:
+ case QIC_TAPE_QIC3010:
+ *qic_std = status & QIC_TAPE_STD_MASK;
+ break;
+ default:
+ *qic_std = -1;
+ break;
+ }
+ switch (status & QIC_TAPE_LEN_MASK) {
+ case QIC_TAPE_205FT:
+ /* 205 or 425+ ft 550 Oe tape */
+ *tape_len = 0;
+ break;
+ case QIC_TAPE_307FT:
+ /* 307.5 ft 550 Oe Extended Length (XL) tape */
+ *tape_len = 307;
+ break;
+ case QIC_TAPE_VARIABLE:
+ /* Variable length 550 Oe tape */
+ *tape_len = 0;
+ break;
+ case QIC_TAPE_1100FT:
+ /* 1100 ft 550 Oe tape */
+ *tape_len = 1100;
+ break;
+ case QIC_TAPE_FLEX:
+ /* Variable length 900 Oe tape */
+ *tape_len = 0;
+ break;
+ default:
+ *tape_len = -1;
+ break;
+ }
+ if (*qic_std == -1 || *tape_len == -1) {
+ TRACE(ft_t_any,
+ "post qic-117b spec drive with unknown tape");
+ }
+ result = *tape_len == -1 ? -EIO : 0;
+ if (status & QIC_TAPE_WIDE) {
+ switch (*qic_std) {
+ case QIC_TAPE_QIC80:
+ TRACE(ft_t_info, "TR-1 tape detected");
+ break;
+ case QIC_TAPE_QIC3010:
+ TRACE(ft_t_info, "TR-2 tape detected");
+ break;
+ case QIC_TAPE_QIC3020:
+ TRACE(ft_t_info, "TR-3 tape detected");
+ break;
+ default:
+ TRACE(ft_t_warn,
+ "Unknown Travan tape type detected");
+ break;
+ }
+ }
+ }
+ TRACE_EXIT (result < 0) ? -EIO : 0;
+}
+
+int ftape_report_rom_version(int *version)
+{
+
+ if (ftape_report_operation(version, QIC_REPORT_ROM_VERSION, 8) < 0) {
+ return -EIO;
+ } else {
+ return 0;
+ }
+}
+
+int ftape_report_signature(int *signature)
+{
+ int result;
+
+ result = ftape_command(28);
+ result = ftape_report_operation(signature, 9, 8);
+ result = ftape_command(30);
+ return (result < 0) ? -EIO : 0;
+}
+
+void ftape_report_vendor_id(unsigned int *id)
+{
+ int result;
+ TRACE_FUN(ft_t_any);
+
+ /* We'll try to get a vendor id from the drive. First
+ * according to the QIC-117 spec, a 16-bit id is requested.
+ * If that fails we'll try an 8-bit version, otherwise we'll
+ * try an undocumented query.
+ */
+ result = ftape_report_operation((int *) id, QIC_REPORT_VENDOR_ID, 16);
+ if (result < 0) {
+ result = ftape_report_operation((int *) id,
+ QIC_REPORT_VENDOR_ID, 8);
+ if (result < 0) {
+ /* The following is an undocumented call found
+ * in the CMS code.
+ */
+ result = ftape_report_operation((int *) id, 24, 8);
+ if (result < 0) {
+ *id = UNKNOWN_VENDOR;
+ } else {
+ TRACE(ft_t_noise, "got old 8 bit id: %04x",
+ *id);
+ *id |= 0x20000;
+ }
+ } else {
+ TRACE(ft_t_noise, "got 8 bit id: %04x", *id);
+ *id |= 0x10000;
+ }
+ } else {
+ TRACE(ft_t_noise, "got 16 bit id: %04x", *id);
+ }
+ if (*id == 0x0047) {
+ int version;
+ int sign;
+
+ if (ftape_report_rom_version(&version) < 0) {
+ TRACE(ft_t_bug, "report rom version failed");
+ TRACE_EXIT;
+ }
+ TRACE(ft_t_noise, "CMS rom version: %d", version);
+ ftape_command(QIC_ENTER_DIAGNOSTIC_1);
+ ftape_command(QIC_ENTER_DIAGNOSTIC_1);
+ diagnostic_mode = 1;
+ if (ftape_report_operation(&sign, 9, 8) < 0) {
+ unsigned int error;
+ qic117_cmd_t command;
+
+ ftape_report_error(&error, &command, 1);
+ ftape_command(QIC_ENTER_PRIMARY_MODE);
+ diagnostic_mode = 0;
+ TRACE_EXIT; /* failure ! */
+ } else {
+ TRACE(ft_t_noise, "CMS signature: %02x", sign);
+ }
+ if (sign == 0xa5) {
+ result = ftape_report_operation(&sign, 37, 8);
+ if (result < 0) {
+ if (version >= 63) {
+ *id = 0x8880;
+ TRACE(ft_t_noise,
+ "This is an Iomega drive !");
+ } else {
+ *id = 0x0047;
+ TRACE(ft_t_noise,
+ "This is a real CMS drive !");
+ }
+ } else {
+ *id = 0x0047;
+ TRACE(ft_t_noise, "CMS status: %d", sign);
+ }
+ } else {
+ *id = UNKNOWN_VENDOR;
+ }
+ ftape_command(QIC_ENTER_PRIMARY_MODE);
+ diagnostic_mode = 0;
+ }
+ TRACE_EXIT;
+}
+
+static int qic_rate_code(unsigned int rate)
+{
+ switch (rate) {
+ case 250:
+ return QIC_CONFIG_RATE_250;
+ case 500:
+ return QIC_CONFIG_RATE_500;
+ case 1000:
+ return QIC_CONFIG_RATE_1000;
+ case 2000:
+ return QIC_CONFIG_RATE_2000;
+ default:
+ return QIC_CONFIG_RATE_500;
+ }
+}
+
+static int ftape_set_rate_test(unsigned int *max_rate)
+{
+ unsigned int error;
+ qic117_cmd_t command;
+ int status;
+ int supported = 0;
+ TRACE_FUN(ft_t_any);
+
+ /* Check if the drive does support the select rate command
+ * by testing all different settings. If any one is accepted
+ * we assume the command is supported, else not.
+ */
+ for (*max_rate = 2000; *max_rate >= 250; *max_rate /= 2) {
+ if (ftape_command(QIC_SELECT_RATE) < 0) {
+ continue;
+ }
+ if (ftape_parameter_wait(qic_rate_code(*max_rate),
+ 1 * FT_SECOND, &status) < 0) {
+ continue;
+ }
+ if (status & QIC_STATUS_ERROR) {
+ ftape_report_error(&error, &command, 0);
+ continue;
+ }
+ supported = 1; /* did accept a request */
+ break;
+ }
+ TRACE(ft_t_noise, "Select Rate command is%s supported",
+ supported ? "" : " not");
+ TRACE_EXIT supported;
+}
+
+int ftape_set_data_rate(unsigned int new_rate /* Kbps */, unsigned int qic_std)
+{
+ int status;
+ int result = 0;
+ unsigned int data_rate = new_rate;
+ static int supported = 0;
+ int rate_changed = 0;
+ qic_model dummy_model;
+ unsigned int dummy_qic_std, dummy_tape_len;
+ TRACE_FUN(ft_t_any);
+
+ if (ft_drive_max_rate == 0) { /* first time */
+ supported = ftape_set_rate_test(&ft_drive_max_rate);
+ }
+ if (supported) {
+ ftape_command(QIC_SELECT_RATE);
+ result = ftape_parameter_wait(qic_rate_code(new_rate),
+ 1 * FT_SECOND, &status);
+ if (result >= 0 && !(status & QIC_STATUS_ERROR)) {
+ rate_changed = 1;
+ }
+ }
+ TRACE_CATCH(result = ftape_report_configuration(&dummy_model,
+ &data_rate,
+ &dummy_qic_std,
+ &dummy_tape_len),);
+ if (data_rate != new_rate) {
+ if (!supported) {
+ TRACE(ft_t_warn, "Rate change not supported!");
+ } else if (rate_changed) {
+ TRACE(ft_t_warn, "Requested: %d, got %d",
+ new_rate, data_rate);
+ } else {
+ TRACE(ft_t_warn, "Rate change failed!");
+ }
+ result = -EINVAL;
+ }
+ /*
+ * Set data rate and write precompensation as specified:
+ *
+ * | QIC-40/80 | QIC-3010/3020
+ * rate | precomp | precomp
+ * ----------+-------------+--------------
+ * 250 Kbps. | 250 ns. | 0 ns.
+ * 500 Kbps. | 125 ns. | 0 ns.
+ * 1 Mbps. | 42 ns. | 0 ns.
+ * 2 Mbps | N/A | 0 ns.
+ */
+ if ((qic_std == QIC_TAPE_QIC40 && data_rate > 500) ||
+ (qic_std == QIC_TAPE_QIC80 && data_rate > 1000)) {
+ TRACE_ABORT(-EINVAL,
+ ft_t_warn, "Datarate too high for QIC-mode");
+ }
+ TRACE_CATCH(fdc_set_data_rate(data_rate),_res = -EINVAL);
+ ft_data_rate = data_rate;
+ if (qic_std == QIC_TAPE_QIC40 || qic_std == QIC_TAPE_QIC80) {
+ switch (data_rate) {
+ case 250:
+ fdc_set_write_precomp(250);
+ break;
+ default:
+ case 500:
+ fdc_set_write_precomp(125);
+ break;
+ case 1000:
+ fdc_set_write_precomp(42);
+ break;
+ }
+ } else {
+ fdc_set_write_precomp(0);
+ }
+ TRACE_EXIT result;
+}
+
+/* The next two functions are used to cope with excessive overrun errors
+ */
+int ftape_increase_threshold(void)
+{
+ TRACE_FUN(ft_t_flow);
+
+ if (fdc.type < i82077 || ft_fdc_threshold >= 12) {
+ TRACE_ABORT(-EIO, ft_t_err, "cannot increase fifo threshold");
+ }
+ if (fdc_fifo_threshold(++ft_fdc_threshold, NULL, NULL, NULL) < 0) {
+ TRACE(ft_t_err, "cannot increase fifo threshold");
+ ft_fdc_threshold --;
+ fdc_reset();
+ }
+ TRACE(ft_t_info, "New FIFO threshold: %d", ft_fdc_threshold);
+ TRACE_EXIT 0;
+}
+
+int ftape_half_data_rate(void)
+{
+ if (ft_data_rate < 500) {
+ return -1;
+ }
+ if (ftape_set_data_rate(ft_data_rate / 2, ft_qic_std) < 0) {
+ return -EIO;
+ }
+ ftape_calc_timeouts(ft_qic_std, ft_data_rate, ftape_tape_len);
+ return 0;
+}
+
+/* Seek the head to the specified track.
+ */
+int ftape_seek_head_to_track(unsigned int track)
+{
+ int status;
+ TRACE_FUN(ft_t_any);
+
+ ft_location.track = -1; /* remains set in case of error */
+ if (track >= ft_tracks_per_tape) {
+ TRACE_ABORT(-EINVAL, ft_t_bug, "track out of bounds");
+ }
+ TRACE(ft_t_flow, "seeking track %d", track);
+ TRACE_CATCH(ftape_command(QIC_SEEK_HEAD_TO_TRACK),);
+ TRACE_CATCH(ftape_parameter_wait(track, ftape_timeout.head_seek,
+ &status),);
+ ft_location.track = track;
+ ftape_might_be_off_track = 0;
+ TRACE_EXIT 0;
+}
+
+int ftape_wakeup_drive(wake_up_types method)
+{
+ int status;
+ int motor_on = 0;
+ TRACE_FUN(ft_t_any);
+
+ switch (method) {
+ case wake_up_colorado:
+ TRACE_CATCH(ftape_command(QIC_PHANTOM_SELECT),);
+ TRACE_CATCH(ftape_parameter(0 /* ft_drive_sel ?? */),);
+ break;
+ case wake_up_mountain:
+ TRACE_CATCH(ftape_command(QIC_SOFT_SELECT),);
+ ftape_sleep(FT_MILLISECOND); /* NEEDED */
+ TRACE_CATCH(ftape_parameter(18),);
+ break;
+ case wake_up_insight:
+ ftape_sleep(100 * FT_MILLISECOND);
+ motor_on = 1;
+ fdc_motor(motor_on); /* enable is done by motor-on */
+ case no_wake_up:
+ break;
+ default:
+ TRACE_EXIT -ENODEV; /* unknown wakeup method */
+ break;
+ }
+ /* If wakeup succeeded we shouldn't get an error here..
+ */
+ TRACE_CATCH(ftape_report_raw_drive_status(&status),
+ if (motor_on) {
+ fdc_motor(0);
+ });
+ TRACE_EXIT 0;
+}
+
+int ftape_put_drive_to_sleep(wake_up_types method)
+{
+ TRACE_FUN(ft_t_any);
+
+ switch (method) {
+ case wake_up_colorado:
+ TRACE_CATCH(ftape_command(QIC_PHANTOM_DESELECT),);
+ break;
+ case wake_up_mountain:
+ TRACE_CATCH(ftape_command(QIC_SOFT_DESELECT),);
+ break;
+ case wake_up_insight:
+ fdc_motor(0); /* enable is done by motor-on */
+ case no_wake_up: /* no wakeup / no sleep ! */
+ break;
+ default:
+ TRACE_EXIT -ENODEV; /* unknown wakeup method */
+ }
+ TRACE_EXIT 0;
+}
+
+int ftape_reset_drive(void)
+{
+ int result = 0;
+ int status;
+ unsigned int err_code;
+ qic117_cmd_t err_command;
+ int i;
+ TRACE_FUN(ft_t_any);
+
+ /* We want to re-establish contact with our drive. Fire a
+ * number of reset commands (single step pulses) and pray for
+ * success.
+ */
+ for (i = 0; i < 2; ++i) {
+ TRACE(ft_t_flow, "Resetting fdc");
+ fdc_reset();
+ ftape_sleep(10 * FT_MILLISECOND);
+ TRACE(ft_t_flow, "Reset command to drive");
+ result = ftape_command(QIC_RESET);
+ if (result == 0) {
+ ftape_sleep(1 * FT_SECOND); /* drive not
+ * accessible
+ * during 1 second
+ */
+ TRACE(ft_t_flow, "Re-selecting drive");
+
+ /* Strange, the QIC-117 specs don't mention
+ * this but the drive gets deselected after a
+ * soft reset ! So we need to enable it
+ * again.
+ */
+ if (ftape_wakeup_drive(ft_drive_type.wake_up) < 0) {
+ TRACE(ft_t_err, "Wakeup failed !");
+ }
+ TRACE(ft_t_flow, "Waiting until drive gets ready");
+ result= ftape_ready_wait(ftape_timeout.reset, &status);
+ if (result == 0 && (status & QIC_STATUS_ERROR)) {
+ result = ftape_report_error(&err_code,
+ &err_command, 1);
+ if (result == 0 && err_code == 27) {
+ /* Okay, drive saw reset
+ * command and responded as it
+ * should
+ */
+ break;
+ } else {
+ result = -EIO;
+ }
+ } else {
+ result = -EIO;
+ }
+ }
+ FT_SIGNAL_EXIT(_DONT_BLOCK);
+ }
+ if (result != 0) {
+ TRACE(ft_t_err, "General failure to reset tape drive");
+ } else {
+ /* Restore correct settings: keep original rate
+ */
+ ftape_set_data_rate(ft_data_rate, ft_qic_std);
+ }
+ ftape_init_drive_needed = 1;
+ TRACE_EXIT result;
+}
diff --git a/drivers/char/ftape/lowlevel/ftape-io.h b/drivers/char/ftape/lowlevel/ftape-io.h
new file mode 100644
index 000000000..841f52d73
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/ftape-io.h
@@ -0,0 +1,94 @@
+#ifndef _FTAPE_IO_H
+#define _FTAPE_IO_H
+
+/*
+ * Copyright (C) 1993-1996 Bas Laarhoven,
+ * (C) 1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-io.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:18 $
+ *
+ * This file contains definitions for the glue part of the
+ * QIC-40/80/3010/3020 floppy-tape driver "ftape" for Linux.
+ */
+
+#include <linux/qic117.h>
+#include <linux/ftape-vendors.h>
+
+typedef struct {
+ unsigned int seek;
+ unsigned int reset;
+ unsigned int rewind;
+ unsigned int head_seek;
+ unsigned int stop;
+ unsigned int pause;
+} ft_timeout_table;
+
+typedef enum {
+ prehistoric, pre_qic117c, post_qic117b, post_qic117d
+} qic_model;
+
+/*
+ * ftape-io.c defined global vars.
+ */
+extern ft_timeout_table ftape_timeout;
+extern unsigned int ftape_tape_len;
+extern volatile qic117_cmd_t ftape_current_command;
+extern const struct qic117_command_table qic117_cmds[];
+extern int ftape_might_be_off_track;
+
+/*
+ * ftape-io.c defined global functions.
+ */
+extern void ftape_udelay(unsigned int usecs);
+extern void ftape_udelay_calibrate(void);
+extern void ftape_sleep(unsigned int time);
+extern void ftape_report_vendor_id(unsigned int *id);
+extern int ftape_command(qic117_cmd_t command);
+extern int ftape_command_wait(qic117_cmd_t command,
+ unsigned int timeout,
+ int *status);
+extern int ftape_parameter(unsigned int parameter);
+extern int ftape_parameter_wait(unsigned int parameter,
+ unsigned int timeout,
+ int *status);
+extern int ftape_report_operation(int *status,
+ qic117_cmd_t command,
+ int result_length);
+extern int ftape_report_configuration(qic_model *model,
+ unsigned int *rate,
+ int *qic_std,
+ int *tape_len);
+extern int ftape_report_drive_status(int *status);
+extern int ftape_report_raw_drive_status(int *status);
+extern int ftape_report_status(int *status);
+extern int ftape_ready_wait(unsigned int timeout, int *status);
+extern int ftape_seek_head_to_track(unsigned int track);
+extern int ftape_in_error_state(int status);
+extern int ftape_set_data_rate(unsigned int new_rate, unsigned int qic_std);
+extern int ftape_report_error(unsigned int *error,
+ qic117_cmd_t *command,
+ int report);
+extern int ftape_reset_drive(void);
+extern int ftape_put_drive_to_sleep(wake_up_types method);
+extern int ftape_wakeup_drive(wake_up_types method);
+extern int ftape_increase_threshold(void);
+extern int ftape_half_data_rate(void);
+
+#endif
diff --git a/drivers/char/ftape/lowlevel/ftape-proc.c b/drivers/char/ftape/lowlevel/ftape-proc.c
new file mode 100644
index 000000000..ebb6a3f04
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/ftape-proc.c
@@ -0,0 +1,431 @@
+/*
+ * Copyright (C) 1997 Claus-Justus Heine
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-proc.c,v $
+ * $Revision: 1.11 $
+ * $Date: 1997/10/24 14:47:37 $
+ *
+ * This file contains the procfs interface for the
+ * QIC-40/80/3010/3020 floppy-tape driver "ftape" for Linux.
+ */
+
+#include <linux/config.h>
+
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_FT_PROC_FS)
+
+/* adding proc entries from inside a module is REALLY complicated
+ * for pre-2.1.28 kernels. I don't want to care about it.
+ */
+
+#include <linux/proc_fs.h>
+
+#include <linux/ftape.h>
+#if LINUX_VERSION_CODE <= KERNEL_VER(1,2,13) /* bail out */
+#error \
+Please disable CONFIG_FT_PROC_FS in "MCONFIG" or upgrade to a newer kernel!
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,16)
+#include <linux/init.h>
+#else
+#define __initdata
+#define __initfunc(__arg) __arg
+#endif
+#include <linux/qic117.h>
+
+
+#include "../lowlevel/ftape-io.h"
+#include "../lowlevel/ftape-ctl.h"
+#include "../lowlevel/ftape-proc.h"
+#include "../lowlevel/ftape-tracing.h"
+
+static int ftape_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data);
+
+#if LINUX_VERSION_CODE < KERNEL_VER(2,1,28)
+
+#include <asm/segment.h> /* for memcpy_tofs() */
+
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,0)
+static long ftape_proc_read(struct inode* inode, struct file* file,
+ char* buf, unsigned long count);
+#else
+static int ftape_proc_read(struct inode* inode, struct file* file,
+ char* buf, int count);
+#endif
+
+#define FT_PROC_REGISTER(parent, child) proc_register_dynamic(parent, child)
+
+/*
+ * Structures for interfacing with the /proc filesystem.
+ * Router creates its own directory /proc/net/router with the folowing
+ * entries:
+ * config device configuration
+ * status global device statistics
+ * <device> entry for each WAN device
+ */
+
+/*
+ * Generic /proc/net/ftape/<file> file and inode operations
+ */
+
+
+static struct file_operations ftape_proc_fops =
+{
+ NULL, /* lseek */
+ ftape_proc_read, /* read */
+ NULL, /* write */
+ NULL, /* readdir */
+ NULL, /* select */
+ NULL, /* ioctl */
+ NULL, /* mmap */
+ NULL, /* no special open code */
+ NULL, /* no special release code */
+ NULL, /* can't fsync */
+};
+
+static struct inode_operations ftape_proc_inode_operations =
+{
+ &ftape_proc_fops,
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ NULL, /* readlink */
+ NULL, /* readpage */
+ NULL, /* writepage */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL, /* permission */
+};
+
+/*
+ * Proc filesystem directory entries.
+ */
+
+static int ftape_get_info(char *page, char **start, off_t off,
+ int count, int dummy)
+{
+ int dummy_eof;
+
+ return ftape_read_proc(page, start, off, count, &dummy_eof, NULL);
+}
+
+static struct proc_dir_entry proc_ftape = {
+ 0, /* low_ino */
+ sizeof("ftape")-1, /* namelen */
+ "ftape", /* name */
+ S_IFREG | S_IRUGO, /* mode */
+ 1, /* nlink */
+ 0, /* uid */
+ 0, /* gid */
+ 0, /* size */
+ &ftape_proc_inode_operations, /* ops */
+ ftape_get_info, /* get_info */
+ NULL, /* fill_inode */
+ NULL, /* next */
+ NULL, /* parent */
+ NULL, /* subdir */
+ NULL /* data */
+};
+
+/* Read ftape proc directory entry.
+ */
+
+#define PROC_BLOCK_SIZE PAGE_SIZE
+
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,0)
+static long ftape_proc_read(struct inode * inode, struct file * file,
+ char * buf, unsigned long nbytes)
+#else
+static int ftape_proc_read(struct inode * inode, struct file * file,
+ char * buf, int nbytes)
+#endif
+{
+ char *page;
+ int retval=0;
+ int eof=0;
+ int n, count;
+ char *start;
+ struct proc_dir_entry * dp;
+
+ if (nbytes < 0)
+ return -EINVAL;
+ dp = (struct proc_dir_entry *) inode->u.generic_ip;
+ if (!(page = (char*) __get_free_page(GFP_KERNEL)))
+ return -ENOMEM;
+
+ while ((nbytes > 0) && !eof)
+ {
+ count = PROC_BLOCK_SIZE <= nbytes ? PROC_BLOCK_SIZE : nbytes;
+
+ start = NULL;
+ if (dp->get_info) {
+ /*
+ * Handle backwards compatibility with the old net
+ * routines.
+ *
+ * XXX What gives with the file->f_flags & O_ACCMODE
+ * test? Seems stupid to me....
+ */
+ n = dp->get_info(page, &start, file->f_pos, count,
+ (file->f_flags & O_ACCMODE) == O_RDWR);
+ if (n < count)
+ eof = 1;
+ } else
+ break;
+
+ if (!start) {
+ /*
+ * For proc files that are less than 4k
+ */
+ start = page + file->f_pos;
+ n -= file->f_pos;
+ if (n <= 0)
+ break;
+ if (n > count)
+ n = count;
+ }
+ if (n == 0)
+ break; /* End of file */
+ if (n < 0) {
+ if (retval == 0)
+ retval = n;
+ break;
+ }
+#if LINUX_VERSION_CODE > KERNEL_VER(2,1,3)
+ copy_to_user(buf, start, n);
+#else
+ memcpy_tofs(buf, start, n);
+#endif
+ file->f_pos += n; /* Move down the file */
+ nbytes -= n;
+ buf += n;
+ retval += n;
+ }
+ free_page((unsigned long) page);
+ return retval;
+}
+
+#else /* LINUX_VERSION_CODE < KERNEL_VER(2,1,28) */
+
+#define FT_PROC_REGISTER(parent, child) proc_register(parent, child)
+
+/*
+ * Proc filesystem directory entries.
+ */
+
+static struct proc_dir_entry proc_ftape = {
+ 0, /* low_ino */
+ sizeof("ftape")-1, /* namelen */
+ "ftape", /* name */
+ S_IFREG | S_IRUGO, /* mode */
+ 1, /* nlink */
+ 0, /* uid */
+ 0, /* gid */
+ 0, /* size */
+ NULL, /* ops */
+ NULL, /* get_info */
+ NULL, /* fill_inode */
+ NULL, /* next */
+ NULL, /* parent */
+ NULL, /* subdir */
+ NULL, /* data */
+ ftape_read_proc, /* read_proc */
+ NULL /* write_proc */
+};
+
+#endif
+
+static size_t get_driver_info(char *buf)
+{
+ const char *debug_level[] = { "bugs" ,
+ "errors",
+ "warnings",
+ "informational",
+ "noisy",
+ "program flow",
+ "fdc and dma",
+ "data flow",
+ "anything" };
+
+ return sprintf(buf,
+ "version : %s\n"
+ "used data rate: %d kbit/sec\n"
+ "dma memory : %d kb\n"
+ "debug messages: %s\n",
+ FTAPE_VERSION,
+ ft_data_rate,
+ FT_BUFF_SIZE * ft_nr_buffers >> 10,
+ debug_level[TRACE_LEVEL]);
+}
+
+static size_t get_tapedrive_info(char *buf)
+{
+ return sprintf(buf,
+ "vendor id : 0x%04x\n"
+ "drive name: %s\n"
+ "wind speed: %d ips\n"
+ "wakeup : %s\n"
+ "max. rate : %d kbit/sec\n",
+ ft_drive_type.vendor_id,
+ ft_drive_type.name,
+ ft_drive_type.speed,
+ ((ft_drive_type.wake_up == no_wake_up)
+ ? "No wakeup needed" :
+ ((ft_drive_type.wake_up == wake_up_colorado)
+ ? "Colorado" :
+ ((ft_drive_type.wake_up == wake_up_mountain)
+ ? "Mountain" :
+ ((ft_drive_type.wake_up == wake_up_insight)
+ ? "Motor on" :
+ "Unknown")))),
+ ft_drive_max_rate);
+}
+
+static size_t get_cartridge_info(char *buf)
+{
+ if (ftape_init_drive_needed) {
+ return sprintf(buf, "uninitialized\n");
+ }
+ if (ft_no_tape) {
+ return sprintf(buf, "no cartridge inserted\n");
+ }
+ return sprintf(buf,
+ "segments : %5d\n"
+ "tracks : %5d\n"
+ "length : %5dft\n"
+ "formatted : %3s\n"
+ "writable : %3s\n"
+ "QIC spec. : QIC-%s\n"
+ "fmt-code : %1d\n",
+ ft_segments_per_track,
+ ft_tracks_per_tape,
+ ftape_tape_len,
+ (ft_formatted == 1) ? "yes" : "no",
+ (ft_write_protected == 1) ? "no" : "yes",
+ ((ft_qic_std == QIC_TAPE_QIC40) ? "40" :
+ ((ft_qic_std == QIC_TAPE_QIC80) ? "80" :
+ ((ft_qic_std == QIC_TAPE_QIC3010) ? "3010" :
+ ((ft_qic_std == QIC_TAPE_QIC3020) ? "3020" :
+ "???")))),
+ ft_format_code);
+}
+
+static size_t get_controller_info(char *buf)
+{
+ const char *fdc_name[] = { "no fdc",
+ "i8272",
+ "i82077",
+ "i82077AA",
+ "Colorado FC-10 or FC-20",
+ "i82078",
+ "i82078_1" };
+
+ return sprintf(buf,
+ "FDC type : %s\n"
+ "FDC base : 0x%03x\n"
+ "FDC irq : %d\n"
+ "FDC dma : %d\n"
+ "FDC thr. : %d\n"
+ "max. rate : %d kbit/sec\n",
+ ft_mach2 ? "Mountain MACH-2" : fdc_name[fdc.type],
+ fdc.sra, fdc.irq, fdc.dma,
+ ft_fdc_threshold, ft_fdc_max_rate);
+}
+
+static size_t get_history_info(char *buf)
+{
+ size_t len;
+
+ len = sprintf(buf,
+ "\nFDC isr statistics\n"
+ " id_am_errors : %3d\n"
+ " id_crc_errors : %3d\n"
+ " data_am_errors : %3d\n"
+ " data_crc_errors : %3d\n"
+ " overrun_errors : %3d\n"
+ " no_data_errors : %3d\n"
+ " retries : %3d\n",
+ ft_history.id_am_errors, ft_history.id_crc_errors,
+ ft_history.data_am_errors, ft_history.data_crc_errors,
+ ft_history.overrun_errors, ft_history.no_data_errors,
+ ft_history.retries);
+ len += sprintf(buf + len,
+ "\nECC statistics\n"
+ " crc_errors : %3d\n"
+ " crc_failures : %3d\n"
+ " ecc_failures : %3d\n"
+ " sectors corrected: %3d\n",
+ ft_history.crc_errors, ft_history.crc_failures,
+ ft_history.ecc_failures, ft_history.corrected);
+ len += sprintf(buf + len,
+ "\ntape quality statistics\n"
+ " media defects : %3d\n",
+ ft_history.defects);
+ len += sprintf(buf + len,
+ "\ntape motion statistics\n"
+ " repositions : %3d\n",
+ ft_history.rewinds);
+ return len;
+}
+
+int ftape_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ char *ptr = page;
+ size_t len;
+
+ ptr += sprintf(ptr, "Kernel Driver\n\n");
+ ptr += get_driver_info(ptr);
+ ptr += sprintf(ptr, "\nTape Drive\n\n");
+ ptr += get_tapedrive_info(ptr);
+ ptr += sprintf(ptr, "\nFDC Controller\n\n");
+ ptr += get_controller_info(ptr);
+ ptr += sprintf(ptr, "\nTape Cartridge\n\n");
+ ptr += get_cartridge_info(ptr);
+ ptr += sprintf(ptr, "\nHistory Record\n\n");
+ ptr += get_history_info(ptr);
+
+ len = strlen(page);
+ *start = 0;
+ if (off+count >= len) {
+ *eof = 1;
+ } else {
+ *eof = 0;
+ }
+ return len;
+}
+
+__initfunc(int ftape_proc_init(void))
+{
+ return FT_PROC_REGISTER(&proc_root, &proc_ftape);
+}
+
+#ifdef MODULE
+void ftape_proc_destroy(void)
+{
+ proc_unregister(&proc_root, proc_ftape.low_ino);
+}
+#endif
+
+#endif /* defined(CONFIG_PROC_FS) && defined(CONFIG_FT_PROC_FS) */
diff --git a/drivers/char/ftape/lowlevel/ftape-proc.h b/drivers/char/ftape/lowlevel/ftape-proc.h
new file mode 100644
index 000000000..5673d5a41
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/ftape-proc.h
@@ -0,0 +1,37 @@
+#ifndef _FTAPE_PROC_H
+#define _FTAPE_PROC_H
+
+/*
+ * Copyright (C) 1997 Claus-Justus Heine
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-proc.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:20 $
+ *
+ * This file contains definitions for the procfs interface of the
+ * QIC-40/80/3010/3020 floppy-tape driver "ftape" for Linux.
+ */
+
+#include <linux/proc_fs.h>
+
+extern struct proc_dir_entry proc_ftape;
+
+extern int ftape_proc_init(void);
+extern void ftape_proc_destroy(void);
+
+#endif
diff --git a/drivers/char/ftape/lowlevel/ftape-read.c b/drivers/char/ftape/lowlevel/ftape-read.c
new file mode 100644
index 000000000..e67f099fe
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/ftape-read.c
@@ -0,0 +1,614 @@
+/*
+ * Copyright (C) 1993-1996 Bas Laarhoven,
+ * (C) 1996-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-read.c,v $
+ * $Revision: 1.6 $
+ * $Date: 1997/10/21 14:39:22 $
+ *
+ * This file contains the reading code
+ * for the QIC-117 floppy-tape driver for Linux.
+ *
+ */
+
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <asm/segment.h>
+
+#include <linux/ftape.h>
+#include <linux/qic117.h>
+#include "../lowlevel/ftape-tracing.h"
+#include "../lowlevel/ftape-read.h"
+#include "../lowlevel/ftape-io.h"
+#include "../lowlevel/ftape-ctl.h"
+#include "../lowlevel/ftape-rw.h"
+#include "../lowlevel/ftape-write.h"
+#include "../lowlevel/ftape-ecc.h"
+#include "../lowlevel/ftape-bsm.h"
+
+/* Global vars.
+ */
+
+/* Local vars.
+ */
+
+void ftape_zap_read_buffers(void)
+{
+ int i;
+
+ for (i = 0; i < ft_nr_buffers; ++i) {
+/* changed to "fit" with dynamic allocation of tape_buffer. --khp */
+ ft_buffer[i]->status = waiting;
+ ft_buffer[i]->bytes = 0;
+ ft_buffer[i]->skip = 0;
+ ft_buffer[i]->retry = 0;
+ }
+/* ftape_reset_buffer(); */
+}
+
+static SectorMap convert_sector_map(buffer_struct * buff)
+{
+ int i = 0;
+ SectorMap bad_map = ftape_get_bad_sector_entry(buff->segment_id);
+ SectorMap src_map = buff->soft_error_map | buff->hard_error_map;
+ SectorMap dst_map = 0;
+ TRACE_FUN(ft_t_any);
+
+ if (bad_map || src_map) {
+ TRACE(ft_t_flow, "bad_map = 0x%08lx", (long) bad_map);
+ TRACE(ft_t_flow, "src_map = 0x%08lx", (long) src_map);
+ }
+ while (bad_map) {
+ while ((bad_map & 1) == 0) {
+ if (src_map & 1) {
+ dst_map |= (1 << i);
+ }
+ src_map >>= 1;
+ bad_map >>= 1;
+ ++i;
+ }
+ /* (bad_map & 1) == 1 */
+ src_map >>= 1;
+ bad_map >>= 1;
+ }
+ if (src_map) {
+ dst_map |= (src_map << i);
+ }
+ if (dst_map) {
+ TRACE(ft_t_flow, "dst_map = 0x%08lx", (long) dst_map);
+ }
+ TRACE_EXIT dst_map;
+}
+
+static int correct_and_copy_fraction(buffer_struct *buff, __u8 * destination,
+ int start, int size)
+{
+ struct memory_segment mseg;
+ int result;
+ SectorMap read_bad;
+ TRACE_FUN(ft_t_any);
+
+ mseg.read_bad = convert_sector_map(buff);
+ mseg.marked_bad = 0; /* not used... */
+ mseg.blocks = buff->bytes / FT_SECTOR_SIZE;
+ mseg.data = buff->address;
+ /* If there are no data sectors we can skip this segment.
+ */
+ if (mseg.blocks <= 3) {
+ TRACE_ABORT(0, ft_t_noise, "empty segment");
+ }
+ read_bad = mseg.read_bad;
+ ft_history.crc_errors += count_ones(read_bad);
+ result = ftape_ecc_correct_data(&mseg);
+ if (read_bad != 0 || mseg.corrected != 0) {
+ TRACE(ft_t_noise, "crc error map: 0x%08lx", (unsigned long)read_bad);
+ TRACE(ft_t_noise, "corrected map: 0x%08lx", (unsigned long)mseg.corrected);
+ ft_history.corrected += count_ones(mseg.corrected);
+ }
+ if (result == ECC_CORRECTED || result == ECC_OK) {
+ if (result == ECC_CORRECTED) {
+ TRACE(ft_t_info, "ecc corrected segment: %d", buff->segment_id);
+ }
+ if(start < 0) {
+ start= 0;
+ }
+ if((start+size) > ((mseg.blocks - 3) * FT_SECTOR_SIZE)) {
+ size = (mseg.blocks - 3) * FT_SECTOR_SIZE - start;
+ }
+ if (size < 0) {
+ size= 0;
+ }
+ if(size > 0) {
+ memcpy(destination + start, mseg.data + start, size);
+ }
+ if ((read_bad ^ mseg.corrected) & mseg.corrected) {
+ /* sectors corrected without crc errors set */
+ ft_history.crc_failures++;
+ }
+ TRACE_EXIT size; /* (mseg.blocks - 3) * FT_SECTOR_SIZE; */
+ } else {
+ ft_history.ecc_failures++;
+ TRACE_ABORT(-EAGAIN,
+ ft_t_err, "ecc failure on segment %d",
+ buff->segment_id);
+ }
+ TRACE_EXIT 0;
+}
+
+/* Read given segment into buffer at address.
+ */
+int ftape_read_segment_fraction(const int segment_id,
+ void *address,
+ const ft_read_mode_t read_mode,
+ const int start,
+ const int size)
+{
+ int result = 0;
+ int retry = 0;
+ int bytes_read = 0;
+ int read_done = 0;
+ TRACE_FUN(ft_t_flow);
+
+ ft_history.used |= 1;
+ TRACE(ft_t_data_flow, "segment_id = %d", segment_id);
+ if (ft_driver_state != reading) {
+ TRACE(ft_t_noise, "calling ftape_abort_operation");
+ TRACE_CATCH(ftape_abort_operation(),);
+ ftape_set_state(reading);
+ }
+ for(;;) {
+ buffer_struct *tail;
+ /* Allow escape from this loop on signal !
+ */
+ FT_SIGNAL_EXIT(_DONT_BLOCK);
+ /* Search all full buffers for the first matching the
+ * wanted segment. Clear other buffers on the fly.
+ */
+ tail = ftape_get_buffer(ft_queue_tail);
+ while (!read_done && tail->status == done) {
+ /* Allow escape from this loop on signal !
+ */
+ FT_SIGNAL_EXIT(_DONT_BLOCK);
+ if (tail->segment_id == segment_id) {
+ /* If out buffer is already full,
+ * return its contents.
+ */
+ TRACE(ft_t_flow, "found segment in cache: %d",
+ segment_id);
+ if (tail->deleted) {
+ /* Return a value that
+ * read_header_segment
+ * understands. As this
+ * should only occur when
+ * searching for the header
+ * segments it shouldn't be
+ * misinterpreted elsewhere.
+ */
+ TRACE_EXIT 0;
+ }
+ result = correct_and_copy_fraction(
+ tail,
+ address,
+ start,
+ size);
+ TRACE(ft_t_flow, "segment contains (bytes): %d",
+ result);
+ if (result < 0) {
+ if (result != -EAGAIN) {
+ TRACE_EXIT result;
+ }
+ /* keep read_done == 0, will
+ * trigger
+ * ftape_abort_operation
+ * because reading wrong
+ * segment.
+ */
+ TRACE(ft_t_err, "ecc failed, retry");
+ ++retry;
+ } else {
+ read_done = 1;
+ bytes_read = result;
+ }
+ } else {
+ TRACE(ft_t_flow,"zapping segment in cache: %d",
+ tail->segment_id);
+ }
+ tail->status = waiting;
+ tail = ftape_next_buffer(ft_queue_tail);
+ }
+ if (!read_done && tail->status == reading) {
+ if (tail->segment_id == segment_id) {
+ switch(ftape_wait_segment(reading)) {
+ case 0:
+ break;
+ case -EINTR:
+ TRACE_ABORT(-EINTR, ft_t_warn,
+ "interrupted by "
+ "non-blockable signal");
+ break;
+ default:
+ TRACE(ft_t_noise,
+ "wait_segment failed");
+ ftape_abort_operation();
+ ftape_set_state(reading);
+ break;
+ }
+ } else {
+ /* We're reading the wrong segment,
+ * stop runner.
+ */
+ TRACE(ft_t_noise, "reading wrong segment");
+ ftape_abort_operation();
+ ftape_set_state(reading);
+ }
+ }
+ /* should runner stop ?
+ */
+ if (ft_runner_status == aborting) {
+ buffer_struct *head = ftape_get_buffer(ft_queue_head);
+ switch(head->status) {
+ case error:
+ ft_history.defects +=
+ count_ones(head->hard_error_map);
+ case reading:
+ head->status = waiting;
+ break;
+ default:
+ break;
+ }
+ TRACE_CATCH(ftape_dumb_stop(),);
+ } else {
+ /* If just passed last segment on tape: wait
+ * for BOT or EOT mark. Sets ft_runner_status to
+ * idle if at lEOT and successful
+ */
+ TRACE_CATCH(ftape_handle_logical_eot(),);
+ }
+ /* If we got a segment: quit, or else retry up to limit.
+ *
+ * If segment to read is empty, do not start runner for it,
+ * but wait for next read call.
+ */
+ if (read_done ||
+ ftape_get_bad_sector_entry(segment_id) == EMPTY_SEGMENT ) {
+ /* bytes_read = 0; should still be zero */
+ TRACE_EXIT bytes_read;
+
+ }
+ if (retry > FT_RETRIES_ON_ECC_ERROR) {
+ ft_history.defects++;
+ TRACE_ABORT(-ENODATA, ft_t_err,
+ "too many retries on ecc failure");
+ }
+ /* Now at least one buffer is empty !
+ * Restart runner & tape if needed.
+ */
+ TRACE(ft_t_any, "head: %d, tail: %d, ft_runner_status: %d",
+ ftape_buffer_id(ft_queue_head),
+ ftape_buffer_id(ft_queue_tail),
+ ft_runner_status);
+ TRACE(ft_t_any, "buffer[].status, [head]: %d, [tail]: %d",
+ ftape_get_buffer(ft_queue_head)->status,
+ ftape_get_buffer(ft_queue_tail)->status);
+ tail = ftape_get_buffer(ft_queue_tail);
+ if (tail->status == waiting) {
+ buffer_struct *head = ftape_get_buffer(ft_queue_head);
+
+ ftape_setup_new_segment(head, segment_id, -1);
+ if (read_mode == FT_RD_SINGLE) {
+ /* disable read-ahead */
+ head->next_segment = 0;
+ }
+ ftape_calc_next_cluster(head);
+ if (ft_runner_status == idle) {
+ result = ftape_start_tape(segment_id,
+ head->sector_offset);
+ if (result < 0) {
+ TRACE_ABORT(result, ft_t_err, "Error: "
+ "segment %d unreachable",
+ segment_id);
+ }
+ }
+ head->status = reading;
+ fdc_setup_read_write(head, FDC_READ);
+ }
+ }
+ /* not reached */
+ TRACE_EXIT -EIO;
+}
+
+int ftape_read_header_segment(__u8 *address)
+{
+ int result;
+ int header_segment;
+ int first_failed = 0;
+ int status;
+ TRACE_FUN(ft_t_flow);
+
+ ft_used_header_segment = -1;
+ TRACE_CATCH(ftape_report_drive_status(&status),);
+ TRACE(ft_t_flow, "reading...");
+ /* We're looking for the first header segment.
+ * A header segment cannot contain bad sectors, therefor at the
+ * tape start, segments with bad sectors are (according to QIC-40/80)
+ * written with deleted data marks and must be skipped.
+ */
+ memset(address, '\0', (FT_SECTORS_PER_SEGMENT - 3) * FT_SECTOR_SIZE);
+ result = 0;
+#define HEADER_SEGMENT_BOUNDARY 68 /* why not 42? */
+ for (header_segment = 0;
+ header_segment < HEADER_SEGMENT_BOUNDARY && result == 0;
+ ++header_segment) {
+ /* Set no read-ahead, the isr will force read-ahead whenever
+ * it encounters deleted data !
+ */
+ result = ftape_read_segment(header_segment,
+ address,
+ FT_RD_SINGLE);
+ if (result < 0 && !first_failed) {
+ TRACE(ft_t_err, "header segment damaged, trying backup");
+ first_failed = 1;
+ result = 0; /* force read of next (backup) segment */
+ }
+ }
+ if (result < 0 || header_segment >= HEADER_SEGMENT_BOUNDARY) {
+ TRACE_ABORT(-EIO, ft_t_err,
+ "no readable header segment found");
+ }
+ TRACE_CATCH(ftape_abort_operation(),);
+ ft_used_header_segment = header_segment;
+ result = ftape_decode_header_segment(address);
+ TRACE_EXIT result;
+}
+
+int ftape_decode_header_segment(__u8 *address)
+{
+ unsigned int max_floppy_side;
+ unsigned int max_floppy_track;
+ unsigned int max_floppy_sector;
+ unsigned int new_tape_len;
+ TRACE_FUN(ft_t_flow);
+
+ if (GET4(address, FT_SIGNATURE) == FT_D2G_MAGIC) {
+ /* Ditto 2GB header segment. They encrypt the bad sector map.
+ * We decrypt it and store them in normal format.
+ * I hope this is correct.
+ */
+ int i;
+ TRACE(ft_t_warn,
+ "Found Ditto 2GB tape, "
+ "trying to decrypt bad sector map");
+ for (i=256; i < 29 * FT_SECTOR_SIZE; i++) {
+ address[i] = ~(address[i] - (i&0xff));
+ }
+ PUT4(address, 0,FT_HSEG_MAGIC);
+ } else if (GET4(address, FT_SIGNATURE) != FT_HSEG_MAGIC) {
+ TRACE_ABORT(-EIO, ft_t_err,
+ "wrong signature in header segment");
+ }
+ ft_format_code = (ft_format_type) address[FT_FMT_CODE];
+ if (ft_format_code != fmt_big) {
+ ft_header_segment_1 = GET2(address, FT_HSEG_1);
+ ft_header_segment_2 = GET2(address, FT_HSEG_2);
+ ft_first_data_segment = GET2(address, FT_FRST_SEG);
+ ft_last_data_segment = GET2(address, FT_LAST_SEG);
+ } else {
+ ft_header_segment_1 = GET4(address, FT_6_HSEG_1);
+ ft_header_segment_2 = GET4(address, FT_6_HSEG_2);
+ ft_first_data_segment = GET4(address, FT_6_FRST_SEG);
+ ft_last_data_segment = GET4(address, FT_6_LAST_SEG);
+ }
+ TRACE(ft_t_noise, "first data segment: %d", ft_first_data_segment);
+ TRACE(ft_t_noise, "last data segment: %d", ft_last_data_segment);
+ TRACE(ft_t_noise, "header segments are %d and %d",
+ ft_header_segment_1, ft_header_segment_2);
+
+ /* Verify tape parameters...
+ * QIC-40/80 spec: tape_parameters:
+ *
+ * segments-per-track segments_per_track
+ * tracks-per-cartridge tracks_per_tape
+ * max-floppy-side (segments_per_track *
+ * tracks_per_tape - 1) /
+ * ftape_segments_per_head
+ * max-floppy-track ftape_segments_per_head /
+ * ftape_segments_per_cylinder - 1
+ * max-floppy-sector ftape_segments_per_cylinder *
+ * FT_SECTORS_PER_SEGMENT
+ */
+ ft_segments_per_track = GET2(address, FT_SPT);
+ ft_tracks_per_tape = address[FT_TPC];
+ max_floppy_side = address[FT_FHM];
+ max_floppy_track = address[FT_FTM];
+ max_floppy_sector = address[FT_FSM];
+ TRACE(ft_t_noise, "(fmt/spt/tpc/fhm/ftm/fsm) = %d/%d/%d/%d/%d/%d",
+ ft_format_code, ft_segments_per_track, ft_tracks_per_tape,
+ max_floppy_side, max_floppy_track, max_floppy_sector);
+ new_tape_len = ftape_tape_len;
+ switch (ft_format_code) {
+ case fmt_425ft:
+ new_tape_len = 425;
+ break;
+ case fmt_normal:
+ if (ftape_tape_len == 0) { /* otherwise 307 ft */
+ new_tape_len = 205;
+ }
+ break;
+ case fmt_1100ft:
+ new_tape_len = 1100;
+ break;
+ case fmt_var:{
+ int segments_per_1000_inch = 1; /* non-zero default for switch */
+ switch (ft_qic_std) {
+ case QIC_TAPE_QIC40:
+ segments_per_1000_inch = 332;
+ break;
+ case QIC_TAPE_QIC80:
+ segments_per_1000_inch = 488;
+ break;
+ case QIC_TAPE_QIC3010:
+ segments_per_1000_inch = 730;
+ break;
+ case QIC_TAPE_QIC3020:
+ segments_per_1000_inch = 1430;
+ break;
+ }
+ new_tape_len = (1000 * ft_segments_per_track +
+ (segments_per_1000_inch - 1)) / segments_per_1000_inch;
+ break;
+ }
+ case fmt_big:{
+ int segments_per_1000_inch = 1; /* non-zero default for switch */
+ switch (ft_qic_std) {
+ case QIC_TAPE_QIC40:
+ segments_per_1000_inch = 332;
+ break;
+ case QIC_TAPE_QIC80:
+ segments_per_1000_inch = 488;
+ break;
+ case QIC_TAPE_QIC3010:
+ segments_per_1000_inch = 730;
+ break;
+ case QIC_TAPE_QIC3020:
+ segments_per_1000_inch = 1430;
+ break;
+ default:
+ TRACE_ABORT(-EIO, ft_t_bug,
+ "%x QIC-standard with fmt-code %d, please report",
+ ft_qic_std, ft_format_code);
+ }
+ new_tape_len = ((1000 * ft_segments_per_track +
+ (segments_per_1000_inch - 1)) /
+ segments_per_1000_inch);
+ break;
+ }
+ default:
+ TRACE_ABORT(-EIO, ft_t_err,
+ "unknown tape format, please report !");
+ }
+ if (new_tape_len != ftape_tape_len) {
+ ftape_tape_len = new_tape_len;
+ TRACE(ft_t_info, "calculated tape length is %d ft",
+ ftape_tape_len);
+ ftape_calc_timeouts(ft_qic_std, ft_data_rate, ftape_tape_len);
+ }
+ if (ft_segments_per_track == 0 && ft_tracks_per_tape == 0 &&
+ max_floppy_side == 0 && max_floppy_track == 0 &&
+ max_floppy_sector == 0) {
+ /* QIC-40 Rev E and earlier has no values in the header.
+ */
+ ft_segments_per_track = 68;
+ ft_tracks_per_tape = 20;
+ max_floppy_side = 1;
+ max_floppy_track = 169;
+ max_floppy_sector = 128;
+ }
+ /* This test will compensate for the wrong parameter on tapes
+ * formatted by Conner software.
+ */
+ if (ft_segments_per_track == 150 &&
+ ft_tracks_per_tape == 28 &&
+ max_floppy_side == 7 &&
+ max_floppy_track == 149 &&
+ max_floppy_sector == 128) {
+TRACE(ft_t_info, "the famous CONNER bug: max_floppy_side off by one !");
+ max_floppy_side = 6;
+ }
+ /* These tests will compensate for the wrong parameter on tapes
+ * formatted by ComByte Windows software.
+ *
+ * First, for 205 foot tapes
+ */
+ if (ft_segments_per_track == 100 &&
+ ft_tracks_per_tape == 28 &&
+ max_floppy_side == 9 &&
+ max_floppy_track == 149 &&
+ max_floppy_sector == 128) {
+TRACE(ft_t_info, "the ComByte bug: max_floppy_side incorrect!");
+ max_floppy_side = 4;
+ }
+ /* Next, for 307 foot tapes. */
+ if (ft_segments_per_track == 150 &&
+ ft_tracks_per_tape == 28 &&
+ max_floppy_side == 9 &&
+ max_floppy_track == 149 &&
+ max_floppy_sector == 128) {
+TRACE(ft_t_info, "the ComByte bug: max_floppy_side incorrect!");
+ max_floppy_side = 6;
+ }
+ /* This test will compensate for the wrong parameter on tapes
+ * formatted by Colorado Windows software.
+ */
+ if (ft_segments_per_track == 150 &&
+ ft_tracks_per_tape == 28 &&
+ max_floppy_side == 6 &&
+ max_floppy_track == 150 &&
+ max_floppy_sector == 128) {
+TRACE(ft_t_info, "the famous Colorado bug: max_floppy_track off by one !");
+ max_floppy_track = 149;
+ }
+ ftape_segments_per_head = ((max_floppy_sector/FT_SECTORS_PER_SEGMENT) *
+ (max_floppy_track + 1));
+ /* This test will compensate for some bug reported by Dima
+ * Brodsky. Seems to be a Colorado bug, either. (freebee
+ * Imation tape shipped together with Colorado T3000
+ */
+ if ((ft_format_code == fmt_var || ft_format_code == fmt_big) &&
+ ft_tracks_per_tape == 50 &&
+ max_floppy_side == 54 &&
+ max_floppy_track == 255 &&
+ max_floppy_sector == 128) {
+TRACE(ft_t_info, "the famous ??? bug: max_floppy_track off by one !");
+ max_floppy_track = 254;
+ }
+ /*
+ * Verify drive_configuration with tape parameters
+ */
+ if (ftape_segments_per_head == 0 || ftape_segments_per_cylinder == 0 ||
+ ((ft_segments_per_track * ft_tracks_per_tape - 1) / ftape_segments_per_head
+ != max_floppy_side) ||
+ (ftape_segments_per_head / ftape_segments_per_cylinder - 1 != max_floppy_track) ||
+ (ftape_segments_per_cylinder * FT_SECTORS_PER_SEGMENT != max_floppy_sector)
+#ifdef TESTING
+ || ((ft_format_code == fmt_var || ft_format_code == fmt_big) &&
+ (max_floppy_track != 254 || max_floppy_sector != 128))
+#endif
+ ) {
+ TRACE(ft_t_err,"Tape parameters inconsistency, please report");
+ TRACE(ft_t_err, "reported = %d/%d/%d/%d/%d/%d",
+ ft_format_code,
+ ft_segments_per_track,
+ ft_tracks_per_tape,
+ max_floppy_side,
+ max_floppy_track,
+ max_floppy_sector);
+ TRACE(ft_t_err, "required = %d/%d/%d/%d/%d/%d",
+ ft_format_code,
+ ft_segments_per_track,
+ ft_tracks_per_tape,
+ ((ft_segments_per_track * ft_tracks_per_tape -1) /
+ ftape_segments_per_head ),
+ (ftape_segments_per_head /
+ ftape_segments_per_cylinder - 1 ),
+ (ftape_segments_per_cylinder * FT_SECTORS_PER_SEGMENT));
+ TRACE_EXIT -EIO;
+ }
+ ftape_extract_bad_sector_map(address);
+ TRACE_EXIT 0;
+}
diff --git a/drivers/char/ftape/ftape-read.h b/drivers/char/ftape/lowlevel/ftape-read.h
index aeca35950..069f99f2a 100644
--- a/drivers/char/ftape/ftape-read.h
+++ b/drivers/char/ftape/lowlevel/ftape-read.h
@@ -2,7 +2,8 @@
#define _FTAPE_READ_H
/*
- * Copyright (C) 1994-1995 Bas Laarhoven.
+ * Copyright (C) 1994-1996 Bas Laarhoven,
+ * (C) 1996-1997 Claus-Justus Heine.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -19,27 +20,32 @@
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
- $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-read.h,v $
- $Author: bas $
- *
- $Revision: 1.13 $
- $Date: 1995/05/10 16:09:36 $
- $State: Beta $
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-read.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:22 $
*
* This file contains the definitions for the read functions
* for the QIC-117 floppy-tape driver for Linux.
*
*/
-/* ftape-read.c defined global vars.
- */
-
/* ftape-read.c defined global functions.
*/
-extern int _ftape_read(char *buff, int req_len);
-extern int read_header_segment(byte * address);
-extern int read_segment(unsigned segment, byte * address, int *eof_mark,
- int read_ahead);
+typedef enum {
+ FT_RD_SINGLE = 0,
+ FT_RD_AHEAD = 1,
+} ft_read_mode_t;
+
+extern int ftape_read_header_segment(__u8 *address);
+extern int ftape_decode_header_segment(__u8 *address);
+extern int ftape_read_segment_fraction(const int segment,
+ void *address,
+ const ft_read_mode_t read_mode,
+ const int start,
+ const int size);
+#define ftape_read_segment(segment, address, read_mode) \
+ ftape_read_segment_fraction(segment, address, read_mode, \
+ 0, FT_SEGMENT_SIZE)
extern void ftape_zap_read_buffers(void);
#endif /* _FTAPE_READ_H */
diff --git a/drivers/char/ftape/lowlevel/ftape-rw.c b/drivers/char/ftape/lowlevel/ftape-rw.c
new file mode 100644
index 000000000..e3e0243f0
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/ftape-rw.c
@@ -0,0 +1,1091 @@
+/*
+ * Copyright (C) 1993-1996 Bas Laarhoven,
+ * (C) 1996-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-rw.c,v $
+ * $Revision: 1.7 $
+ * $Date: 1997/10/28 14:26:49 $
+ *
+ * This file contains some common code for the segment read and
+ * segment write routines for the QIC-117 floppy-tape driver for
+ * Linux.
+ */
+
+#include <linux/string.h>
+#include <linux/errno.h>
+
+#include <linux/ftape.h>
+#include <linux/qic117.h>
+#include "../lowlevel/ftape-tracing.h"
+#include "../lowlevel/ftape-rw.h"
+#include "../lowlevel/fdc-io.h"
+#include "../lowlevel/ftape-init.h"
+#include "../lowlevel/ftape-io.h"
+#include "../lowlevel/ftape-ctl.h"
+#include "../lowlevel/ftape-read.h"
+#include "../lowlevel/ftape-ecc.h"
+#include "../lowlevel/ftape-bsm.h"
+
+/* Global vars.
+ */
+int ft_nr_buffers = 0;
+buffer_struct *ft_buffer[FT_MAX_NR_BUFFERS] = {NULL, };
+static volatile int ft_head;
+static volatile int ft_tail; /* not volatile but need same type as head */
+int fdc_setup_error;
+location_record ft_location = {-1, 0};
+volatile int ftape_tape_running = 0;
+
+/* Local vars.
+ */
+static int overrun_count_offset = 0;
+static int inhibit_correction = 0;
+
+/* maxmimal allowed overshoot when fast seeking
+ */
+#define OVERSHOOT_LIMIT 10
+
+/* Increment cyclic buffer nr.
+ */
+buffer_struct *ftape_next_buffer(ft_buffer_queue_t pos)
+{
+ switch (pos) {
+ case ft_queue_head:
+ if (++ft_head >= ft_nr_buffers) {
+ ft_head = 0;
+ }
+ return ft_buffer[ft_head];
+ case ft_queue_tail:
+ if (++ft_tail >= ft_nr_buffers) {
+ ft_tail = 0;
+ }
+ return ft_buffer[ft_tail];
+ default:
+ return NULL;
+ }
+}
+int ftape_buffer_id(ft_buffer_queue_t pos)
+{
+ switch(pos) {
+ case ft_queue_head: return ft_head;
+ case ft_queue_tail: return ft_tail;
+ default: return -1;
+ }
+}
+buffer_struct *ftape_get_buffer(ft_buffer_queue_t pos)
+{
+ switch(pos) {
+ case ft_queue_head: return ft_buffer[ft_head];
+ case ft_queue_tail: return ft_buffer[ft_tail];
+ default: return NULL;
+ }
+}
+void ftape_reset_buffer(void)
+{
+ ft_head = ft_tail = 0;
+}
+
+buffer_state_enum ftape_set_state(buffer_state_enum new_state)
+{
+ buffer_state_enum old_state = ft_driver_state;
+
+ ft_driver_state = new_state;
+ return old_state;
+}
+/* Calculate Floppy Disk Controller and DMA parameters for a segment.
+ * head: selects buffer struct in array.
+ * offset: number of physical sectors to skip (including bad ones).
+ * count: number of physical sectors to handle (including bad ones).
+ */
+static int setup_segment(buffer_struct * buff,
+ int segment_id,
+ unsigned int sector_offset,
+ unsigned int sector_count,
+ int retry)
+{
+ SectorMap offset_mask;
+ SectorMap mask;
+ TRACE_FUN(ft_t_any);
+
+ buff->segment_id = segment_id;
+ buff->sector_offset = sector_offset;
+ buff->remaining = sector_count;
+ buff->head = segment_id / ftape_segments_per_head;
+ buff->cyl = (segment_id % ftape_segments_per_head) / ftape_segments_per_cylinder;
+ buff->sect = (segment_id % ftape_segments_per_cylinder) * FT_SECTORS_PER_SEGMENT + 1;
+ buff->deleted = 0;
+ offset_mask = (1 << buff->sector_offset) - 1;
+ mask = ftape_get_bad_sector_entry(segment_id) & offset_mask;
+ while (mask) {
+ if (mask & 1) {
+ offset_mask >>= 1; /* don't count bad sector */
+ }
+ mask >>= 1;
+ }
+ buff->data_offset = count_ones(offset_mask); /* good sectors to skip */
+ buff->ptr = buff->address + buff->data_offset * FT_SECTOR_SIZE;
+ TRACE(ft_t_flow, "data offset = %d sectors", buff->data_offset);
+ if (retry) {
+ buff->soft_error_map &= offset_mask; /* keep skipped part */
+ } else {
+ buff->hard_error_map = buff->soft_error_map = 0;
+ }
+ buff->bad_sector_map = ftape_get_bad_sector_entry(buff->segment_id);
+ if (buff->bad_sector_map != 0) {
+ TRACE(ft_t_noise, "segment: %d, bad sector map: %08lx",
+ buff->segment_id, (long)buff->bad_sector_map);
+ } else {
+ TRACE(ft_t_flow, "segment: %d", buff->segment_id);
+ }
+ if (buff->sector_offset > 0) {
+ buff->bad_sector_map >>= buff->sector_offset;
+ }
+ if (buff->sector_offset != 0 || buff->remaining != FT_SECTORS_PER_SEGMENT) {
+ TRACE(ft_t_flow, "sector offset = %d, count = %d",
+ buff->sector_offset, buff->remaining);
+ }
+ /* Segments with 3 or less sectors are not written with valid
+ * data because there is no space left for the ecc. The
+ * data written is whatever happens to be in the buffer.
+ * Reading such a segment will return a zero byte-count.
+ * To allow us to read/write segments with all bad sectors
+ * we fake one readable sector in the segment. This
+ * prevents having to handle these segments in a very
+ * special way. It is not important if the reading of this
+ * bad sector fails or not (the data is ignored). It is
+ * only read to keep the driver running.
+ *
+ * The QIC-40/80 spec. has no information on how to handle
+ * this case, so this is my interpretation.
+ */
+ if (buff->bad_sector_map == EMPTY_SEGMENT) {
+ TRACE(ft_t_flow, "empty segment %d, fake first sector good",
+ buff->segment_id);
+ if (buff->ptr != buff->address) {
+ TRACE(ft_t_bug, "This is a bug: %p/%p",
+ buff->ptr, buff->address);
+ }
+ buff->bad_sector_map = FAKE_SEGMENT;
+ }
+ fdc_setup_error = 0;
+ buff->next_segment = segment_id + 1;
+ TRACE_EXIT 0;
+}
+
+/* Calculate Floppy Disk Controller and DMA parameters for a new segment.
+ */
+int ftape_setup_new_segment(buffer_struct * buff, int segment_id, int skip)
+{
+ int result = 0;
+ static int old_segment_id = -1;
+ static buffer_state_enum old_ft_driver_state = idle;
+ int retry = 0;
+ unsigned offset = 0;
+ int count = FT_SECTORS_PER_SEGMENT;
+ TRACE_FUN(ft_t_flow);
+
+ TRACE(ft_t_flow, "%s segment %d (old = %d)",
+ (ft_driver_state == reading || ft_driver_state == verifying)
+ ? "reading" : "writing",
+ segment_id, old_segment_id);
+ if (ft_driver_state != old_ft_driver_state) { /* when verifying */
+ old_segment_id = -1;
+ old_ft_driver_state = ft_driver_state;
+ }
+ if (segment_id == old_segment_id) {
+ ++buff->retry;
+ ++ft_history.retries;
+ TRACE(ft_t_flow, "setting up for retry nr %d", buff->retry);
+ retry = 1;
+ if (skip && buff->skip > 0) { /* allow skip on retry */
+ offset = buff->skip;
+ count -= offset;
+ TRACE(ft_t_flow, "skipping %d sectors", offset);
+ }
+ } else {
+ buff->retry = 0;
+ buff->skip = 0;
+ old_segment_id = segment_id;
+ }
+ result = setup_segment(buff, segment_id, offset, count, retry);
+ TRACE_EXIT result;
+}
+
+/* Determine size of next cluster of good sectors.
+ */
+int ftape_calc_next_cluster(buffer_struct * buff)
+{
+ /* Skip bad sectors.
+ */
+ while (buff->remaining > 0 && (buff->bad_sector_map & 1) != 0) {
+ buff->bad_sector_map >>= 1;
+ ++buff->sector_offset;
+ --buff->remaining;
+ }
+ /* Find next cluster of good sectors
+ */
+ if (buff->bad_sector_map == 0) { /* speed up */
+ buff->sector_count = buff->remaining;
+ } else {
+ SectorMap map = buff->bad_sector_map;
+
+ buff->sector_count = 0;
+ while (buff->sector_count < buff->remaining && (map & 1) == 0) {
+ ++buff->sector_count;
+ map >>= 1;
+ }
+ }
+ return buff->sector_count;
+}
+
+/* if just passed the last segment on a track, wait for BOT
+ * or EOT mark.
+ */
+int ftape_handle_logical_eot(void)
+{
+ TRACE_FUN(ft_t_flow);
+
+ if (ft_runner_status == logical_eot) {
+ int status;
+
+ TRACE(ft_t_noise, "tape at logical EOT");
+ TRACE_CATCH(ftape_ready_wait(ftape_timeout.seek, &status),);
+ if ((status & (QIC_STATUS_AT_BOT | QIC_STATUS_AT_EOT)) == 0) {
+ TRACE_ABORT(-EIO, ft_t_err, "eot/bot not reached");
+ }
+ ft_runner_status = end_of_tape;
+ }
+ if (ft_runner_status == end_of_tape) {
+ TRACE(ft_t_noise, "runner stopped because of logical EOT");
+ ft_runner_status = idle;
+ }
+ TRACE_EXIT 0;
+}
+
+static int check_bot_eot(int status)
+{
+ TRACE_FUN(ft_t_flow);
+
+ if (status & (QIC_STATUS_AT_BOT | QIC_STATUS_AT_EOT)) {
+ ft_location.bot = ((ft_location.track & 1) == 0 ?
+ (status & QIC_STATUS_AT_BOT) != 0:
+ (status & QIC_STATUS_AT_EOT) != 0);
+ ft_location.eot = !ft_location.bot;
+ ft_location.segment = (ft_location.track +
+ (ft_location.bot ? 0 : 1)) * ft_segments_per_track - 1;
+ ft_location.sector = -1;
+ ft_location.known = 1;
+ TRACE(ft_t_flow, "tape at logical %s",
+ ft_location.bot ? "bot" : "eot");
+ TRACE(ft_t_flow, "segment = %d", ft_location.segment);
+ } else {
+ ft_location.known = 0;
+ }
+ TRACE_EXIT ft_location.known;
+}
+
+/* Read Id of first sector passing tape head.
+ */
+int ftape_read_id(void)
+{
+ int status;
+ __u8 out[2];
+ TRACE_FUN(ft_t_any);
+
+ /* Assume tape is running on entry, be able to handle
+ * situation where it stopped or is stopping.
+ */
+ ft_location.known = 0; /* default is location not known */
+ out[0] = FDC_READID;
+ out[1] = ft_drive_sel;
+ TRACE_CATCH(fdc_command(out, 2),);
+ switch (fdc_interrupt_wait(20 * FT_SECOND)) {
+ case 0:
+ if (fdc_sect == 0) {
+ if (ftape_report_drive_status(&status) >= 0 &&
+ (status & QIC_STATUS_READY)) {
+ ftape_tape_running = 0;
+ TRACE(ft_t_flow, "tape has stopped");
+ check_bot_eot(status);
+ }
+ } else {
+ ft_location.known = 1;
+ ft_location.segment = (ftape_segments_per_head
+ * fdc_head
+ + ftape_segments_per_cylinder
+ * fdc_cyl
+ + (fdc_sect - 1)
+ / FT_SECTORS_PER_SEGMENT);
+ ft_location.sector = ((fdc_sect - 1)
+ % FT_SECTORS_PER_SEGMENT);
+ ft_location.eot = ft_location.bot = 0;
+ }
+ break;
+ case -ETIME:
+ /* Didn't find id on tape, must be near end: Wait
+ * until stopped.
+ */
+ if (ftape_ready_wait(FT_FOREVER, &status) >= 0) {
+ ftape_tape_running = 0;
+ TRACE(ft_t_flow, "tape has stopped");
+ check_bot_eot(status);
+ }
+ break;
+ default:
+ /* Interrupted or otherwise failing
+ * fdc_interrupt_wait()
+ */
+ TRACE(ft_t_err, "fdc_interrupt_wait failed");
+ break;
+ }
+ if (!ft_location.known) {
+ TRACE_ABORT(-EIO, ft_t_flow, "no id found");
+ }
+ if (ft_location.sector == 0) {
+ TRACE(ft_t_flow, "passing segment %d/%d",
+ ft_location.segment, ft_location.sector);
+ } else {
+ TRACE(ft_t_fdc_dma, "passing segment %d/%d",
+ ft_location.segment, ft_location.sector);
+ }
+ TRACE_EXIT 0;
+}
+
+static int logical_forward(void)
+{
+ ftape_tape_running = 1;
+ return ftape_command(QIC_LOGICAL_FORWARD);
+}
+
+int ftape_stop_tape(int *pstatus)
+{
+ int retry = 0;
+ int result;
+ TRACE_FUN(ft_t_flow);
+
+ do {
+ result = ftape_command_wait(QIC_STOP_TAPE,
+ ftape_timeout.stop, pstatus);
+ if (result == 0) {
+ if ((*pstatus & QIC_STATUS_READY) == 0) {
+ result = -EIO;
+ } else {
+ ftape_tape_running = 0;
+ }
+ }
+ } while (result < 0 && ++retry <= 3);
+ if (result < 0) {
+ TRACE(ft_t_err, "failed ! (fatal)");
+ }
+ TRACE_EXIT result;
+}
+
+int ftape_dumb_stop(void)
+{
+ int result;
+ int status;
+ TRACE_FUN(ft_t_flow);
+
+ /* Abort current fdc operation if it's busy (probably read
+ * or write operation pending) with a reset.
+ */
+ if (fdc_ready_wait(100 /* usec */) < 0) {
+ TRACE(ft_t_noise, "aborting fdc operation");
+ fdc_reset();
+ }
+ /* Reading id's after the last segment on a track may fail
+ * but eventually the drive will become ready (logical eot).
+ */
+ result = ftape_report_drive_status(&status);
+ ft_location.known = 0;
+ do {
+ if (result == 0 && status & QIC_STATUS_READY) {
+ /* Tape is not running any more.
+ */
+ TRACE(ft_t_noise, "tape already halted");
+ check_bot_eot(status);
+ ftape_tape_running = 0;
+ } else if (ftape_tape_running) {
+ /* Tape is (was) still moving.
+ */
+#ifdef TESTING
+ ftape_read_id();
+#endif
+ result = ftape_stop_tape(&status);
+ } else {
+ /* Tape not yet ready but stopped.
+ */
+ result = ftape_ready_wait(ftape_timeout.pause,&status);
+ }
+ } while (ftape_tape_running && (current->signal & _NEVER_BLOCK) == 0);
+#ifndef TESTING
+ ft_location.known = 0;
+#endif
+ if (ft_runner_status == aborting || ft_runner_status == do_abort) {
+ ft_runner_status = idle;
+ }
+ TRACE_EXIT result;
+}
+
+/* Wait until runner has finished tail buffer.
+ *
+ */
+int ftape_wait_segment(buffer_state_enum state)
+{
+ int status;
+ int result = 0;
+ TRACE_FUN(ft_t_flow);
+
+ while (ft_buffer[ft_tail]->status == state) {
+ TRACE(ft_t_flow, "state: %d", ft_buffer[ft_tail]->status);
+ /* First buffer still being worked on, wait up to timeout.
+ *
+ * Note: we check two times for being killed. 50
+ * seconds are quite long. Note that
+ * fdc_interrupt_wait() is not killable by any
+ * means. ftape_read_segment() wants us to return
+ * -EINTR in case of a signal.
+ */
+ FT_SIGNAL_EXIT(_DONT_BLOCK);
+ result = fdc_interrupt_wait(50 * FT_SECOND);
+ FT_SIGNAL_EXIT(_DONT_BLOCK);
+ if (result < 0) {
+ TRACE_ABORT(result,
+ ft_t_err, "fdc_interrupt_wait failed");
+ }
+ if (fdc_setup_error) {
+ /* recover... FIXME */
+ TRACE_ABORT(-EIO, ft_t_err, "setup error");
+ }
+ }
+ if (ft_buffer[ft_tail]->status != error) {
+ TRACE_EXIT 0;
+ }
+ TRACE_CATCH(ftape_report_drive_status(&status),);
+ TRACE(ft_t_noise, "ftape_report_drive_status: 0x%02x", status);
+ if ((status & QIC_STATUS_READY) &&
+ (status & QIC_STATUS_ERROR)) {
+ unsigned int error;
+ qic117_cmd_t command;
+
+ /* Report and clear error state.
+ * In case the drive can't operate at the selected
+ * rate, select the next lower data rate.
+ */
+ ftape_report_error(&error, &command, 1);
+ if (error == 31 && command == QIC_LOGICAL_FORWARD) {
+ /* drive does not accept this data rate */
+ if (ft_data_rate > 250) {
+ TRACE(ft_t_info,
+ "Probable data rate conflict");
+ TRACE(ft_t_info,
+ "Lowering data rate to %d Kbps",
+ ft_data_rate / 2);
+ ftape_half_data_rate();
+ if (ft_buffer[ft_tail]->retry > 0) {
+ /* give it a chance */
+ --ft_buffer[ft_tail]->retry;
+ }
+ } else {
+ /* no rate is accepted... */
+ TRACE(ft_t_err, "We're dead :(");
+ }
+ } else {
+ TRACE(ft_t_err, "Unknown error");
+ }
+ TRACE_EXIT -EIO; /* g.p. error */
+ }
+ TRACE_EXIT 0;
+}
+
+/* forward */ static int seek_forward(int segment_id, int fast);
+
+static int fast_seek(int count, int reverse)
+{
+ int result = 0;
+ int status;
+ TRACE_FUN(ft_t_flow);
+
+ if (count > 0) {
+ /* If positioned at begin or end of tape, fast seeking needs
+ * special treatment.
+ * Starting from logical bot needs a (slow) seek to the first
+ * segment before the high speed seek. Most drives do this
+ * automatically but some older don't, so we treat them
+ * all the same.
+ * Starting from logical eot is even more difficult because
+ * we cannot (slow) reverse seek to the last segment.
+ * TO BE IMPLEMENTED.
+ */
+ inhibit_correction = 0;
+ if (ft_location.known &&
+ ((ft_location.bot && !reverse) ||
+ (ft_location.eot && reverse))) {
+ if (!reverse) {
+ /* (slow) skip to first segment on a track
+ */
+ seek_forward(ft_location.track * ft_segments_per_track, 0);
+ --count;
+ } else {
+ /* When seeking backwards from
+ * end-of-tape the number of erased
+ * gaps found seems to be higher than
+ * expected. Therefor the drive must
+ * skip some more segments than
+ * calculated, but we don't know how
+ * many. Thus we will prevent the
+ * re-calculation of offset and
+ * overshoot when seeking backwards.
+ */
+ inhibit_correction = 1;
+ count += 3; /* best guess */
+ }
+ }
+ } else {
+ TRACE(ft_t_flow, "warning: zero or negative count: %d", count);
+ }
+ if (count > 0) {
+ int i;
+ int nibbles = count > 255 ? 3 : 2;
+
+ if (count > 4095) {
+ TRACE(ft_t_noise, "skipping clipped at 4095 segment");
+ count = 4095;
+ }
+ /* Issue this tape command first. */
+ if (!reverse) {
+ TRACE(ft_t_noise, "skipping %d segment(s)", count);
+ result = ftape_command(nibbles == 3 ?
+ QIC_SKIP_EXTENDED_FORWARD : QIC_SKIP_FORWARD);
+ } else {
+ TRACE(ft_t_noise, "backing up %d segment(s)", count);
+ result = ftape_command(nibbles == 3 ?
+ QIC_SKIP_EXTENDED_REVERSE : QIC_SKIP_REVERSE);
+ }
+ if (result < 0) {
+ TRACE(ft_t_noise, "Skip command failed");
+ } else {
+ --count; /* 0 means one gap etc. */
+ for (i = 0; i < nibbles; ++i) {
+ if (result >= 0) {
+ result = ftape_parameter(count & 15);
+ count /= 16;
+ }
+ }
+ result = ftape_ready_wait(ftape_timeout.rewind, &status);
+ if (result >= 0) {
+ ftape_tape_running = 0;
+ }
+ }
+ }
+ TRACE_EXIT result;
+}
+
+static int validate(int id)
+{
+ /* Check to see if position found is off-track as reported
+ * once. Because all tracks in one direction lie next to
+ * each other, if off-track the error will be approximately
+ * 2 * ft_segments_per_track.
+ */
+ if (ft_location.track == -1) {
+ return 1; /* unforseen situation, don't generate error */
+ } else {
+ /* Use margin of ft_segments_per_track on both sides
+ * because ftape needs some margin and the error we're
+ * looking for is much larger !
+ */
+ int lo = (ft_location.track - 1) * ft_segments_per_track;
+ int hi = (ft_location.track + 2) * ft_segments_per_track;
+
+ return (id >= lo && id < hi);
+ }
+}
+
+static int seek_forward(int segment_id, int fast)
+{
+ int failures = 0;
+ int count;
+ static int margin = 1; /* fixed: stop this before target */
+ static int overshoot = 1;
+ static int min_count = 8;
+ int expected = -1;
+ int target = segment_id - margin;
+ int fast_seeking;
+ int prev_segment = ft_location.segment;
+ TRACE_FUN(ft_t_flow);
+
+ if (!ft_location.known) {
+ TRACE_ABORT(-EIO, ft_t_err,
+ "fatal: cannot seek from unknown location");
+ }
+ if (!validate(segment_id)) {
+ ftape_sleep(1 * FT_SECOND);
+ ft_failure = 1;
+ TRACE_ABORT(-EIO, ft_t_err,
+ "fatal: head off track (bad hardware?)");
+ }
+ TRACE(ft_t_noise, "from %d/%d to %d/0 - %d",
+ ft_location.segment, ft_location.sector,segment_id,margin);
+ count = target - ft_location.segment - overshoot;
+ fast_seeking = (fast &&
+ count > (min_count + (ft_location.bot ? 1 : 0)));
+ if (fast_seeking) {
+ TRACE(ft_t_noise, "fast skipping %d segments", count);
+ expected = segment_id - margin;
+ fast_seek(count, 0);
+ }
+ if (!ftape_tape_running) {
+ logical_forward();
+ }
+ while (ft_location.segment < segment_id) {
+ /* This requires at least one sector in a (bad) segment to
+ * have a valid and readable sector id !
+ * It looks like this is not guaranteed, so we must try
+ * to find a way to skip an EMPTY_SEGMENT. !!! FIXME !!!
+ */
+ if (ftape_read_id() < 0 || !ft_location.known ||
+ (current->signal & _DONT_BLOCK)) {
+ ft_location.known = 0;
+ if (!ftape_tape_running ||
+ ++failures > FT_SECTORS_PER_SEGMENT) {
+ TRACE_ABORT(-EIO, ft_t_err,
+ "read_id failed completely");
+ }
+ FT_SIGNAL_EXIT(_DONT_BLOCK);
+ TRACE(ft_t_flow, "read_id failed, retry (%d)",
+ failures);
+ continue;
+ }
+ if (fast_seeking) {
+ TRACE(ft_t_noise, "ended at %d/%d (%d,%d)",
+ ft_location.segment, ft_location.sector,
+ overshoot, inhibit_correction);
+ if (!inhibit_correction &&
+ (ft_location.segment < expected ||
+ ft_location.segment > expected + margin)) {
+ int error = ft_location.segment - expected;
+ TRACE(ft_t_noise,
+ "adjusting overshoot from %d to %d",
+ overshoot, overshoot + error);
+ overshoot += error;
+ /* All overshoots have the same
+ * direction, so it should never
+ * become negative, but who knows.
+ */
+ if (overshoot < -5 ||
+ overshoot > OVERSHOOT_LIMIT) {
+ if (overshoot < 0) {
+ /* keep sane value */
+ overshoot = -5;
+ } else {
+ /* keep sane value */
+ overshoot = OVERSHOOT_LIMIT;
+ }
+ TRACE(ft_t_noise,
+ "clipped overshoot to %d",
+ overshoot);
+ }
+ }
+ fast_seeking = 0;
+ }
+ if (ft_location.known) {
+ if (ft_location.segment > prev_segment + 1) {
+ TRACE(ft_t_noise,
+ "missed segment %d while skipping",
+ prev_segment + 1);
+ }
+ prev_segment = ft_location.segment;
+ }
+ }
+ if (ft_location.segment > segment_id) {
+ TRACE_ABORT(-EIO,
+ ft_t_noise, "failed: skip ended at segment %d/%d",
+ ft_location.segment, ft_location.sector);
+ }
+ TRACE_EXIT 0;
+}
+
+static int skip_reverse(int segment_id, int *pstatus)
+{
+ int failures = 0;
+ static int overshoot = 1;
+ static int min_rewind = 2; /* 1 + overshoot */
+ static const int margin = 1; /* stop this before target */
+ int expected = 0;
+ int count = 1;
+ int short_seek;
+ int target = segment_id - margin;
+ TRACE_FUN(ft_t_flow);
+
+ if (ft_location.known && !validate(segment_id)) {
+ ftape_sleep(1 * FT_SECOND);
+ ft_failure = 1;
+ TRACE_ABORT(-EIO, ft_t_err,
+ "fatal: head off track (bad hardware?)");
+ }
+ do {
+ if (!ft_location.known) {
+ TRACE(ft_t_warn, "warning: location not known");
+ }
+ TRACE(ft_t_noise, "from %d/%d to %d/0 - %d",
+ ft_location.segment, ft_location.sector,
+ segment_id, margin);
+ /* min_rewind == 1 + overshoot_when_doing_minimum_rewind
+ * overshoot == overshoot_when_doing_larger_rewind
+ * Initially min_rewind == 1 + overshoot, optimization
+ * of both values will be done separately.
+ * overshoot and min_rewind can be negative as both are
+ * sums of three components:
+ * any_overshoot == rewind_overshoot -
+ * stop_overshoot -
+ * start_overshoot
+ */
+ if (ft_location.segment - target - (min_rewind - 1) < 1) {
+ short_seek = 1;
+ } else {
+ count = ft_location.segment - target - overshoot;
+ short_seek = (count < 1);
+ }
+ if (short_seek) {
+ count = 1; /* do shortest rewind */
+ expected = ft_location.segment - min_rewind;
+ if (expected/ft_segments_per_track != ft_location.track) {
+ expected = (ft_location.track *
+ ft_segments_per_track);
+ }
+ } else {
+ expected = target;
+ }
+ fast_seek(count, 1);
+ logical_forward();
+ if (ftape_read_id() < 0 || !ft_location.known ||
+ (current->signal & _DONT_BLOCK)) {
+ if ((!ftape_tape_running && !ft_location.known) ||
+ ++failures > FT_SECTORS_PER_SEGMENT) {
+ TRACE_ABORT(-EIO, ft_t_err,
+ "read_id failed completely");
+ }
+ FT_SIGNAL_EXIT(_DONT_BLOCK);
+ TRACE_CATCH(ftape_report_drive_status(pstatus),);
+ TRACE(ft_t_noise, "ftape_read_id failed, retry (%d)",
+ failures);
+ continue;
+ }
+ TRACE(ft_t_noise, "ended at %d/%d (%d,%d,%d)",
+ ft_location.segment, ft_location.sector,
+ min_rewind, overshoot, inhibit_correction);
+ if (!inhibit_correction &&
+ (ft_location.segment < expected ||
+ ft_location.segment > expected + margin)) {
+ int error = expected - ft_location.segment;
+ if (short_seek) {
+ TRACE(ft_t_noise,
+ "adjusting min_rewind from %d to %d",
+ min_rewind, min_rewind + error);
+ min_rewind += error;
+ if (min_rewind < -5) {
+ /* is this right ? FIXME ! */
+ /* keep sane value */
+ min_rewind = -5;
+ TRACE(ft_t_noise,
+ "clipped min_rewind to %d",
+ min_rewind);
+ }
+ } else {
+ TRACE(ft_t_noise,
+ "adjusting overshoot from %d to %d",
+ overshoot, overshoot + error);
+ overshoot += error;
+ if (overshoot < -5 ||
+ overshoot > OVERSHOOT_LIMIT) {
+ if (overshoot < 0) {
+ /* keep sane value */
+ overshoot = -5;
+ } else {
+ /* keep sane value */
+ overshoot = OVERSHOOT_LIMIT;
+ }
+ TRACE(ft_t_noise,
+ "clipped overshoot to %d",
+ overshoot);
+ }
+ }
+ }
+ } while (ft_location.segment > segment_id);
+ if (ft_location.known) {
+ TRACE(ft_t_noise, "current location: %d/%d",
+ ft_location.segment, ft_location.sector);
+ }
+ TRACE_EXIT 0;
+}
+
+static int determine_position(void)
+{
+ int retry = 0;
+ int status;
+ int result;
+ TRACE_FUN(ft_t_flow);
+
+ if (!ftape_tape_running) {
+ /* This should only happen if tape is stopped by isr.
+ */
+ TRACE(ft_t_flow, "waiting for tape stop");
+ if (ftape_ready_wait(ftape_timeout.pause, &status) < 0) {
+ TRACE(ft_t_flow, "drive still running (fatal)");
+ ftape_tape_running = 1; /* ? */
+ }
+ } else {
+ ftape_report_drive_status(&status);
+ }
+ if (status & QIC_STATUS_READY) {
+ /* Drive must be ready to check error state !
+ */
+ TRACE(ft_t_flow, "drive is ready");
+ if (status & QIC_STATUS_ERROR) {
+ unsigned int error;
+ qic117_cmd_t command;
+
+ /* Report and clear error state, try to continue.
+ */
+ TRACE(ft_t_flow, "error status set");
+ ftape_report_error(&error, &command, 1);
+ ftape_ready_wait(ftape_timeout.reset, &status);
+ ftape_tape_running = 0; /* ? */
+ }
+ if (check_bot_eot(status)) {
+ if (ft_location.bot) {
+ if ((status & QIC_STATUS_READY) == 0) {
+ /* tape moving away from
+ * bot/eot, let's see if we
+ * can catch up with the first
+ * segment on this track.
+ */
+ } else {
+ TRACE(ft_t_flow,
+ "start tape from logical bot");
+ logical_forward(); /* start moving */
+ }
+ } else {
+ if ((status & QIC_STATUS_READY) == 0) {
+ TRACE(ft_t_noise, "waiting for logical end of track");
+ result = ftape_ready_wait(ftape_timeout.reset, &status);
+ /* error handling needed ? */
+ } else {
+ TRACE(ft_t_noise,
+ "tape at logical end of track");
+ }
+ }
+ } else {
+ TRACE(ft_t_flow, "start tape");
+ logical_forward(); /* start moving */
+ ft_location.known = 0; /* not cleared by logical forward ! */
+ }
+ }
+ /* tape should be moving now, start reading id's
+ */
+ while (!ft_location.known &&
+ retry++ < FT_SECTORS_PER_SEGMENT &&
+ (result = ftape_read_id()) < 0) {
+
+ TRACE(ft_t_flow, "location unknown");
+
+ /* exit on signal
+ */
+ FT_SIGNAL_EXIT(_DONT_BLOCK);
+
+ /* read-id somehow failed, tape may
+ * have reached end or some other
+ * error happened.
+ */
+ TRACE(ft_t_flow, "read-id failed");
+ TRACE_CATCH(ftape_report_drive_status(&status),);
+ TRACE(ft_t_err, "ftape_report_drive_status: 0x%02x", status);
+ if (status & QIC_STATUS_READY) {
+ ftape_tape_running = 0;
+ TRACE(ft_t_noise, "tape stopped for unknown reason! "
+ "status = 0x%02x", status);
+ if (status & QIC_STATUS_ERROR ||
+ !check_bot_eot(status)) {
+ /* oops, tape stopped but not at end!
+ */
+ TRACE_EXIT -EIO;
+ }
+ }
+ }
+ TRACE(ft_t_flow,
+ "tape is positioned at segment %d", ft_location.segment);
+ TRACE_EXIT ft_location.known ? 0 : -EIO;
+}
+
+/* Get the tape running and position it just before the
+ * requested segment.
+ * Seek tape-track and reposition as needed.
+ */
+int ftape_start_tape(int segment_id, int sector_offset)
+{
+ int track = segment_id / ft_segments_per_track;
+ int result = -EIO;
+ int status;
+ static int last_segment = -1;
+ static int bad_bus_timing = 0;
+ /* number of segments passing the head between starting the tape
+ * and being able to access the first sector.
+ */
+ static int start_offset = 1;
+ int retry;
+ TRACE_FUN(ft_t_flow);
+
+ /* If sector_offset > 0, seek into wanted segment instead of
+ * into previous.
+ * This allows error recovery if a part of the segment is bad
+ * (erased) causing the tape drive to generate an index pulse
+ * thus causing a no-data error before the requested sector
+ * is reached.
+ */
+ ftape_tape_running = 0;
+ TRACE(ft_t_noise, "target segment: %d/%d%s", segment_id, sector_offset,
+ ft_buffer[ft_head]->retry > 0 ? " retry" : "");
+ if (ft_buffer[ft_head]->retry > 0) { /* this is a retry */
+ int dist = segment_id - last_segment;
+
+ if ((int)ft_history.overrun_errors < overrun_count_offset) {
+ overrun_count_offset = ft_history.overrun_errors;
+ } else if (dist < 0 || dist > 50) {
+ overrun_count_offset = ft_history.overrun_errors;
+ } else if ((ft_history.overrun_errors -
+ overrun_count_offset) >= 8) {
+ if (ftape_increase_threshold() >= 0) {
+ --ft_buffer[ft_head]->retry;
+ overrun_count_offset =
+ ft_history.overrun_errors;
+ TRACE(ft_t_warn, "increased threshold because "
+ "of excessive overrun errors");
+ } else if (!bad_bus_timing && ft_data_rate >= 1000) {
+ ftape_half_data_rate();
+ --ft_buffer[ft_head]->retry;
+ bad_bus_timing = 1;
+ overrun_count_offset =
+ ft_history.overrun_errors;
+ TRACE(ft_t_warn, "reduced datarate because "
+ "of excessive overrun errors");
+ }
+ }
+ }
+ last_segment = segment_id;
+ if (ft_location.track != track ||
+ (ftape_might_be_off_track && ft_buffer[ft_head]->retry== 0)) {
+ /* current track unknown or not equal to destination
+ */
+ ftape_ready_wait(ftape_timeout.seek, &status);
+ ftape_seek_head_to_track(track);
+ /* overrun_count_offset = ft_history.overrun_errors; */
+ }
+ result = -EIO;
+ retry = 0;
+ while (result < 0 &&
+ retry++ <= 5 &&
+ !ft_failure &&
+ (current->signal & _DONT_BLOCK) == 0) {
+
+ if (retry && start_offset < 5) {
+ start_offset ++;
+ }
+ /* Check if we are able to catch the requested
+ * segment in time.
+ */
+ if ((ft_location.known || (determine_position() == 0)) &&
+ ft_location.segment >=
+ (segment_id -
+ ((ftape_tape_running || ft_location.bot)
+ ? 0 : start_offset))) {
+ /* Too far ahead (in or past target segment).
+ */
+ if (ftape_tape_running) {
+ if ((result = ftape_stop_tape(&status)) < 0) {
+ TRACE(ft_t_err,
+ "stop tape failed with code %d",
+ result);
+ break;
+ }
+ TRACE(ft_t_noise, "tape stopped");
+ ftape_tape_running = 0;
+ }
+ TRACE(ft_t_noise, "repositioning");
+ ++ft_history.rewinds;
+ if (segment_id % ft_segments_per_track < start_offset){
+ TRACE(ft_t_noise, "end of track condition\n"
+ KERN_INFO "segment_id : %d\n"
+ KERN_INFO "ft_segments_per_track: %d\n"
+ KERN_INFO "start_offset : %d",
+ segment_id, ft_segments_per_track,
+ start_offset);
+
+ /* If seeking to first segments on
+ * track better do a complete rewind
+ * to logical begin of track to get a
+ * more steady tape motion.
+ */
+ result = ftape_command_wait(
+ (ft_location.track & 1)
+ ? QIC_PHYSICAL_FORWARD
+ : QIC_PHYSICAL_REVERSE,
+ ftape_timeout.rewind, &status);
+ check_bot_eot(status); /* update location */
+ } else {
+ result= skip_reverse(segment_id - start_offset,
+ &status);
+ }
+ }
+ if (!ft_location.known) {
+ TRACE(ft_t_bug, "panic: location not known");
+ result = -EIO;
+ continue; /* while() will check for failure */
+ }
+ TRACE(ft_t_noise, "current segment: %d/%d",
+ ft_location.segment, ft_location.sector);
+ /* We're on the right track somewhere before the
+ * wanted segment. Start tape movement if needed and
+ * skip to just before or inside the requested
+ * segment. Keep tape running.
+ */
+ result = 0;
+ if (ft_location.segment <
+ (segment_id - ((ftape_tape_running || ft_location.bot)
+ ? 0 : start_offset))) {
+ if (sector_offset > 0) {
+ result = seek_forward(segment_id,
+ retry <= 3);
+ } else {
+ result = seek_forward(segment_id - 1,
+ retry <= 3);
+ }
+ }
+ if (result == 0 &&
+ ft_location.segment !=
+ (segment_id - (sector_offset > 0 ? 0 : 1))) {
+ result = -EIO;
+ }
+ }
+ if (result < 0) {
+ TRACE(ft_t_err, "failed to reposition");
+ } else {
+ ft_runner_status = running;
+ }
+ TRACE_EXIT result;
+}
diff --git a/drivers/char/ftape/lowlevel/ftape-rw.h b/drivers/char/ftape/lowlevel/ftape-rw.h
new file mode 100644
index 000000000..862461396
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/ftape-rw.h
@@ -0,0 +1,121 @@
+#ifndef _FTAPE_RW_H
+#define _FTAPE_RW_H
+
+/*
+ * Copyright (C) 1993-1996 Bas Laarhoven,
+ * (C) 1996-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-rw.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:25 $
+ *
+ * This file contains the definitions for the read and write
+ * functions for the QIC-117 floppy-tape driver for Linux.
+ *
+ * Claus-Justus Heine (1996/09/20): Add definition of format code 6
+ * Claus-Justus Heine (1996/10/04): Changed GET/PUT macros to cast to (__u8 *)
+ *
+ */
+
+#include "../lowlevel/fdc-io.h"
+#include "../lowlevel/ftape-init.h"
+#include "../lowlevel/ftape-bsm.h"
+
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,0,0)
+#include <asm/unaligned.h>
+
+#define GET2(address, offset) get_unaligned((__u16*)((__u8 *)address + offset))
+#define GET4(address, offset) get_unaligned((__u32*)((__u8 *)address + offset))
+#define GET8(address, offset) get_unaligned((__u64*)((__u8 *)address + offset))
+#define PUT2(address, offset , value) put_unaligned((value), (__u16*)((__u8 *)address + offset))
+#define PUT4(address, offset , value) put_unaligned((value), (__u32*)((__u8 *)address + offset))
+#define PUT8(address, offset , value) put_unaligned((value), (__u64*)((__u8 *)address + offset))
+#else
+#define GET2(address, offset) *(__u16*)((__u8 *)address + offset)
+#define GET4(address, offset) *(__u32*)((__u8 *)address + offset)
+#define GET8(address, offset) *(__u64*)((__u8 *)address + offset)
+#define PUT2(address, offset , value) *(__u16*)((__u8 *)address + offset) = (__u16)(value)
+#define PUT4(address, offset , value) *(__u32*)((__u8 *)address + offset) = (__u32)(value)
+#define PUT8(address, offset , value) *(__u64*)((__u8 *)address + offset) = (__u32)(value)
+#endif
+
+enum runner_status_enum {
+ idle = 0,
+ running,
+ do_abort,
+ aborting,
+ logical_eot,
+ end_of_tape,
+};
+
+typedef enum ft_buffer_queue {
+ ft_queue_head = 0,
+ ft_queue_tail = 1
+} ft_buffer_queue_t;
+
+
+typedef struct {
+ int track; /* tape head position */
+ volatile int segment; /* current segment */
+ volatile int sector; /* sector offset within current segment */
+ volatile unsigned int bot; /* logical begin of track */
+ volatile unsigned int eot; /* logical end of track */
+ volatile unsigned int known; /* validates bot, segment, sector */
+} location_record;
+
+/* Count nr of 1's in pattern.
+ */
+extern inline int count_ones(unsigned long mask)
+{
+ int bits;
+
+ for (bits = 0; mask != 0; mask >>= 1) {
+ if (mask & 1) {
+ ++bits;
+ }
+ }
+ return bits;
+}
+
+#define FT_MAX_NR_BUFFERS 16 /* arbitrary value */
+/* ftape-rw.c defined global vars.
+ */
+extern buffer_struct *ft_buffer[FT_MAX_NR_BUFFERS];
+extern int ft_nr_buffers;
+extern location_record ft_location;
+extern volatile int ftape_tape_running;
+
+/* ftape-rw.c defined global functions.
+ */
+extern int ftape_setup_new_segment(buffer_struct * buff,
+ int segment_id,
+ int offset);
+extern int ftape_calc_next_cluster(buffer_struct * buff);
+extern buffer_struct *ftape_next_buffer (ft_buffer_queue_t pos);
+extern buffer_struct *ftape_get_buffer (ft_buffer_queue_t pos);
+extern int ftape_buffer_id (ft_buffer_queue_t pos);
+extern void ftape_reset_buffer(void);
+extern int ftape_read_id(void);
+extern void ftape_tape_parameters(__u8 drive_configuration);
+extern int ftape_wait_segment(buffer_state_enum state);
+extern int ftape_dumb_stop(void);
+extern int ftape_start_tape(int segment_id, int offset);
+extern int ftape_stop_tape(int *pstatus);
+extern int ftape_handle_logical_eot(void);
+extern buffer_state_enum ftape_set_state(buffer_state_enum new_state);
+#endif /* _FTAPE_RW_H */
diff --git a/drivers/char/ftape/lowlevel/ftape-setup.c b/drivers/char/ftape/lowlevel/ftape-setup.c
new file mode 100644
index 000000000..5cb89c694
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/ftape-setup.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 1996, 1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-setup.c,v $
+ * $Revision: 1.7 $
+ * $Date: 1997/10/10 09:57:06 $
+ *
+ * This file contains the code for processing the kernel command
+ * line options for the QIC-40/80/3010/3020 floppy-tape driver
+ * "ftape" for Linux.
+ */
+
+#include <linux/config.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <asm/segment.h>
+
+#include <linux/ftape.h>
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,16)
+#include <linux/init.h>
+#else
+#define __initdata
+#define __initfunc(__arg) __arg
+#endif
+#include "../lowlevel/ftape-tracing.h"
+#include "../lowlevel/fdc-io.h"
+
+static struct param_table {
+ const char *name;
+ int *var;
+ int def_param;
+ int min;
+ int max;
+} config_params[] __initdata = {
+#ifndef CONFIG_FT_NO_TRACE_AT_ALL
+ { "tracing", &ftape_tracing, 3, ft_t_bug, ft_t_any},
+#endif
+ { "ioport", &ft_fdc_base, CONFIG_FT_FDC_BASE, 0x0, 0xfff},
+ { "irq", &ft_fdc_irq, CONFIG_FT_FDC_IRQ, 2, 15},
+ { "dma", &ft_fdc_dma, CONFIG_FT_FDC_DMA, 0, 3},
+ { "threshold", &ft_fdc_threshold, CONFIG_FT_FDC_THR, 1, 16},
+ { "datarate", &ft_fdc_rate_limit, CONFIG_FT_FDC_MAX_RATE, 500, 2000},
+ { "fc10", &ft_probe_fc10, CONFIG_FT_PROBE_FC10, 0, 1},
+ { "mach2", &ft_mach2, CONFIG_FT_MACH2, 0, 1}
+};
+
+__initfunc(void ftape_setup(char *str, int *ints))
+{
+ int i;
+ int param;
+ TRACE_FUN(ft_t_flow);
+
+ if (str) {
+ for (i=0; i < NR_ITEMS(config_params); i++) {
+ if (strcmp(str,config_params[i].name) == 0){
+ if (ints[0]) {
+ param = ints[1];
+ } else {
+ param = config_params[i].def_param;
+ }
+ if (param < config_params[i].min ||
+ param > config_params[i].max) {
+ TRACE(ft_t_err,
+ "parameter %s out of range %d ... %d",
+ config_params[i].name,
+ config_params[i].min,
+ config_params[i].max);
+ TRACE_EXIT;
+ }
+ if(config_params[i].var) {
+ TRACE(ft_t_info, "%s=%d", str, param);
+ *config_params[i].var = param;
+ }
+ TRACE_EXIT;
+ }
+ }
+ }
+ if (str) {
+ TRACE(ft_t_err, "unknown ftape option [%s]", str);
+
+ TRACE(ft_t_err, "allowed options are:");
+ for (i=0; i < NR_ITEMS(config_params); i++) {
+ TRACE(ft_t_err, " %s",config_params[i].name);
+ }
+ } else {
+ TRACE(ft_t_err, "botched ftape option");
+ }
+ TRACE_EXIT;
+}
diff --git a/drivers/char/ftape/lowlevel/ftape-tracing.c b/drivers/char/ftape/lowlevel/ftape-tracing.c
new file mode 100644
index 000000000..95e3df8e8
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/ftape-tracing.c
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 1993-1996 Bas Laarhoven,
+ * (C) 1996-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-tracing.c,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:27 $
+ *
+ * This file contains the reading code
+ * for the QIC-117 floppy-tape driver for Linux.
+ */
+
+#include <linux/ftape.h>
+#include "../lowlevel/ftape-tracing.h"
+
+/* Global vars.
+ */
+/* tracing
+ * set it to: to log :
+ * 0 bugs
+ * 1 + errors
+ * 2 + warnings
+ * 3 + information
+ * 4 + more information
+ * 5 + program flow
+ * 6 + fdc/dma info
+ * 7 + data flow
+ * 8 + everything else
+ */
+ft_trace_t ftape_tracing = ft_t_info; /* Default level: information and up */
+int ftape_function_nest_level = 0;
+
+/* Local vars.
+ */
+static __u8 trace_id = 0;
+static char spacing[] = "* ";
+
+void ftape_trace_call(const char *file, const char *name)
+{
+ char *indent;
+
+ /* Since printk seems not to work with "%*s" format
+ * we'll use this work-around.
+ */
+ if (ftape_function_nest_level < 0) {
+ printk(KERN_INFO "function nest level (%d) < 0\n",
+ ftape_function_nest_level);
+ ftape_function_nest_level = 0;
+ }
+ if (ftape_function_nest_level < sizeof(spacing)) {
+ indent = (spacing +
+ sizeof(spacing) - 1 -
+ ftape_function_nest_level);
+ } else {
+ indent = spacing;
+ }
+ printk(KERN_INFO "[%03d]%s+%s (%s)\n",
+ (int) trace_id++, indent, file, name);
+}
+
+void ftape_trace_exit(const char *file, const char *name)
+{
+ char *indent;
+
+ /* Since printk seems not to work with "%*s" format
+ * we'll use this work-around.
+ */
+ if (ftape_function_nest_level < 0) {
+ printk(KERN_INFO "function nest level (%d) < 0\n", ftape_function_nest_level);
+ ftape_function_nest_level = 0;
+ }
+ if (ftape_function_nest_level < sizeof(spacing)) {
+ indent = (spacing +
+ sizeof(spacing) - 1 -
+ ftape_function_nest_level);
+ } else {
+ indent = spacing;
+ }
+ printk(KERN_INFO "[%03d]%s-%s (%s)\n",
+ (int) trace_id++, indent, file, name);
+}
+
+void ftape_trace_log(const char *file, const char *function)
+{
+ char *indent;
+
+ /* Since printk seems not to work with "%*s" format
+ * we'll use this work-around.
+ */
+ if (ftape_function_nest_level < 0) {
+ printk(KERN_INFO "function nest level (%d) < 0\n", ftape_function_nest_level);
+ ftape_function_nest_level = 0;
+ }
+ if (ftape_function_nest_level < sizeof(spacing)) {
+ indent = (spacing +
+ sizeof(spacing) - 1 -
+ ftape_function_nest_level);
+ } else {
+ indent = spacing;
+ }
+ printk(KERN_INFO "[%03d]%s%s (%s) - ",
+ (int) trace_id++, indent, file, function);
+}
diff --git a/drivers/char/ftape/lowlevel/ftape-tracing.h b/drivers/char/ftape/lowlevel/ftape-tracing.h
new file mode 100644
index 000000000..ac675568a
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/ftape-tracing.h
@@ -0,0 +1,180 @@
+#ifndef _FTAPE_TRACING_H
+#define _FTAPE_TRACING_H
+
+/*
+ * Copyright (C) 1994-1996 Bas Laarhoven,
+ * (C) 1996-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-tracing.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:28 $
+ *
+ * This file contains definitions that eases the debugging of the
+ * QIC-40/80/3010/3020 floppy-tape driver "ftape" for Linux.
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+
+/*
+ * Be very careful with TRACE_EXIT and TRACE_ABORT.
+ *
+ * if (something) TRACE_EXIT error;
+ *
+ * will NOT work. Use
+ *
+ * if (something) {
+ * TRACE_EXIT error;
+ * }
+ *
+ * instead. Maybe a bit dangerous, but save lots of lines of code.
+ */
+
+#define LL_X "%d/%d KB"
+#define LL(x) (unsigned int)((__u64)(x)>>10), (unsigned int)((x)&1023)
+
+typedef enum {
+ ft_t_nil = -1,
+ ft_t_bug,
+ ft_t_err,
+ ft_t_warn,
+ ft_t_info,
+ ft_t_noise,
+ ft_t_flow,
+ ft_t_fdc_dma,
+ ft_t_data_flow,
+ ft_t_any
+} ft_trace_t;
+
+#ifdef CONFIG_FT_NO_TRACE_AT_ALL
+/* the compiler will optimize away most TRACE() macros
+ */
+#define FT_TRACE_TOP_LEVEL ft_t_bug
+#define TRACE_FUN(level) do {} while(0)
+#define TRACE_EXIT return
+#define TRACE(l, m, i...) \
+{ \
+ if ((ft_trace_t)(l) == FT_TRACE_TOP_LEVEL) { \
+ printk(KERN_INFO"ftape"__FILE__"("__FUNCTION__"):\n" \
+ KERN_INFO m".\n" ,##i); \
+ } \
+}
+#define SET_TRACE_LEVEL(l) if ((l) == (l)) do {} while(0)
+#define TRACE_LEVEL FT_TRACE_TOP_LEVEL
+
+#else
+
+#ifdef CONFIG_FT_NO_TRACE
+/* the compiler will optimize away many TRACE() macros
+ * the ftape_simple_trace_call() function simply increments
+ * the function nest level.
+ */
+#define FT_TRACE_TOP_LEVEL ft_t_warn
+#define TRACE_FUN(level) ftape_function_nest_level++
+#define TRACE_EXIT ftape_function_nest_level--; return
+
+#else
+#ifdef CONFIG_FT_FULL_DEBUG
+#define FT_TRACE_TOP_LEVEL ft_t_any
+#else
+#define FT_TRACE_TOP_LEVEL ft_t_flow
+#endif
+#define TRACE_FUN(level) \
+ const ft_trace_t _tracing = level; \
+ if (ftape_tracing >= (ft_trace_t)(level) && \
+ (ft_trace_t)(level) <= FT_TRACE_TOP_LEVEL) \
+ ftape_trace_call(__FILE__, __FUNCTION__); \
+ ftape_function_nest_level ++;
+
+#define TRACE_EXIT \
+ --ftape_function_nest_level; \
+ if (ftape_tracing >= (ft_trace_t)(_tracing) && \
+ (ft_trace_t)(_tracing) <= FT_TRACE_TOP_LEVEL) \
+ ftape_trace_exit(__FILE__, __FUNCTION__); \
+ return
+
+#endif
+
+#define TRACE(l, m, i...) \
+{ \
+ if (ftape_tracing >= (ft_trace_t)(l) && \
+ (ft_trace_t)(l) <= FT_TRACE_TOP_LEVEL) { \
+ ftape_trace_log(__FILE__, __FUNCTION__); \
+ printk(m".\n" ,##i); \
+ } \
+}
+
+#define SET_TRACE_LEVEL(l) \
+{ \
+ if ((ft_trace_t)(l) <= FT_TRACE_TOP_LEVEL) { \
+ ftape_tracing = (ft_trace_t)(l); \
+ } else { \
+ ftape_tracing = FT_TRACE_TOP_LEVEL; \
+ } \
+}
+#define TRACE_LEVEL \
+((ftape_tracing <= FT_TRACE_TOP_LEVEL) ? ftape_tracing : FT_TRACE_TOP_LEVEL)
+
+
+/* Global variables declared in tracing.c
+ */
+extern ft_trace_t ftape_tracing; /* sets default level */
+extern int ftape_function_nest_level;
+
+/* Global functions declared in tracing.c
+ */
+extern void ftape_trace_call(const char *file, const char *name);
+extern void ftape_trace_exit(const char *file, const char *name);
+extern void ftape_trace_log (const char *file, const char *name);
+
+#endif /* !defined(CONFIG_FT_NO_TRACE_AT_ALL) */
+
+/*
+ * Abort with a message.
+ */
+#define TRACE_ABORT(res, i...) \
+{ \
+ TRACE(##i); \
+ TRACE_EXIT res; \
+}
+
+/* The following transforms the common "if(result < 0) ... " into a
+ * one-liner.
+ */
+#define _TRACE_CATCH(level, fun, action) \
+{ \
+ int _res = (fun); \
+ if (_res < 0) { \
+ do { action /* */ ; } while(0); \
+ TRACE_ABORT(_res, level, "%s failed: %d", #fun, _res); \
+ } \
+}
+
+#define TRACE_CATCH(fun, fail) _TRACE_CATCH(ft_t_err, fun, fail)
+
+/* Abort the current function when signalled. This doesn't belong here,
+ * but rather into ftape-rw.h (maybe)
+ */
+#define FT_SIGNAL_EXIT(sig_mask) \
+ if (current->signal & (sig_mask)) { \
+ TRACE_ABORT(-EINTR, \
+ ft_t_warn, \
+ "interrupted by non-blockable signal"); \
+ }
+
+#endif /* _FTAPE_TRACING_H */
diff --git a/drivers/char/ftape/lowlevel/ftape-write.c b/drivers/char/ftape/lowlevel/ftape-write.c
new file mode 100644
index 000000000..a0f095b9a
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/ftape-write.c
@@ -0,0 +1,337 @@
+/*
+ * Copyright (C) 1993-1995 Bas Laarhoven,
+ * (C) 1996-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-write.c,v $
+ * $Revision: 1.3.4.1 $
+ * $Date: 1997/11/14 18:07:04 $
+ *
+ * This file contains the writing code
+ * for the QIC-117 floppy-tape driver for Linux.
+ */
+
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <asm/segment.h>
+
+#include <linux/ftape.h>
+#include <linux/qic117.h>
+#include "../lowlevel/ftape-tracing.h"
+#include "../lowlevel/ftape-write.h"
+#include "../lowlevel/ftape-read.h"
+#include "../lowlevel/ftape-io.h"
+#include "../lowlevel/ftape-ctl.h"
+#include "../lowlevel/ftape-rw.h"
+#include "../lowlevel/ftape-ecc.h"
+#include "../lowlevel/ftape-bsm.h"
+#include "../lowlevel/fdc-isr.h"
+
+/* Global vars.
+ */
+
+/* Local vars.
+ */
+static int last_write_failed = 0;
+
+void ftape_zap_write_buffers(void)
+{
+ int i;
+
+ for (i = 0; i < ft_nr_buffers; ++i) {
+ ft_buffer[i]->status = done;
+ }
+ ftape_reset_buffer();
+}
+
+static int copy_and_gen_ecc(void *destination,
+ const void *source,
+ const SectorMap bad_sector_map)
+{
+ int result;
+ struct memory_segment mseg;
+ int bads = count_ones(bad_sector_map);
+ TRACE_FUN(ft_t_any);
+
+ if (bads > 0) {
+ TRACE(ft_t_noise, "bad sectors in map: %d", bads);
+ }
+ if (bads + 3 >= FT_SECTORS_PER_SEGMENT) {
+ TRACE(ft_t_noise, "empty segment");
+ mseg.blocks = 0; /* skip entire segment */
+ result = 0; /* nothing written */
+ } else {
+ mseg.blocks = FT_SECTORS_PER_SEGMENT - bads;
+ mseg.data = destination;
+ memcpy(mseg.data, source, (mseg.blocks - 3) * FT_SECTOR_SIZE);
+ result = ftape_ecc_set_segment_parity(&mseg);
+ if (result < 0) {
+ TRACE(ft_t_err, "ecc_set_segment_parity failed");
+ } else {
+ result = (mseg.blocks - 3) * FT_SECTOR_SIZE;
+ }
+ }
+ TRACE_EXIT result;
+}
+
+
+int ftape_start_writing(const ft_write_mode_t mode)
+{
+ buffer_struct *head = ftape_get_buffer(ft_queue_head);
+ int segment_id = head->segment_id;
+ int result;
+ buffer_state_enum wanted_state = (mode == FT_WR_DELETE
+ ? deleting
+ : writing);
+ TRACE_FUN(ft_t_flow);
+
+ if ((ft_driver_state != wanted_state) || head->status != waiting) {
+ TRACE_EXIT 0;
+ }
+ ftape_setup_new_segment(head, segment_id, 1);
+ if (mode == FT_WR_SINGLE) {
+ /* stop tape instead of pause */
+ head->next_segment = 0;
+ }
+ ftape_calc_next_cluster(head); /* prepare */
+ head->status = ft_driver_state; /* either writing or deleting */
+ if (ft_runner_status == idle) {
+ TRACE(ft_t_noise,
+ "starting runner for segment %d", segment_id);
+ TRACE_CATCH(ftape_start_tape(segment_id,head->sector_offset),);
+ } else {
+ TRACE(ft_t_noise, "runner not idle, not starting tape");
+ }
+ /* go */
+ result = fdc_setup_read_write(head, (mode == FT_WR_DELETE
+ ? FDC_WRITE_DELETED : FDC_WRITE));
+ ftape_set_state(wanted_state); /* should not be necessary */
+ TRACE_EXIT result;
+}
+
+/* Wait until all data is actually written to tape.
+ *
+ * There is a problem: when the tape runs into logical EOT, then this
+ * failes. We need to restart the runner in this case.
+ */
+int ftape_loop_until_writes_done(void)
+{
+ buffer_struct *head;
+ TRACE_FUN(ft_t_flow);
+
+ while ((ft_driver_state == writing || ft_driver_state == deleting) &&
+ ftape_get_buffer(ft_queue_head)->status != done) {
+ /* set the runner status to idle if at lEOT */
+ TRACE_CATCH(ftape_handle_logical_eot(), last_write_failed = 1);
+ /* restart the tape if necessary */
+ if (ft_runner_status == idle) {
+ TRACE(ft_t_noise, "runner is idle, restarting");
+ if (ft_driver_state == deleting) {
+ TRACE_CATCH(ftape_start_writing(FT_WR_DELETE),
+ last_write_failed = 1);
+ } else {
+ TRACE_CATCH(ftape_start_writing(FT_WR_MULTI),
+ last_write_failed = 1);
+ }
+ }
+ TRACE(ft_t_noise, "tail: %d, head: %d",
+ ftape_buffer_id(ft_queue_tail),
+ ftape_buffer_id(ft_queue_head));
+ TRACE_CATCH(fdc_interrupt_wait(5 * FT_SECOND),
+ last_write_failed = 1);
+ head = ftape_get_buffer(ft_queue_head);
+ if (head->status == error) {
+ /* Allow escape from loop when signaled !
+ */
+ FT_SIGNAL_EXIT(_DONT_BLOCK);
+ if (head->hard_error_map != 0) {
+ /* Implement hard write error recovery here
+ */
+ }
+ /* retry this one */
+ head->status = waiting;
+ if (ft_runner_status == aborting) {
+ ftape_dumb_stop();
+ }
+ if (ft_runner_status != idle) {
+ TRACE_ABORT(-EIO, ft_t_err,
+ "unexpected state: "
+ "ft_runner_status != idle");
+ }
+ ftape_start_writing(ft_driver_state == deleting
+ ? FT_WR_MULTI : FT_WR_DELETE);
+ }
+ TRACE(ft_t_noise, "looping until writes done");
+ }
+ ftape_set_state(idle);
+ TRACE_EXIT 0;
+}
+
+/* Write given segment from buffer at address to tape.
+ */
+static int write_segment(const int segment_id,
+ const void *address,
+ const ft_write_mode_t write_mode)
+{
+ int bytes_written = 0;
+ buffer_struct *tail;
+ buffer_state_enum wanted_state = (write_mode == FT_WR_DELETE
+ ? deleting : writing);
+ TRACE_FUN(ft_t_flow);
+
+ TRACE(ft_t_noise, "segment_id = %d", segment_id);
+ if (ft_driver_state != wanted_state) {
+ if (ft_driver_state == deleting ||
+ wanted_state == deleting) {
+ TRACE_CATCH(ftape_loop_until_writes_done(),);
+ }
+ TRACE(ft_t_noise, "calling ftape_abort_operation");
+ TRACE_CATCH(ftape_abort_operation(),);
+ ftape_zap_write_buffers();
+ ftape_set_state(wanted_state);
+ }
+ /* if all buffers full we'll have to wait...
+ */
+ ftape_wait_segment(wanted_state);
+ tail = ftape_get_buffer(ft_queue_tail);
+ switch(tail->status) {
+ case done:
+ ft_history.defects += count_ones(tail->hard_error_map);
+ break;
+ case waiting:
+ /* this could happen with multiple EMPTY_SEGMENTs, but
+ * shouldn't happen any more as we re-start the runner even
+ * with an empty segment.
+ */
+ bytes_written = -EAGAIN;
+ break;
+ case error:
+ /* setup for a retry
+ */
+ tail->status = waiting;
+ bytes_written = -EAGAIN; /* force retry */
+ if (tail->hard_error_map != 0) {
+ TRACE(ft_t_warn,
+ "warning: %d hard error(s) in written segment",
+ count_ones(tail->hard_error_map));
+ TRACE(ft_t_noise, "hard_error_map = 0x%08lx",
+ (long)tail->hard_error_map);
+ /* Implement hard write error recovery here
+ */
+ }
+ break;
+ default:
+ TRACE_ABORT(-EIO, ft_t_err,
+ "wait for empty segment failed, tail status: %d",
+ tail->status);
+ }
+ /* should runner stop ?
+ */
+ if (ft_runner_status == aborting) {
+ buffer_struct *head = ftape_get_buffer(ft_queue_head);
+ if (head->status == wanted_state) {
+ head->status = done; /* ???? */
+ }
+ /* don't call abort_operation(), we don't want to zap
+ * the dma buffers
+ */
+ TRACE_CATCH(ftape_dumb_stop(),);
+ } else {
+ /* If just passed last segment on tape: wait for BOT
+ * or EOT mark. Sets ft_runner_status to idle if at lEOT
+ * and successful
+ */
+ TRACE_CATCH(ftape_handle_logical_eot(),);
+ }
+ if (tail->status == done) {
+ /* now at least one buffer is empty, fill it with our
+ * data. skip bad sectors and generate ecc.
+ * copy_and_gen_ecc return nr of bytes written, range
+ * 0..29 Kb inclusive!
+ *
+ * Empty segments are handled inside coyp_and_gen_ecc()
+ */
+ if (write_mode != FT_WR_DELETE) {
+ TRACE_CATCH(bytes_written = copy_and_gen_ecc(
+ tail->address, address,
+ ftape_get_bad_sector_entry(segment_id)),);
+ }
+ tail->segment_id = segment_id;
+ tail->status = waiting;
+ tail = ftape_next_buffer(ft_queue_tail);
+ }
+ /* Start tape only if all buffers full or flush mode.
+ * This will give higher probability of streaming.
+ */
+ if (ft_runner_status != running &&
+ ((tail->status == waiting &&
+ ftape_get_buffer(ft_queue_head) == tail) ||
+ write_mode != FT_WR_ASYNC)) {
+ TRACE_CATCH(ftape_start_writing(write_mode),);
+ }
+ TRACE_EXIT bytes_written;
+}
+
+/* Write as much as fits from buffer to the given segment on tape
+ * and handle retries.
+ * Return the number of bytes written (>= 0), or:
+ * -EIO write failed
+ * -EINTR interrupted by signal
+ * -ENOSPC device full
+ */
+int ftape_write_segment(const int segment_id,
+ const void *buffer,
+ const ft_write_mode_t flush)
+{
+ int retry = 0;
+ int result;
+ TRACE_FUN(ft_t_flow);
+
+ ft_history.used |= 2;
+ if (segment_id >= ft_tracks_per_tape*ft_segments_per_track) {
+ /* tape full */
+ TRACE_ABORT(-ENOSPC, ft_t_err,
+ "invalid segment id: %d (max %d)",
+ segment_id,
+ ft_tracks_per_tape * ft_segments_per_track -1);
+ }
+ for (;;) {
+ if ((result = write_segment(segment_id, buffer, flush)) >= 0) {
+ if (result == 0) { /* empty segment */
+ TRACE(ft_t_noise,
+ "empty segment, nothing written");
+ }
+ TRACE_EXIT result;
+ }
+ if (result == -EAGAIN) {
+ if (++retry > 100) { /* give up */
+ TRACE_ABORT(-EIO, ft_t_err,
+ "write failed, >100 retries in segment");
+ }
+ TRACE(ft_t_warn, "write error, retry %d (%d)",
+ retry,
+ ftape_get_buffer(ft_queue_tail)->segment_id);
+ } else {
+ TRACE_ABORT(result, ft_t_err,
+ "write_segment failed, error: %d", result);
+ }
+ /* Allow escape from loop when signaled !
+ */
+ FT_SIGNAL_EXIT(_DONT_BLOCK);
+ }
+}
diff --git a/drivers/char/ftape/ftape-write.h b/drivers/char/ftape/lowlevel/ftape-write.h
index 2cbf07668..0e7f898b7 100644
--- a/drivers/char/ftape/ftape-write.h
+++ b/drivers/char/ftape/lowlevel/ftape-write.h
@@ -2,7 +2,8 @@
#define _FTAPE_WRITE_H
/*
- * Copyright (C) 1994-1995 Bas Laarhoven.
+ * Copyright (C) 1994-1995 Bas Laarhoven,
+ * (C) 1996-1997 Claus-Justus Heine.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -19,30 +20,34 @@
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
- $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-write.h,v $
- $Author: bas $
+ $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-write.h,v $
+ $Author: claus $
*
- $Revision: 1.13 $
- $Date: 1995/04/22 07:30:15 $
- $State: Beta $
+ $Revision: 1.2 $
+ $Date: 1997/10/05 19:18:30 $
+ $State: Exp $
*
* This file contains the definitions for the write functions
* for the QIC-117 floppy-tape driver for Linux.
*
*/
-/* ftape-write.c defined global vars.
- */
/* ftape-write.c defined global functions.
*/
-extern int _ftape_write(const char *buff, int req_len);
-extern int ftape_flush_buffers(void);
-extern int ftape_write_header_segments(byte * buffer);
-extern int ftape_update_header_segments(byte * buffer, int update);
-extern int write_segment(unsigned segment, byte * address, int flushing);
-extern int ftape_fix(void);
-extern void prevent_flush(void);
+typedef enum {
+ FT_WR_ASYNC = 0, /* start tape only when all buffers are full */
+ FT_WR_MULTI = 1, /* start tape, but don't necessarily stop */
+ FT_WR_SINGLE = 2, /* write a single segment and stop afterwards */
+ FT_WR_DELETE = 3 /* write deleted data marks */
+} ft_write_mode_t;
+
+extern int ftape_start_writing(const ft_write_mode_t mode);
+extern int ftape_write_segment(const int segment,
+ const void *address,
+ const ft_write_mode_t flushing);
extern void ftape_zap_write_buffers(void);
+extern int ftape_loop_until_writes_done(void);
#endif /* _FTAPE_WRITE_H */
+
diff --git a/drivers/char/ftape/lowlevel/ftape_syms.c b/drivers/char/ftape/lowlevel/ftape_syms.c
new file mode 100644
index 000000000..dce372118
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/ftape_syms.c
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 1996-1997 Claus-Justus Heine
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape_syms.c,v $
+ * $Revision: 1.4 $
+ * $Date: 1997/10/17 00:03:51 $
+ *
+ * This file contains the the symbols that the ftape low level
+ * part of the QIC-40/80/3010/3020 floppy-tape driver "ftape"
+ * exports to it's high level clients
+ */
+
+#include <linux/config.h>
+#define __NO_VERSION__
+#include <linux/module.h>
+
+#include <linux/ftape.h>
+#include "../lowlevel/ftape-tracing.h"
+#include "../lowlevel/ftape-init.h"
+#include "../lowlevel/fdc-io.h"
+#include "../lowlevel/ftape-read.h"
+#include "../lowlevel/ftape-write.h"
+#include "../lowlevel/ftape-io.h"
+#include "../lowlevel/ftape-ctl.h"
+#include "../lowlevel/ftape-rw.h"
+#include "../lowlevel/ftape-bsm.h"
+#include "../lowlevel/ftape-buffer.h"
+#include "../lowlevel/ftape-format.h"
+
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,18)
+# define FT_KSYM(sym) EXPORT_SYMBOL(##sym);
+#else
+# define FT_KSYM(sym) X(##sym),
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VER(2,1,18)
+struct symbol_table ftape_symbol_table = {
+#include <linux/symtab_begin.h>
+#endif
+/* bad sector handling from ftape-bsm.c */
+FT_KSYM(ftape_get_bad_sector_entry)
+FT_KSYM(ftape_find_end_of_bsm_list)
+/* from ftape-rw.c */
+FT_KSYM(ftape_set_state)
+/* from ftape-ctl.c */
+FT_KSYM(ftape_seek_to_bot)
+FT_KSYM(ftape_seek_to_eot)
+FT_KSYM(ftape_abort_operation)
+FT_KSYM(ftape_get_status)
+FT_KSYM(ftape_enable)
+FT_KSYM(ftape_disable)
+FT_KSYM(ftape_mmap)
+FT_KSYM(ftape_calibrate_data_rate)
+/* from ftape-io.c */
+FT_KSYM(ftape_reset_drive)
+FT_KSYM(ftape_command)
+FT_KSYM(ftape_parameter)
+FT_KSYM(ftape_ready_wait)
+FT_KSYM(ftape_report_operation)
+FT_KSYM(ftape_report_error)
+/* from ftape-read.c */
+FT_KSYM(ftape_read_segment_fraction)
+FT_KSYM(ftape_zap_read_buffers)
+FT_KSYM(ftape_read_header_segment)
+FT_KSYM(ftape_decode_header_segment)
+/* from ftape-write.c */
+FT_KSYM(ftape_write_segment)
+FT_KSYM(ftape_start_writing)
+FT_KSYM(ftape_loop_until_writes_done)
+/* from ftape-buffer.h */
+FT_KSYM(ftape_set_nr_buffers)
+/* from ftape-format.h */
+FT_KSYM(ftape_format_track)
+FT_KSYM(ftape_format_status)
+FT_KSYM(ftape_verify_segment)
+/* from tracing.c */
+#ifndef CONFIG_FT_NO_TRACE_AT_ALL
+FT_KSYM(ftape_tracing)
+FT_KSYM(ftape_function_nest_level)
+FT_KSYM(ftape_trace_call)
+FT_KSYM(ftape_trace_exit)
+FT_KSYM(ftape_trace_log)
+#endif
+/* end of ksym table */
+#if LINUX_VERSION_CODE < KERNEL_VER(2,1,18)
+#include <linux/symtab_end.h>
+};
+#endif
diff --git a/drivers/char/ftape/lowlevel/ftape_syms.h b/drivers/char/ftape/lowlevel/ftape_syms.h
new file mode 100644
index 000000000..d31f83565
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/ftape_syms.h
@@ -0,0 +1,39 @@
+#ifndef _FTAPE_SYMS_H
+#define _FTAPE_SYMS_H
+
+/*
+ * Copyright (C) 1996-1997 Claus-Justus Heine
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape_syms.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:32 $
+ *
+ * This file contains the definitions needed to export symbols
+ * from the QIC-40/80/3010/3020 floppy-tape driver "ftape" for
+ * Linux.
+ */
+
+#if LINUX_VERSION_CODE < KERNEL_VER(2,1,18)
+#include <linux/module.h>
+/* ftape-vfs.c defined global vars.
+ */
+
+extern struct symbol_table ftape_symbol_table;
+#endif
+
+#endif
diff --git a/drivers/char/ftape/qic117.h b/drivers/char/ftape/qic117.h
deleted file mode 100644
index 9eb9bb65f..000000000
--- a/drivers/char/ftape/qic117.h
+++ /dev/null
@@ -1,280 +0,0 @@
-#ifndef _QIC117_H
-#define _QIC117_H
-
-/*
- * Copyright (C) 1993-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING. If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- *
- $Source: /home/bas/distr/ftape-2.03b/RCS/qic117.h,v $
- $Author: bas $
- *
- $Revision: 1.27 $
- $Date: 1995/05/01 19:02:20 $
- $State: Beta $
- *
- * This file contains QIC-117 spec. related definitions
- * for the QIC-40/80 floppy-tape driver for Linux.
- *
- * These data were taken from the Quarter-Inch Cartridge
- * Drive Standards, Inc. document titled:
- * `Common Command Set Interface Specification for Flexible
- * Disk Controller Based Minicartridge Tape Drives'
- * document QIC-117 Revision C, 3 Dec 92.
- * For more information, contact:
- * Quarter-Inch Cartridge Drive Standards, Inc.
- * 311 East Carrillo Street
- * Santa Barbara, California 93101
- * Telephone (805) 963-3853
- * Fax (805) 962-1541
- *
- * Current QIC standard revisions (of interest) are:
- * QIC-80-MC, Rev. M, 15 Jun 95.
- * QIC-117, Rev. I, 15 Mar 95.
- * QIC-122, Rev. B, 6 Mar 91.
- * QIC-130, Rev. C, 2 Sep 92.
- * QIC-3010-MC, Rev. F, 14 Jun 95.
- * QIC-3020-MC, Rev. G, 31 Aug 95.
- * QIC-CRF3, Rev. B, 15 Jun 95.
- *
- */
-
-/*
- * QIC-117 common command set rev. I.
- * These commands are sent to the tape unit
- * as number of pulses over the step line.
- */
-
-#define QIC_RESET 1
-#define QIC_REPORT_NEXT_BIT 2
-#define QIC_PAUSE 3
-#define QIC_MICRO_STEP_PAUSE 4
-#define QIC_ALTERNATE_TIMEOUT 5
-#define QIC_REPORT_DRIVE_STATUS 6
-#define QIC_REPORT_ERROR_CODE 7
-#define QIC_REPORT_DRIVE_CONFIGURATION 8
-#define QIC_REPORT_ROM_VERSION 9
-#define QIC_LOGICAL_FORWARD 10
-#define QIC_PHYSICAL_REVERSE 11
-#define QIC_PHYSICAL_FORWARD 12
-#define QIC_SEEK_HEAD_TO_TRACK 13
-#define QIC_SEEK_LOAD_POINT 14
-#define QIC_ENTER_FORMAT_MODE 15
-#define QIC_WRITE_REFERENCE_BURST 16
-#define QIC_ENTER_VERIFY_MODE 17
-#define QIC_STOP_TAPE 18
-/* commands 19-20: reserved */
-#define QIC_MICRO_STEP_HEAD_UP 21
-#define QIC_MICRO_STEP_HEAD_DOWN 22
-#define QIC_SOFT_SELECT 23
-#define QIC_SOFT_DESELECT 24
-#define QIC_SKIP_REVERSE 25
-#define QIC_SKIP_FORWARD 26
-#define QIC_SELECT_RATE 27
-/* command 27, in ccs2: Select Rate or Format */
-#define QIC_ENTER_DIAGNOSTIC_1 28
-#define QIC_ENTER_DIAGNOSTIC_2 29
-#define QIC_ENTER_PRIMARY_MODE 30
-/* command 31: vendor unique */
-#define QIC_REPORT_VENDOR_ID 32
-#define QIC_REPORT_TAPE_STATUS 33
-#define QIC_SKIP_EXTENDED_REVERSE 34
-#define QIC_SKIP_EXTENDED_FORWARD 35
-#define QIC_CALIBRATE_TAPE_LENGTH 36
-#define QIC_REPORT_FORMAT_SEGMENTS 37
-#define QIC_SET_FORMAT_SEGMENTS 38
-/* commands 39-45: reserved */
-#define QIC_PHANTOM_SELECT 46
-#define QIC_PHANTOM_DESELECT 47
-
-typedef enum {
- discretional = 0, required, ccs1, ccs2
-} qic_compatibility;
-
-typedef enum {
- unused, mode, motion, report
-} command_types;
-
-struct qic117_command_table {
- char *name;
- byte mask;
- byte state;
- byte cmd_type;
- byte non_intr;
- byte level;
-};
-
-#define QIC117_COMMANDS {\
-/* command mask state cmd_type */\
-/* | name | | | non_intr */\
-/* | | | | | | level */\
-/* 0*/ {NULL, 0x00, 0x00, mode, 0, discretional},\
-/* 1*/ {"soft reset", 0x00, 0x00, motion, 1, required},\
-/* 2*/ {"report next bit", 0x00, 0x00, report, 0, required},\
-/* 3*/ {"pause", 0x36, 0x24, motion, 1, required},\
-/* 4*/ {"micro step pause", 0x36, 0x24, motion, 1, required},\
-/* 5*/ {"alternate command timeout", 0x00, 0x00, mode, 0, required},\
-/* 6*/ {"report drive status", 0x00, 0x00, report, 0, required},\
-/* 7*/ {"report error code", 0x01, 0x01, report, 0, required},\
-/* 8*/ {"report drive configuration",0x00, 0x00, report, 0, required},\
-/* 9*/ {"report rom version", 0x00, 0x00, report, 0, required},\
-/*10*/ {"logical forward", 0x37, 0x25, motion, 0, required},\
-/*11*/ {"physical reverse", 0x17, 0x05, motion, 0, required},\
-/*12*/ {"physical forward", 0x17, 0x05, motion, 0, required},\
-/*13*/ {"seek head to track", 0x37, 0x25, motion, 0, required},\
-/*14*/ {"seek load point", 0x17, 0x05, motion, 1, required},\
-/*15*/ {"enter format mode", 0x1f, 0x05, mode, 0, required},\
-/*16*/ {"write reference burst", 0x1f, 0x05, motion, 1, required},\
-/*17*/ {"enter verify mode", 0x37, 0x25, mode, 0, required},\
-/*18*/ {"stop tape", 0x00, 0x00, motion, 1, required},\
-/*19*/ {"reserved (19)", 0x00, 0x00, unused, 0, discretional},\
-/*20*/ {"reserved (20)", 0x00, 0x00, unused, 0, discretional},\
-/*21*/ {"micro step head up", 0x02, 0x00, motion, 0, required},\
-/*22*/ {"micro step head down", 0x02, 0x00, motion, 0, required},\
-/*23*/ {"soft select", 0x00, 0x00, mode, 0, discretional},\
-/*24*/ {"soft deselect", 0x00, 0x00, mode, 0, discretional},\
-/*25*/ {"skip segments reverse", 0x36, 0x24, motion, 1, required},\
-/*26*/ {"skip segments forward", 0x36, 0x24, motion, 1, required},\
-/*27*/ {"select rate [or format]", 0x03, 0x01, mode, 0, required /* [ccs2] */},\
-/*28*/ {"enter diag mode 1", 0x00, 0x00, mode, 0, discretional},\
-/*29*/ {"enter diag mode 2", 0x00, 0x00, mode, 0, discretional},\
-/*30*/ {"enter primary mode", 0x00, 0x00, mode, 0, required},\
-/*31*/ {"vendor unique (31)", 0x00, 0x00, unused, 0, discretional},\
-/*32*/ {"report vendor id", 0x00, 0x00, report, 0, required},\
-/*33*/ {"report tape status", 0x04, 0x04, report, 0, ccs1},\
-/*34*/ {"skip extended reverse", 0x36, 0x24, motion, 1, ccs1},\
-/*35*/ {"skip extended forward", 0x36, 0x24, motion, 1, ccs1},\
-/*36*/ {"calibrate tape length", 0x17, 0x05, motion, 1, ccs2},\
-/*37*/ {"report format segments", 0x17, 0x05, report, 0, ccs2},\
-/*38*/ {"set format segments", 0x17, 0x05, mode, 0, ccs2},\
-/*39*/ {"reserved (39)", 0x00, 0x00, unused, 0, discretional},\
-/*40*/ {"vendor unique (40)", 0x00, 0x00, unused, 0, discretional},\
-/*41*/ {"vendor unique (41)", 0x00, 0x00, unused, 0, discretional},\
-/*42*/ {"vendor unique (42)", 0x00, 0x00, unused, 0, discretional},\
-/*43*/ {"vendor unique (43)", 0x00, 0x00, unused, 0, discretional},\
-/*44*/ {"vendor unique (44)", 0x00, 0x00, unused, 0, discretional},\
-/*45*/ {"vendor unique (45)", 0x00, 0x00, unused, 0, discretional},\
-/*46*/ {"phantom select", 0x00, 0x00, mode, 0, discretional},\
-/*47*/ {"phantom deselect", 0x00, 0x00, mode, 0, discretional},\
-}
-
-/*
- * Status bits returned by QIC_REPORT_DRIVE_STATUS
- */
-
-#define QIC_STATUS_READY 0x01 /* Drive is ready or idle. */
-#define QIC_STATUS_ERROR 0x02 /* Error detected, must read
- error code to clear this */
-#define QIC_STATUS_CARTRIDGE_PRESENT 0x04 /* Tape is present */
-#define QIC_STATUS_WRITE_PROTECT 0x08 /* Tape is write protected */
-#define QIC_STATUS_NEW_CARTRIDGE 0x10 /* New cartridge inserted, must
- read error status to clear. */
-#define QIC_STATUS_REFERENCED 0x20 /* Cartridge appears to have been
- formatted. */
-#define QIC_STATUS_AT_BOT 0x40 /* Cartridge is at physical
- beginning of tape. */
-#define QIC_STATUS_AT_EOT 0x80 /* Cartridge is at physical end
- of tape. */
-/*
- * Status bits returned by QIC_REPORT_DRIVE_CONFIGURATION
- */
-
-#define QIC_CONFIG_RATE_MASK 0x18
-#define QIC_CONFIG_RATE_SHIFT 3
-#define QIC_CONFIG_RATE_250 0
-#define QIC_CONFIG_RATE_500 2
-#define QIC_CONFIG_RATE_1000 3
-#define QIC_CONFIG_RATE_2000 1
-
-#define QIC_CONFIG_LONG 0x40 /* Extra Length Tape Detected */
-#define QIC_CONFIG_80 0x80 /* QIC-80 detected. */
-
-/*
- * Status bits returned by QIC_REPORT_TAPE_STATUS
- */
-
-#define QIC_TAPE_STD_MASK 0x0f
-#define QIC_TAPE_QIC40 0x01
-#define QIC_TAPE_QIC80 0x02
-#define QIC_TAPE_QIC3020 0x03
-#define QIC_TAPE_QIC3010 0x04
-
-#define QIC_TAPE_LEN_MASK 0x70
-#define QIC_TAPE_205FT 0x10
-#define QIC_TAPE_307FT 0x20
-#define QIC_TAPE_400FT 0x30
-#define QIC_TAPE_1100FT 0x40
-#define QIC_TAPE_FLEX 0x60
-
-#define QIC_TAPE_WIDE 0x80
-
-/*
- * Errors: List of error codes, and their severity.
- */
-
-typedef struct {
- char *message; /* Text describing the error. */
- int fatal; /* Non-zero if the error is fatal. */
-} ftape_error;
-
-#define QIC117_ERRORS {\
- /* 0*/ { "No error", 0, },\
- /* 1*/ { "Command Received while Drive Not Ready", 0, },\
- /* 2*/ { "Cartridge Not Present or Removed", 1, },\
- /* 3*/ { "Motor Speed Error (not within 1%)", 1, },\
- /* 4*/ { "Motor Speed Fault (jammed, or gross speed error", 1, },\
- /* 5*/ { "Cartridge Write Protected", 1, },\
- /* 6*/ { "Undefined or Reserved Command Code", 1, },\
- /* 7*/ { "Illegal Track Address Specified for Seek", 1, },\
- /* 8*/ { "Illegal Command in Report Subcontext", 0, },\
- /* 9*/ { "Illegal Entry into a Diagnostic Mode", 1, },\
- /*10*/ { "Broken Tape Detected (based on hole sensor)", 1, },\
- /*11*/ { "Warning--Read Gain Setting Error", 1, },\
- /*12*/ { "Command Received While Error Status Pending (obs)", 1, },\
- /*13*/ { "Command Received While New Cartridge Pending", 1, },\
- /*14*/ { "Command Illegal or Undefined in Primary Mode", 1, },\
- /*15*/ { "Command Illegal or Undefined in Format Mode", 1, },\
- /*16*/ { "Command Illegal or Undefined in Verify Mode", 1, },\
- /*17*/ { "Logical Forward Not a Logical BOT or no Format Segments in Format Mode", 1, },\
- /*18*/ { "Logical EOT Before All Segments generated", 1, },\
- /*19*/ { "Command Illegal When Cartridge Not Referenced", 1, },\
- /*20*/ { "Self-Diagnostic Failed (cannot be cleared)", 1, },\
- /*21*/ { "Warning EEPROM Not Initialized, Defaults Set", 1, },\
- /*22*/ { "EEPROM Corrupted or Hardware Failure", 1, },\
- /*23*/ { "Motion Time-out Error", 1, },\
- /*24*/ { "Data Segment Too Long -- Logical Forward or Pause", 1, },\
- /*25*/ { "Transmit Overrun (obs)", 1, },\
- /*26*/ { "Power On Reset Occurred", 0, },\
- /*27*/ { "Software Reset Occurred", 0, },\
- /*28*/ { "Diagnostic Mode 1 Error", 1, },\
- /*29*/ { "Diagnostic Mode 2 Error", 1, },\
- /*30*/ { "Command Received During Non-Interruptible Process", 1, },\
- /*31*/ { "Rate or Format Selection Error", 1, },\
- /*32*/ { "Illegal Command While in High Speed Mode", 1, },\
- /*33*/ { "Illegal Seek Segment Value", 1, },\
- /*34*/ { "Invalid Media", 1, },\
- /*35*/ { "Head Positioning Failure", 1, },\
- /*36*/ { "Write Reference Burst Failure", 1, },\
- /*37*/ { "Prom Code Missing", 1, },\
- /*38*/ { "Invalid Format", 1, },\
- /*39*/ { "EOT/BOT System Failure", 1, },\
- /*40*/ { "Prom A Checksum Error", 1, },\
- /*41*/ { "Drive Wakeup Reset Occurred", 1, },\
- /*42*/ { "Prom B Checksum Error", 1, },\
- /*43*/ { "Illegal Entry into Format Mode", 1, },\
-}
-
-#endif /* _QIC117_H */
diff --git a/drivers/char/ftape/tracing.c b/drivers/char/ftape/tracing.c
deleted file mode 100644
index a2d1a4268..000000000
--- a/drivers/char/ftape/tracing.c
+++ /dev/null
@@ -1,103 +0,0 @@
-
-/*
- * Copyright (C) 1993-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING. If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- *
- * This file contains the reading code
- * for the QIC-117 floppy-tape driver for Linux.
- */
-
-#include <linux/ftape.h>
-
-#include "tracing.h"
-
-/* Global vars.
- */
-/* tracing
- * set it to: to get:
- * 0 bugs
- * 1 + errors
- * 2 + warnings
- * 3 + information
- * 4 + more information
- * 5 + program flow
- * 6 + fdc/dma info
- * 7 + data flow
- * 8 + everything else
- */
-int tracing = 3; /* Default level: report only errors */
-
-#ifndef NO_TRACE_AT_ALL
-
-byte trace_id = 0;
-int function_nest_level = 0;
-
-/* Local vars.
- */
-static char spacing[] = "* ";
-
-int trace_call(int level, char *file, char *name)
-{
- char *indent;
-
- if (tracing >= level && level <= TOP_LEVEL) {
- /* Since printk seems not to work with "%*s" format
- * we'll use this work-around.
- */
- if (function_nest_level < sizeof(spacing)) {
- indent = spacing + sizeof(spacing) - 1 - function_nest_level;
- } else {
- indent = spacing;
- }
- printk(KERN_INFO "[%03d]%s+%s (%s)\n", (int) trace_id++, indent, file, name);
- }
- return function_nest_level++;
-}
-
-void trace_exit(int level, char *file, char *name)
-{
- char *indent;
-
- if (tracing >= level && level <= TOP_LEVEL) {
- /* Since printk seems not to work with "%*s" format
- * we'll use this work-around.
- */
- if (function_nest_level < sizeof(spacing)) {
- indent = spacing + sizeof(spacing) - 1 - function_nest_level;
- } else {
- indent = spacing;
- }
- printk(KERN_INFO "[%03d]%s-%s (%s)\n", (int) trace_id++, indent, file, name);
- }
-}
-
-void trace_log(char *file, char *name)
-{
- char *indent;
-
- /* Since printk seems not to work with "%*s" format
- * we'll use this work-around.
- */
- if (function_nest_level < sizeof(spacing)) {
- indent = spacing + sizeof(spacing) - 1 - function_nest_level;
- } else {
- indent = spacing;
- }
- printk(KERN_INFO "[%03d]%s%s (%s) - ", (int) trace_id++, indent, file, name);
-}
-
-#endif /* NO_TRACE_AT_ALL */
diff --git a/drivers/char/ftape/tracing.h b/drivers/char/ftape/tracing.h
deleted file mode 100644
index 9341bc5af..000000000
--- a/drivers/char/ftape/tracing.h
+++ /dev/null
@@ -1,99 +0,0 @@
-#ifndef _TRACING_H
-#define _TRACING_H
-
-/*
- * Copyright (C) 1994-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING. If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- *
- $Source: /home/bas/distr/ftape-2.03b/RCS/tracing.h,v $
- $Author: bas $
- *
- $Revision: 1.10 $
- $Date: 1995/04/22 07:30:15 $
- $State: Beta $
- *
- * This file contains definitions that eases the debugging
- * of the QIC-40/80 floppy-tape driver for Linux.
- */
-
-#include <linux/kernel.h>
-
-#ifdef NO_TRACE_AT_ALL
-static inline void trace_dummy(void)
-{
-}
-
-#define TRACE_FUN( level, name) int _trace_dummy
-#define TRACE_EXIT _trace_dummy= 0
-#define TRACE_(l,m) trace_dummy()
-#define TRACE(l,m) trace_dummy()
-#define TRACEi(l,m,i) trace_dummy()
-#define TRACElx(l,m,i) trace_dummy()
-#define TRACEx1(l,m,a) trace_dummy()
-#define TRACEx2(l,m,a,b) trace_dummy()
-#define TRACEx3(l,m,a,b,c) trace_dummy()
-#define TRACEx4(l,m,a,b,c,d) trace_dummy()
-#define TRACEx5(l,m,a,b,c,d,e) trace_dummy()
-#define TRACEx6(l,m,a,b,c,d,e,f) trace_dummy()
-#else
-#ifdef NO_TRACE
-#define TOP_LEVEL 2
-#else
-#define TOP_LEVEL 10
-#endif
-
-#define TRACE_FUN( level, name) \
- char _trace_fun[] = name; \
- int _function_nest_level = trace_call( level, __FILE__, _trace_fun); \
- int _tracing = level
-
-#define TRACE_EXIT \
- function_nest_level = _function_nest_level; \
- trace_exit( _tracing, __FILE__, _trace_fun)
-
-#define TRACE_(l,m) \
-{ \
- if (tracing >= (l) && (l) <= TOP_LEVEL) { \
- trace_log( __FILE__, _trace_fun); \
- m; \
- } \
-}
-#define TRACE(l,m) TRACE_(l,printk(m".\n"))
-#define TRACEi(l,m,i) TRACE_(l,printk(m" %d.\n",i))
-#define TRACElx(l,m,i) TRACE_(l,printk(m" 0x%08lx.\n",i))
-#define TRACEx1(l,m,a) TRACE_(l,printk(m".\n",a))
-#define TRACEx2(l,m,a,b) TRACE_(l,printk(m".\n",a,b))
-#define TRACEx3(l,m,a,b,c) TRACE_(l,printk(m".\n",a,b,c))
-#define TRACEx4(l,m,a,b,c,d) TRACE_(l,printk(m".\n",a,b,c,d))
-#define TRACEx5(l,m,a,b,c,d,e) TRACE_(l,printk(m".\n",a,b,c,d,e))
-#define TRACEx6(l,m,a,b,c,d,e,f) TRACE_(l,printk(m".\n",a,b,c,d,e,f))
-
-/* Global variables declared in tracing.c
- */
-extern unsigned char trace_id;
-extern int tracing; /* sets default level */
-extern int function_nest_level;
-
-/* Global functions declared in tracing.c
- */
-extern int trace_call(int level, char *file, char *name);
-extern void trace_exit(int level, char *file, char *name);
-extern void trace_log(char *file, char *name);
-
-#endif /* NO_TRACE_AT_ALL */
-
-#endif
diff --git a/drivers/char/ftape/vendors.h b/drivers/char/ftape/vendors.h
deleted file mode 100644
index 9f8e39839..000000000
--- a/drivers/char/ftape/vendors.h
+++ /dev/null
@@ -1,127 +0,0 @@
-#ifndef _VENDORS_H
-#define _VENDORS_H
-
-/*
- * Copyright (C) 1993-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING. If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- *
- $Source: /home/bas/distr/ftape-2.03b/RCS/vendors.h,v $
- $Author: bas $
- *
- $Revision: 1.34 $
- $Date: 1995/05/27 08:54:21 $
- $State: Beta $
- *
- * This file contains the supported drive types
- * with their QIC-117 spec. vendor code and
- * drive dependent configuration information.
- */
-
-typedef enum {
- unknown_wake_up = 0,
- no_wake_up,
- wake_up_colorado,
- wake_up_mountain,
- wake_up_insight,
-} wake_up_types;
-
-typedef struct {
- wake_up_types wake_up; /* see wake_up_types */
- char *name; /* Text describing the drive */
-} wakeup_method;
-
-/* Note: order of entries in WAKEUP_METHODS must be so that a variable
- * of type wake_up_types can be used as an index in the array.
- */
-#define WAKEUP_METHODS { \
- { unknown_wake_up, "Unknown" }, \
- { no_wake_up, "None" }, \
- { wake_up_colorado, "Colorado" }, \
- { wake_up_mountain, "Mountain" }, \
- { wake_up_insight, "Motor-on" }, \
-}
-
-typedef struct {
- unsigned int vendor_id; /* vendor id from drive */
- int speed; /* maximum tape transport speed (ips) */
- wake_up_types wake_up; /* see wake_up_types */
- char *name; /* Text describing the drive */
-} vendor_struct;
-
-#define UNKNOWN_VENDOR (-1)
-
-#define QIC117_VENDORS { \
-/* see _vendor_struct */ \
- { 0x00000, 82, wake_up_colorado, "Colorado DJ-10 (old)" }, \
- { 0x00047, 90, wake_up_colorado, "Colorado DJ-10/DJ-20" }, \
- { 0x011c2, 84, wake_up_colorado, "Colorado 700" }, \
- { 0x011c3, 90, wake_up_colorado, "Colorado 1400" }, \
- { 0x011c4, 84, wake_up_colorado, "Colorado DJ-10/DJ-20 (new)" }, \
- { 0x011c5, 84, wake_up_colorado, "HP Colorado T1000" }, \
- { 0x00005, 45, wake_up_mountain, "Archive 5580i" }, \
- { 0x10005, 50, wake_up_insight, "Insight 80Mb, Irwin 80SX" }, \
- { 0x00140, 74, wake_up_mountain, "Archive S.Hornet [Identity/Escom]" }, \
- { 0x00146, 72, wake_up_mountain, "Archive 31250Q [Escom]" }, \
- { 0x0014a, 100, wake_up_mountain, "Archive XL9250i [Conner/Escom]" }, \
- { 0x0014c, 98, wake_up_mountain, "Conner C250MQT" }, \
- { 0x0014e, 80, wake_up_mountain, "Conner C250MQ" }, \
- { 0x00150, 80, wake_up_mountain, "Conner TSM420R/TST800R" }, \
- { 0x00152, 80, wake_up_mountain, "Conner TSM850R" }, \
- { 0x00156, 80, wake_up_mountain, "Conner TSM850R/1700R/TST3200R" }, \
- { 0x00180, 0, wake_up_mountain, "Summit SE 150" }, \
- { 0x00181, 85, wake_up_mountain, "Summit SE 250, Mountain FS8000" }, \
- { 0x001c1, 82, no_wake_up, "Wangtek 3040F" }, \
- { 0x001c8, 64, no_wake_up, "Wangtek 3080F" }, \
- { 0x001c8, 64, wake_up_colorado, "Wangtek 3080F" }, \
- { 0x001ca, 67, no_wake_up, "Wangtek 3080F (new)" }, \
- { 0x001cc, 77, wake_up_colorado, "Wangtek 3200 / Teac 700" }, \
- { 0x001cd, 75, wake_up_colorado, "Reveal TB1400" }, \
- { 0x00380, 0, wake_up_colorado, "Exabyte EXB-1500" }, \
- { 0x08880, 64, no_wake_up, "Iomega 250" }, \
- { 0x08880, 64, wake_up_colorado, "Iomega 250, Ditto 800" }, \
- { 0x08880, 64, wake_up_insight, "Iomega 250" }, \
- { 0x08881, 0, wake_up_colorado, "Iomega 700" }, \
- { 0x08882, 80, wake_up_colorado, "Iomega 3200" }, \
- { 0x00021, 70, no_wake_up, "AIWA CT-803" }, \
- { 0x00021, 0, wake_up_mountain, "COREtape QIC80" }, \
-}
-
-#define QIC117_MAKE_CODES { \
- { 0, "Unassigned" }, \
- { 1, "Alloy Computer Products" }, \
- { 2, "3M" }, \
- { 3, "Tandberg Data" }, \
- { 4, "Colorado" }, \
- { 5, "Archive/Conner" }, \
- { 6, "Mountain/Summit Memory Systems" }, \
- { 7, "Wangtek/Rexon/Tecmar" }, \
- { 8, "Sony" }, \
- { 9, "Cipher Data Products" }, \
- { 10, "Irwin Magnetic Systems" }, \
- { 11, "Braemar" }, \
- { 12, "Verbatim" }, \
- { 13, "Core International" }, \
- { 14, "Exabyte" }, \
- { 15, "Teac" }, \
- { 16, "Gigatek" }, \
- { 17, "ComByte" }, \
- { 18, "PERTEC Memories" }, \
- { 71, "Colorado" }, \
- { 546, "Iomega Inc" }, \
-}
-
-#endif
diff --git a/drivers/char/ftape/zftape/.cvsignore b/drivers/char/ftape/zftape/.cvsignore
new file mode 100644
index 000000000..4671378ae
--- /dev/null
+++ b/drivers/char/ftape/zftape/.cvsignore
@@ -0,0 +1 @@
+.depend
diff --git a/drivers/char/ftape/zftape/Makefile b/drivers/char/ftape/zftape/Makefile
new file mode 100644
index 000000000..f0cb73a96
--- /dev/null
+++ b/drivers/char/ftape/zftape/Makefile
@@ -0,0 +1,54 @@
+#
+# Copyright (C) 1996, 1997 Claus-Justus Heine.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; see the file COPYING. If not, write to
+# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+# $Source: /homes/cvs/ftape-stacked/ftape/zftape/Makefile,v $
+# $Revision: 1.4 $
+# $Date: 1997/10/05 19:18:58 $
+#
+# Makefile for the QIC-40/80/3010/3020 zftape interface VFS to
+# ftape
+#
+
+#
+# This isn't used inside the kernel, only for my private development
+# version
+#
+ifndef TOPDIR
+TOPDIR=../..
+include $(TOPDIR)/MCONFIG
+endif
+
+SUB_DIRS :=
+MOD_SUB_DIRS := $(SUB_DIRS)
+ALL_SUB_DIRS := $(SUB_DIRS)
+
+# ZFT_OBSOLETE - enable the MTIOC_ZFTAPE_GETBLKSZ ioctl. You should
+# leave this enabled for compatibility with taper.
+
+EXTRA_CFLAGS := -DZFT_OBSOLETE
+
+O_TARGET := zftape.o
+O_OBJS := zftape-rw.o zftape-ctl.o zftape-read.o \
+ zftape-write.o zftape-vtbl.o zftape-eof.o \
+ zftape-init.o zftape-buffers.o
+
+OX_OBJS += zftape_syms.o
+
+M_OBJS := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
+
diff --git a/drivers/char/ftape/zftape/zftape-buffers.c b/drivers/char/ftape/zftape/zftape-buffers.c
new file mode 100644
index 000000000..13594d02a
--- /dev/null
+++ b/drivers/char/ftape/zftape/zftape-buffers.c
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 1995-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-buffers.c,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:59 $
+ *
+ * This file contains the dynamic buffer allocation routines
+ * of zftape
+ */
+
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <asm/segment.h>
+
+#include <linux/zftape.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,0)
+#include <linux/vmalloc.h>
+#endif
+
+#include "../zftape/zftape-init.h"
+#include "../zftape/zftape-eof.h"
+#include "../zftape/zftape-ctl.h"
+#include "../zftape/zftape-write.h"
+#include "../zftape/zftape-read.h"
+#include "../zftape/zftape-rw.h"
+#include "../zftape/zftape-vtbl.h"
+
+/* global variables
+ */
+
+/* local varibales
+ */
+static unsigned int used_memory = 0;
+static unsigned int peak_memory = 0;
+
+void zft_memory_stats(void)
+{
+ TRACE_FUN(ft_t_flow);
+
+ TRACE(ft_t_noise, "Memory usage (vmalloc allocations):\n"
+ KERN_INFO "total allocated: %d\n"
+ KERN_INFO "peak allocation: %d",
+ used_memory, peak_memory);
+ peak_memory = used_memory;
+ TRACE_EXIT;
+}
+
+int zft_vcalloc_once(void *new, size_t size)
+{
+ TRACE_FUN(ft_t_flow);
+ if (zft_vmalloc_once(new, size) < 0) {
+ TRACE_EXIT -ENOMEM;
+ }
+ memset(*(void **)new, '\0', size);
+ TRACE_EXIT 0;
+}
+int zft_vmalloc_once(void *new, size_t size)
+{
+ TRACE_FUN(ft_t_flow);
+
+ if (*(void **)new != NULL || size == 0) {
+ TRACE_EXIT 0;
+ }
+ if ((*(void **)new = vmalloc(size)) == NULL) {
+ TRACE_EXIT -ENOMEM;
+ }
+ used_memory += size;
+ if (peak_memory < used_memory) {
+ peak_memory = used_memory;
+ }
+ TRACE_ABORT(0, ft_t_noise,
+ "allocated buffer @ %p, %d bytes", *(void **)new, size);
+}
+int zft_vcalloc_always(void *new, size_t size)
+{
+ TRACE_FUN(ft_t_flow);
+
+ zft_vfree(new, size);
+ TRACE_EXIT zft_vcalloc_once(new, size);
+}
+int zft_vmalloc_always(void *new, size_t size)
+{
+ TRACE_FUN(ft_t_flow);
+
+ zft_vfree(new, size);
+ TRACE_EXIT zft_vmalloc_once(new, size);
+}
+void zft_vfree(void *old, size_t size)
+{
+ TRACE_FUN(ft_t_flow);
+
+ if (*(void **)old) {
+ vfree(*(void **)old);
+ used_memory -= size;
+ TRACE(ft_t_noise, "released buffer @ %p, %d bytes",
+ *(void **)old, size);
+ *(void **)old = NULL;
+ }
+ TRACE_EXIT;
+}
+
+void *zft_kmalloc(size_t size)
+{
+ void *new;
+
+ while ((new = kmalloc(size, GFP_KERNEL)) == NULL) {
+ current->timeout = HZ/10;
+ current->state = TASK_INTERRUPTIBLE;
+ schedule();
+ }
+ memset(new, 0, size);
+ used_memory += size;
+ if (peak_memory < used_memory) {
+ peak_memory = used_memory;
+ }
+ return new;
+}
+
+void zft_kfree(void *old, size_t size)
+{
+ kfree(old);
+ used_memory -= size;
+}
+
+/* there are some more buffers that are allocated on demand.
+ * cleanup_module() calles this function to be sure to have released
+ * them
+ */
+void zft_uninit_mem(void)
+{
+ TRACE_FUN(ft_t_flow);
+
+ zft_vfree(&zft_hseg_buf, FT_SEGMENT_SIZE);
+ zft_vfree(&zft_deblock_buf, FT_SEGMENT_SIZE); zft_deblock_segment = -1;
+ zft_free_vtbl();
+ if (zft_cmpr_lock(0 /* don't load */) == 0) {
+ (*zft_cmpr_ops->cleanup)();
+ (*zft_cmpr_ops->reset)(); /* unlock it again */
+ }
+ zft_memory_stats();
+ TRACE_EXIT;
+}
diff --git a/drivers/char/ftape/zftape/zftape-buffers.h b/drivers/char/ftape/zftape/zftape-buffers.h
new file mode 100644
index 000000000..c61498495
--- /dev/null
+++ b/drivers/char/ftape/zftape/zftape-buffers.h
@@ -0,0 +1,56 @@
+#ifndef _FTAPE_DYNMEM_H
+#define _FTAPE_DYNMEM_H
+
+/*
+ * Copyright (C) 1995-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-buffers.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:59 $
+ *
+ * memory allocation routines.
+ *
+ */
+
+/* we do not allocate all of the really large buffer memory before
+ * someone tries to open the drive. ftape_open() may fail with
+ * -ENOMEM, but that's better having 200k of vmalloced memory which
+ * cannot be swapped out.
+ */
+
+extern void zft_memory_stats(void);
+extern int zft_vmalloc_once(void *new, size_t size);
+extern int zft_vcalloc_once(void *new, size_t size);
+extern int zft_vmalloc_always(void *new, size_t size);
+extern int zft_vcalloc_always(void *new, size_t size);
+extern void zft_vfree(void *old, size_t size);
+extern void *zft_kmalloc(size_t size);
+extern void zft_kfree(void *old, size_t size);
+
+/* called by cleanup_module()
+ */
+extern void zft_uninit_mem(void);
+
+#endif
+
+
+
+
+
+
+
diff --git a/drivers/char/ftape/zftape/zftape-ctl.c b/drivers/char/ftape/zftape/zftape-ctl.c
new file mode 100644
index 000000000..19d10c95b
--- /dev/null
+++ b/drivers/char/ftape/zftape/zftape-ctl.c
@@ -0,0 +1,1502 @@
+/*
+ * Copyright (C) 1996, 1997 Claus-Justus Heine
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-ctl.c,v $
+ * $Revision: 1.2.6.2 $
+ * $Date: 1997/11/14 18:07:33 $
+ *
+ * This file contains the non-read/write zftape functions
+ * for the QIC-40/80/3010/3020 floppy-tape driver for Linux.
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#define __NO_VERSION__
+#include <linux/module.h>
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
+#endif
+#include <linux/fcntl.h>
+
+#include <linux/zftape.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,6)
+#include <asm/uaccess.h>
+#else
+#include <asm/segment.h>
+#endif
+
+#include "../zftape/zftape-init.h"
+#include "../zftape/zftape-eof.h"
+#include "../zftape/zftape-ctl.h"
+#include "../zftape/zftape-write.h"
+#include "../zftape/zftape-read.h"
+#include "../zftape/zftape-rw.h"
+#include "../zftape/zftape-vtbl.h"
+
+/* Global vars.
+ */
+int zft_write_protected = 0; /* this is when cartridge rdonly or O_RDONLY */
+int zft_header_read = 0;
+int zft_offline = 0;
+unsigned int zft_unit = 0;
+int zft_resid = 0;
+int zft_mt_compression = 0;
+
+/* Local vars.
+ */
+static int going_offline = 0;
+
+typedef int (mt_fun)(int *argptr);
+typedef int (*mt_funp)(int *argptr);
+typedef struct
+{
+ mt_funp function;
+ unsigned offline : 1; /* op permitted if offline or no_tape */
+ unsigned write_protected : 1; /* op permitted if write-protected */
+ unsigned not_formatted : 1; /* op permitted if tape not formatted */
+ unsigned raw_mode : 1; /* op permitted if zft_mode == 0 */
+ unsigned need_idle_state : 1; /* need to call def_idle_state */
+ char *name;
+} fun_entry;
+
+static mt_fun mt_dummy, mt_reset, mt_fsr, mt_bsr, mt_rew, mt_offl, mt_nop,
+ mt_weof, mt_erase, mt_ras2, mt_setblk, mt_setdensity,
+ mt_seek, mt_tell, mt_reten, mt_eom, mt_fsf, mt_bsf,
+ mt_fsfm, mt_bsfm, mt_setdrvbuffer, mt_compression;
+
+static fun_entry mt_funs[]=
+{
+ {mt_reset , 1, 1, 1, 1, 0, "MT_RESET" }, /* 0 */
+ {mt_fsf , 0, 1, 0, 0, 1, "MT_FSF" },
+ {mt_bsf , 0, 1, 0, 0, 1, "MT_BSF" },
+ {mt_fsr , 0, 1, 0, 1, 1, "MT_FSR" },
+ {mt_bsr , 0, 1, 0, 1, 1, "MT_BSR" },
+ {mt_weof , 0, 0, 0, 0, 0, "MT_WEOF" }, /* 5 */
+ {mt_rew , 0, 1, 1, 1, 0, "MT_REW" },
+ {mt_offl , 0, 1, 1, 1, 0, "MT_OFFL" },
+ {mt_nop , 1, 1, 1, 1, 0, "MT_NOP" },
+ {mt_reten , 0, 1, 1, 1, 0, "MT_RETEN" },
+ {mt_bsfm , 0, 1, 0, 0, 1, "MT_BSFM" }, /* 10 */
+ {mt_fsfm , 0, 1, 0, 0, 1, "MT_FSFM" },
+ {mt_eom , 0, 1, 0, 0, 1, "MT_EOM" },
+ {mt_erase , 0, 0, 0, 1, 0, "MT_ERASE" },
+ {mt_dummy , 1, 1, 1, 1, 0, "MT_RAS1" },
+ {mt_ras2 , 0, 0, 0, 1, 0, "MT_RAS2" },
+ {mt_dummy , 1, 1, 1, 1, 0, "MT_RAS3" },
+ {mt_dummy , 1, 1, 1, 1, 0, "UNKNOWN" },
+ {mt_dummy , 1, 1, 1, 1, 0, "UNKNOWN" },
+ {mt_dummy , 1, 1, 1, 1, 0, "UNKNOWN" },
+ {mt_setblk , 1, 1, 1, 1, 1, "MT_SETBLK"}, /* 20 */
+ {mt_setdensity , 1, 1, 1, 1, 0, "MT_SETDENSITY"},
+ {mt_seek , 0, 1, 0, 1, 1, "MT_SEEK" },
+ {mt_dummy , 0, 1, 0, 1, 1, "MT_TELL" }, /* wr-only ?! */
+ {mt_setdrvbuffer, 1, 1, 1, 1, 0, "MT_SETDRVBUFFER" },
+ {mt_dummy , 1, 1, 1, 1, 0, "MT_FSS" }, /* 25 */
+ {mt_dummy , 1, 1, 1, 1, 0, "MT_BSS" },
+ {mt_dummy , 1, 1, 1, 1, 0, "MT_WSM" },
+ {mt_dummy , 1, 1, 1, 1, 0, "MT_LOCK" },
+ {mt_dummy , 1, 1, 1, 1, 0, "MT_UNLOCK"},
+ {mt_dummy , 1, 1, 1, 1, 0, "MT_LOAD" }, /* 30 */
+ {mt_dummy , 1, 1, 1, 1, 0, "MT_UNLOAD"},
+ {mt_compression , 1, 1, 1, 0, 1, "MT_COMPRESSION"},
+ {mt_dummy , 1, 1, 1, 1, 0, "MT_SETPART"},
+ {mt_dummy , 1, 1, 1, 1, 0, "MT_MKPART"}
+};
+
+#define NR_MT_CMDS NR_ITEMS(mt_funs)
+
+void zft_reset_position(zft_position *pos)
+{
+ TRACE_FUN(ft_t_flow);
+
+ pos->seg_byte_pos =
+ pos->volume_pos = 0;
+ if (zft_header_read) {
+ /* need to keep track of the volume table and
+ * compression map. We therefor simply
+ * position at the beginning of the first
+ * volume. This covers old ftape archives as
+ * well has various flavours of the
+ * compression map segments. The worst case is
+ * that the compression map shows up as a
+ * additional volume in front of all others.
+ */
+ pos->seg_pos = zft_find_volume(0)->start_seg;
+ pos->tape_pos = zft_calc_tape_pos(pos->seg_pos);
+ } else {
+ pos->tape_pos = 0;
+ pos->seg_pos = -1;
+ }
+ zft_just_before_eof = 0;
+ zft_deblock_segment = -1;
+ zft_io_state = zft_idle;
+ zft_zap_read_buffers();
+ zft_prevent_flush();
+ /* unlock the compresison module if it is loaded.
+ * The zero arg means not to try to load the module.
+ */
+ if (zft_cmpr_lock(0) == 0) {
+ (*zft_cmpr_ops->reset)(); /* unlock */
+ }
+ TRACE_EXIT;
+}
+
+static void zft_init_driver(void)
+{
+ TRACE_FUN(ft_t_flow);
+
+ zft_resid =
+ zft_header_read =
+ zft_old_ftape =
+ zft_offline =
+ zft_write_protected =
+ going_offline =
+ zft_mt_compression =
+ zft_header_changed =
+ zft_volume_table_changed =
+ zft_written_segments = 0;
+ zft_blk_sz = CONFIG_ZFT_DFLT_BLK_SZ;
+ zft_reset_position(&zft_pos); /* does most of the stuff */
+ ftape_zap_read_buffers();
+ ftape_set_state(idle);
+ TRACE_EXIT;
+}
+
+int zft_def_idle_state(void)
+{
+ int result = 0;
+ TRACE_FUN(ft_t_flow);
+
+ if (!zft_header_read) {
+ result = zft_read_header_segments();
+ } else if ((result = zft_flush_buffers()) >= 0 && zft_qic_mode) {
+ /* don't move past eof
+ */
+ (void)zft_close_volume(&zft_pos);
+ }
+ if (ftape_abort_operation() < 0) {
+ TRACE(ft_t_warn, "ftape_abort_operation() failed");
+ result = -EIO;
+ }
+ /* clear remaining read buffers */
+ zft_zap_read_buffers();
+ zft_io_state = zft_idle;
+ TRACE_EXIT result;
+}
+
+/*****************************************************************************
+ * *
+ * functions for the MTIOCTOP commands *
+ * *
+ *****************************************************************************/
+
+static int mt_dummy(int *dummy)
+{
+ TRACE_FUN(ft_t_flow);
+
+ TRACE_EXIT -ENOSYS;
+}
+
+static int mt_reset(int *dummy)
+{
+ TRACE_FUN(ft_t_flow);
+
+ (void)ftape_seek_to_bot();
+ TRACE_CATCH(ftape_reset_drive(),
+ zft_init_driver(); zft_uninit_mem(); zft_offline = 1);
+ /* fake a re-open of the device. This will set all flage and
+ * allocate buffers as appropriate. The new tape condition will
+ * force the open routine to do anything we need.
+ */
+ TRACE_CATCH(_zft_open(-1 /* fake reopen */, 0 /* dummy */),);
+ TRACE_EXIT 0;
+}
+
+static int mt_fsf(int *arg)
+{
+ int result;
+ TRACE_FUN(ft_t_flow);
+
+ result = zft_skip_volumes(*arg, &zft_pos);
+ zft_just_before_eof = 0;
+ TRACE_EXIT result;
+}
+
+static int mt_bsf(int *arg)
+{
+ int result = 0;
+ TRACE_FUN(ft_t_flow);
+
+ if (*arg != 0) {
+ result = zft_skip_volumes(-*arg + 1, &zft_pos);
+ }
+ TRACE_EXIT result;
+}
+
+static int seek_block(__s64 data_offset,
+ __s64 block_increment,
+ zft_position *pos)
+{
+ int result = 0;
+ __s64 new_block_pos;
+ __s64 vol_block_count;
+ const zft_volinfo *volume;
+ int exceed;
+ TRACE_FUN(ft_t_flow);
+
+ volume = zft_find_volume(pos->seg_pos);
+ if (volume->start_seg == 0 || volume->end_seg == 0) {
+ TRACE_EXIT -EIO;
+ }
+ new_block_pos = (zft_div_blksz(data_offset, volume->blk_sz)
+ + block_increment);
+ vol_block_count = zft_div_blksz(volume->size, volume->blk_sz);
+ if (new_block_pos < 0) {
+ TRACE(ft_t_noise,
+ "new_block_pos " LL_X " < 0", LL(new_block_pos));
+ zft_resid = (int)new_block_pos;
+ new_block_pos = 0;
+ exceed = 1;
+ } else if (new_block_pos > vol_block_count) {
+ TRACE(ft_t_noise,
+ "new_block_pos " LL_X " exceeds size of volume " LL_X,
+ LL(new_block_pos), LL(vol_block_count));
+ zft_resid = (int)(vol_block_count - new_block_pos);
+ new_block_pos = vol_block_count;
+ exceed = 1;
+ } else {
+ exceed = 0;
+ }
+ if (zft_use_compression && volume->use_compression) {
+ TRACE_CATCH(zft_cmpr_lock(1 /* try to load */),);
+ result = (*zft_cmpr_ops->seek)(new_block_pos, pos, volume,
+ zft_deblock_buf);
+ pos->tape_pos = zft_calc_tape_pos(pos->seg_pos);
+ pos->tape_pos += pos->seg_byte_pos;
+ } else {
+ pos->volume_pos = zft_mul_blksz(new_block_pos, volume->blk_sz);
+ pos->tape_pos = zft_calc_tape_pos(volume->start_seg);
+ pos->tape_pos += pos->volume_pos;
+ pos->seg_pos = zft_calc_seg_byte_coord(&pos->seg_byte_pos,
+ pos->tape_pos);
+ }
+ zft_just_before_eof = volume->size == pos->volume_pos;
+ if (zft_just_before_eof) {
+ /* why this? because zft_file_no checks agains start
+ * and end segment of a volume. We do not want to
+ * advance to the next volume with this function.
+ */
+ TRACE(ft_t_noise, "set zft_just_before_eof");
+ zft_position_before_eof(pos, volume);
+ }
+ TRACE(ft_t_noise, "\n"
+ KERN_INFO "new_seg_pos : %d\n"
+ KERN_INFO "new_tape_pos: " LL_X "\n"
+ KERN_INFO "vol_size : " LL_X "\n"
+ KERN_INFO "seg_byte_pos: %d\n"
+ KERN_INFO "blk_sz : %d",
+ pos->seg_pos, LL(pos->tape_pos),
+ LL(volume->size), pos->seg_byte_pos,
+ volume->blk_sz);
+ if (!exceed) {
+ zft_resid = new_block_pos - zft_div_blksz(pos->volume_pos,
+ volume->blk_sz);
+ }
+ if (zft_resid < 0) {
+ zft_resid = -zft_resid;
+ }
+ TRACE_EXIT ((exceed || zft_resid != 0) && result >= 0) ? -EINVAL : result;
+}
+
+static int mt_fsr(int *arg)
+{
+ int result;
+ TRACE_FUN(ft_t_flow);
+
+ result = seek_block(zft_pos.volume_pos, *arg, &zft_pos);
+ TRACE_EXIT result;
+}
+
+static int mt_bsr(int *arg)
+{
+ int result;
+ TRACE_FUN(ft_t_flow);
+
+ result = seek_block(zft_pos.volume_pos, -*arg, &zft_pos);
+ TRACE_EXIT result;
+}
+
+static int mt_weof(int *arg)
+{
+ int result;
+ TRACE_FUN(ft_t_flow);
+
+ TRACE_CATCH(zft_flush_buffers(),);
+ result = zft_weof(*arg, &zft_pos);
+ TRACE_EXIT result;
+}
+
+static int mt_rew(int *dummy)
+{
+ int result;
+ TRACE_FUN(ft_t_flow);
+
+ if(zft_header_read) {
+ (void)zft_def_idle_state();
+ }
+ result = ftape_seek_to_bot();
+ zft_reset_position(&zft_pos);
+ TRACE_EXIT result;
+}
+
+static int mt_offl(int *dummy)
+{
+ int result;
+ TRACE_FUN(ft_t_flow);
+
+ going_offline= 1;
+ result = mt_rew(NULL);
+ TRACE_EXIT result;
+}
+
+static int mt_nop(int *dummy)
+{
+ TRACE_FUN(ft_t_flow);
+ /* should we set tape status?
+ */
+ if (!zft_offline) { /* offline includes no_tape */
+ (void)zft_def_idle_state();
+ }
+ TRACE_EXIT 0;
+}
+
+static int mt_reten(int *dummy)
+{
+ int result;
+ TRACE_FUN(ft_t_flow);
+
+ if(zft_header_read) {
+ (void)zft_def_idle_state();
+ }
+ result = ftape_seek_to_eot();
+ if (result >= 0) {
+ result = ftape_seek_to_bot();
+ }
+ TRACE_EXIT(result);
+}
+
+static int fsfbsfm(int arg, zft_position *pos)
+{
+ const zft_volinfo *vtbl;
+ __s64 block_pos;
+ TRACE_FUN(ft_t_flow);
+
+ /* What to do? This should seek to the next file-mark and
+ * position BEFORE. That is, a next write would just extend
+ * the current file. Well. Let's just seek to the end of the
+ * current file, if count == 1. If count > 1, then do a
+ * "mt_fsf(count - 1)", and then seek to the end of that file.
+ * If count == 0, do nothing
+ */
+ if (arg == 0) {
+ TRACE_EXIT 0;
+ }
+ zft_just_before_eof = 0;
+ TRACE_CATCH(zft_skip_volumes(arg < 0 ? arg : arg-1, pos),
+ if (arg > 0) {
+ zft_resid ++;
+ });
+ vtbl = zft_find_volume(pos->seg_pos);
+ block_pos = zft_div_blksz(vtbl->size, vtbl->blk_sz);
+ (void)seek_block(0, block_pos, pos);
+ if (pos->volume_pos != vtbl->size) {
+ zft_just_before_eof = 0;
+ zft_resid = 1;
+ /* we didn't managed to go there */
+ TRACE_ABORT(-EIO, ft_t_err,
+ "wanted file position " LL_X ", arrived at " LL_X,
+ LL(vtbl->size), LL(pos->volume_pos));
+ }
+ zft_just_before_eof = 1;
+ TRACE_EXIT 0;
+}
+
+static int mt_bsfm(int *arg)
+{
+ int result;
+ TRACE_FUN(ft_t_flow);
+
+ result = fsfbsfm(-*arg, &zft_pos);
+ TRACE_EXIT result;
+}
+
+static int mt_fsfm(int *arg)
+{
+ int result;
+ TRACE_FUN(ft_t_flow);
+
+ result = fsfbsfm(*arg, &zft_pos);
+ TRACE_EXIT result;
+}
+
+static int mt_eom(int *dummy)
+{
+ TRACE_FUN(ft_t_flow);
+
+ zft_skip_to_eom(&zft_pos);
+ TRACE_EXIT 0;
+}
+
+static int mt_erase(int *dummy)
+{
+ int result;
+ TRACE_FUN(ft_t_flow);
+
+ result = zft_erase();
+ TRACE_EXIT result;
+}
+
+static int mt_ras2(int *dummy)
+{
+ int result;
+ TRACE_FUN(ft_t_flow);
+
+ result = -ENOSYS;
+ TRACE_EXIT result;
+}
+
+/* Sets the new blocksize in BYTES
+ *
+ */
+static int mt_setblk(int *new_size)
+{
+ TRACE_FUN(ft_t_flow);
+
+ if((unsigned int)(*new_size) > ZFT_MAX_BLK_SZ) {
+ TRACE_ABORT(-EINVAL, ft_t_info,
+ "desired blk_sz (%d) should be <= %d bytes",
+ *new_size, ZFT_MAX_BLK_SZ);
+ }
+ if ((*new_size & (FT_SECTOR_SIZE-1)) != 0) {
+ TRACE_ABORT(-EINVAL, ft_t_info,
+ "desired blk_sz (%d) must be a multiple of %d bytes",
+ *new_size, FT_SECTOR_SIZE);
+ }
+ if (*new_size == 0) {
+ if (zft_use_compression) {
+ TRACE_ABORT(-EINVAL, ft_t_info,
+ "Variable block size not yet "
+ "supported with compression");
+ }
+ *new_size = 1;
+ }
+ zft_blk_sz = *new_size;
+ TRACE_EXIT 0;
+}
+
+static int mt_setdensity(int *arg)
+{
+ TRACE_FUN(ft_t_flow);
+
+ SET_TRACE_LEVEL(*arg);
+ TRACE(TRACE_LEVEL, "tracing set to %d", TRACE_LEVEL);
+ if ((int)TRACE_LEVEL != *arg) {
+ TRACE_EXIT -EINVAL;
+ }
+ TRACE_EXIT 0;
+}
+
+static int mt_seek(int *new_block_pos)
+{
+ int result= 0;
+ TRACE_FUN(ft_t_any);
+
+ result = seek_block(0, (__s64)*new_block_pos, &zft_pos);
+ TRACE_EXIT result;
+}
+
+/* OK, this is totally different from SCSI, but the worst thing that can
+ * happen is that there is not enough defragmentated memory that can be
+ * allocated. Also, there is a hardwired limit of 16 dma buffers in the
+ * stock ftape module. This shouldn't bring the system down.
+ *
+ * NOTE: the argument specifies the total number of dma buffers to use.
+ * The driver needs at least 3 buffers to function at all.
+ *
+ */
+static int mt_setdrvbuffer(int *cnt)
+{
+ TRACE_FUN(ft_t_flow);
+
+ if (*cnt < 3) {
+ TRACE_EXIT -EINVAL;
+ }
+ TRACE_CATCH(ftape_set_nr_buffers(*cnt),);
+ TRACE_EXIT 0;
+}
+/* return the block position from start of volume
+ */
+static int mt_tell(int *arg)
+{
+ TRACE_FUN(ft_t_flow);
+
+ *arg = zft_div_blksz(zft_pos.volume_pos,
+ zft_find_volume(zft_pos.seg_pos)->blk_sz);
+ TRACE_EXIT 0;
+}
+
+static int mt_compression(int *arg)
+{
+ TRACE_FUN(ft_t_flow);
+
+ /* Ok. We could also check whether compression is available at
+ * all by trying to load the compression module. We could
+ * also check for a block size of 1 byte which is illegal
+ * with compression. Instead of doing it here we rely on
+ * zftape_write() to do the proper checks.
+ */
+ if ((unsigned int)*arg > 1) {
+ TRACE_EXIT -EINVAL;
+ }
+ if (*arg != 0 && zft_blk_sz == 1) { /* variable block size */
+ TRACE_ABORT(-EINVAL, ft_t_info,
+ "Compression not yet supported "
+ "with variable block size");
+ }
+ zft_mt_compression = *arg;
+ if ((zft_unit & ZFT_ZIP_MODE) == 0) {
+ zft_use_compression = zft_mt_compression;
+ }
+ TRACE_EXIT 0;
+}
+
+/* check whether write access is allowed. Write access is denied when
+ * + zft_write_protected == 1 -- this accounts for either hard write
+ * protection of the cartridge or for
+ * O_RDONLY access mode of the tape device
+ * + zft_offline == 1 -- this meany that there is either no tape
+ * or that the MTOFFLINE ioctl has been
+ * previously issued (`soft eject')
+ * + ft_formatted == 0 -- this means that the cartridge is not
+ * formatted
+ * Then we distinuguish two cases. When zft_qic_mode is TRUE, then we try
+ * to emulate a `traditional' (aka SCSI like) UN*X tape device. Therefore we
+ * deny writes when
+ * + zft_qic_mode ==1 &&
+ * (!zft_tape_at_lbot() && -- tape no at logical BOT
+ * !(zft_tape_at_eom() || -- tape not at logical EOM (or EOD)
+ * (zft_tape_at_eom() &&
+ * zft_old_ftape()))) -- we can't add new volume to tapes
+ * written by old ftape because ftape
+ * don't use the volume table
+ *
+ * when the drive is in true raw mode (aka /dev/rawft0) then we don't
+ * care about LBOT and EOM conditions. This device is intended for a
+ * user level program that wants to truly implement the QIC-80 compliance
+ * at the logical data layout level of the cartridge, i.e. implement all
+ * that volume table and volume directory stuff etc.<
+ */
+int zft_check_write_access(zft_position *pos)
+{
+ TRACE_FUN(ft_t_flow);
+
+ if (zft_offline) { /* offline includes no_tape */
+ TRACE_ABORT(-ENXIO,
+ ft_t_info, "tape is offline or no cartridge");
+ }
+ if (!ft_formatted) {
+ TRACE_ABORT(-EACCES, ft_t_info, "tape is not formatted");
+ }
+ if (zft_write_protected) {
+ TRACE_ABORT(-EACCES, ft_t_info, "cartridge write protected");
+ }
+ if (zft_qic_mode) {
+ /* check BOT condition */
+ if (!zft_tape_at_lbot(pos)) {
+ /* protect cartridges written by old ftape if
+ * not at BOT because they use the vtbl
+ * segment for storing data
+ */
+ if (zft_old_ftape) {
+ TRACE_ABORT(-EACCES, ft_t_warn,
+ "Cannot write to cartridges written by old ftape when not at BOT");
+ }
+ /* not at BOT, but allow writes at EOD, of course
+ */
+ if (!zft_tape_at_eod(pos)) {
+ TRACE_ABORT(-EACCES, ft_t_info,
+ "tape not at BOT and not at EOD");
+ }
+ }
+ /* fine. Now the tape is either at BOT or at EOD. */
+ }
+ /* or in raw mode in which case we don't care about BOT and EOD */
+ TRACE_EXIT 0;
+}
+
+/* decide when we should lock the module in memory, even when calling
+ * the release routine. This really is necessary for use with
+ * kerneld.
+ *
+ * NOTE: we MUST NOT use zft_write_protected, because this includes
+ * the file access mode as well which has no meaning with our
+ * asynchronous update scheme.
+ *
+ * Ugly, ugly. We need to look the module if we changed the block size.
+ * How sad! Need persistent modules storage!
+ *
+ * NOTE: I don't want to lock the module if the number of dma buffers
+ * has been changed. It's enough! Stop the story! Give me persisitent
+ * module storage! Do it!
+ */
+int zft_dirty(void)
+{
+ if (!ft_formatted || zft_offline) {
+ /* cannot be dirty if not formatted or offline */
+ return 0;
+ }
+ if (zft_blk_sz != CONFIG_ZFT_DFLT_BLK_SZ) {
+ /* blocksize changed, must lock */
+ return 1;
+ }
+ if (zft_mt_compression != 0) {
+ /* compression mode with /dev/qft, must lock */
+ return 1;
+ }
+ if (!zft_header_read) {
+ /* tape is logical at BOT, no lock */
+ return 0;
+ }
+ if (!zft_tape_at_lbot(&zft_pos)) {
+ /* somewhere inside a volume, lock tape */
+ return 1;
+ }
+ if (zft_volume_table_changed || zft_header_changed) {
+ /* header segments dirty if tape not write protected */
+ return !(ft_write_protected || zft_old_ftape);
+ }
+ return 0;
+}
+
+/* OPEN routine called by kernel-interface code
+ *
+ * NOTE: this is also called by mt_reset() with dev_minor == -1
+ * to fake a reopen after a reset.
+ */
+int _zft_open(unsigned int dev_minor, unsigned int access_mode)
+{
+ static unsigned int tape_unit;
+ static unsigned int file_access_mode;
+ int result;
+ TRACE_FUN(ft_t_flow);
+
+ if ((int)dev_minor == -1) {
+ /* fake reopen */
+ zft_unit = tape_unit;
+ access_mode = file_access_mode;
+ zft_init_driver(); /* reset all static data to defaults */
+ } else {
+ tape_unit = dev_minor;
+ file_access_mode = access_mode;
+ if ((result = ftape_enable(FTAPE_SEL(dev_minor))) < 0) {
+ TRACE_ABORT(-ENXIO, ft_t_err,
+ "ftape_enable failed: %d", result);
+ }
+ if (ft_new_tape || ft_no_tape || !ft_formatted ||
+ (FTAPE_SEL(zft_unit) != FTAPE_SEL(dev_minor)) ||
+ (zft_unit & ZFT_RAW_MODE) != (dev_minor & ZFT_RAW_MODE)) {
+ /* reset all static data to defaults,
+ */
+ zft_init_driver();
+ }
+ zft_unit = dev_minor;
+ }
+ zft_set_flags(zft_unit); /* decode the minor bits */
+ if (zft_blk_sz == 1 && zft_use_compression) {
+ ftape_disable(); /* resets ft_no_tape */
+ TRACE_ABORT(-ENODEV, ft_t_warn, "Variable block size not yet "
+ "supported with compression");
+ }
+ /* no need for most of the buffers when no tape or not
+ * formatted. for the read/write operations, it is the
+ * regardless whether there is no tape, a not-formatted tape
+ * or the whether the driver is soft offline.
+ * Nevertheless we allow some ioctls with non-formatted tapes,
+ * like rewind and reset.
+ */
+ if (ft_no_tape || !ft_formatted) {
+ zft_uninit_mem();
+ }
+ if (ft_no_tape) {
+ zft_offline = 1; /* so we need not test two variables */
+ }
+ if ((access_mode == O_WRONLY || access_mode == O_RDWR) &&
+ (ft_write_protected || ft_no_tape)) {
+ ftape_disable(); /* resets ft_no_tape */
+ TRACE_ABORT(ft_no_tape ? -ENXIO : -EROFS,
+ ft_t_warn, "wrong access mode %s cartridge",
+ ft_no_tape ? "without a" : "with write protected");
+ }
+ zft_write_protected = (access_mode == O_RDONLY ||
+ ft_write_protected != 0);
+ if (zft_write_protected) {
+ TRACE(ft_t_noise,
+ "read only access mode: %d, "
+ "drive write protected: %d",
+ access_mode == O_RDONLY,
+ ft_write_protected != 0);
+ }
+ if (!zft_offline) {
+ TRACE_CATCH(zft_vmalloc_once(&zft_deblock_buf,FT_SEGMENT_SIZE),
+ ftape_disable());
+ }
+ /* zft_seg_pos should be greater than the vtbl segpos but not
+ * if in compatability mode and only after we read in the
+ * header segments
+ *
+ * might also be a problem if the user makes a backup with a
+ * *qft* device and rewinds it with a raw device.
+ */
+ if (zft_qic_mode &&
+ !zft_old_ftape &&
+ zft_pos.seg_pos >= 0 &&
+ zft_header_read &&
+ zft_pos.seg_pos <= ft_first_data_segment) {
+ TRACE(ft_t_noise, "you probably mixed up the zftape devices!");
+ zft_reset_position(&zft_pos);
+ }
+ TRACE_EXIT 0;
+}
+
+/* RELEASE routine called by kernel-interface code
+ */
+int _zft_close(void)
+{
+ int result = 0;
+ TRACE_FUN(ft_t_flow);
+
+ if (zft_offline) {
+ /* call the hardware release routine. Puts the drive offline */
+ ftape_disable();
+ TRACE_EXIT 0;
+ }
+ if (!(ft_write_protected || zft_old_ftape)) {
+ result = zft_flush_buffers();
+ TRACE(ft_t_noise, "writing file mark at current position");
+ if (zft_qic_mode && zft_close_volume(&zft_pos) == 0) {
+ zft_move_past_eof(&zft_pos);
+ }
+ if ((zft_tape_at_lbot(&zft_pos) ||
+ !(zft_unit & FTAPE_NO_REWIND))) {
+ if (result >= 0) {
+ result = zft_update_header_segments();
+ } else {
+ TRACE(ft_t_err,
+ "Error: unable to update header segments");
+ }
+ }
+ }
+ ftape_abort_operation();
+ if (!(zft_unit & FTAPE_NO_REWIND)) {
+ TRACE(ft_t_noise, "rewinding tape");
+ if (ftape_seek_to_bot() < 0 && result >= 0) {
+ result = -EIO; /* keep old value */
+ }
+ zft_reset_position(&zft_pos);
+ }
+ zft_zap_read_buffers();
+ /* now free up memory as much as possible. We don't destroy
+ * the deblock buffer if it containes a valid segment.
+ */
+ if (zft_deblock_segment == -1) {
+ zft_vfree(&zft_deblock_buf, FT_SEGMENT_SIZE);
+ }
+ /* high level driver status, forces creation of a new volume
+ * when calling ftape_write again and not zft_just_before_eof
+ */
+ zft_io_state = zft_idle;
+ if (going_offline) {
+ zft_init_driver();
+ zft_uninit_mem();
+ going_offline = 0;
+ zft_offline = 1;
+ } else if (zft_dirty()) {
+ TRACE(ft_t_noise, "Keeping module locked in memory because:\n"
+ KERN_INFO "header segments need updating: %s\n"
+ KERN_INFO "tape not at BOT : %s",
+ (zft_volume_table_changed || zft_header_changed)
+ ? "yes" : "no",
+ zft_tape_at_lbot(&zft_pos) ? "no" : "yes");
+ } else if (zft_cmpr_lock(0 /* don't load */) == 0) {
+ (*zft_cmpr_ops->reset)(); /* unlock it again */
+ }
+ zft_memory_stats();
+ /* call the hardware release routine. Puts the drive offline */
+ ftape_disable();
+ TRACE_EXIT result;
+}
+
+/*
+ * the wrapper function around the wrapper MTIOCTOP ioctl
+ */
+static int mtioctop(struct mtop *mtop, int arg_size)
+{
+ int result = 0;
+ fun_entry *mt_fun_entry;
+ TRACE_FUN(ft_t_flow);
+
+ if (arg_size != sizeof(struct mtop) || mtop->mt_op >= NR_MT_CMDS) {
+ TRACE_EXIT -EINVAL;
+ }
+ TRACE(ft_t_noise, "calling MTIOCTOP command: %s",
+ mt_funs[mtop->mt_op].name);
+ mt_fun_entry= &mt_funs[mtop->mt_op];
+ zft_resid = mtop->mt_count;
+ if (!mt_fun_entry->offline && zft_offline) {
+ if (ft_no_tape) {
+ TRACE_ABORT(-ENXIO, ft_t_info, "no tape present");
+ } else {
+ TRACE_ABORT(-ENXIO, ft_t_info, "drive is offline");
+ }
+ }
+ if (!mt_fun_entry->not_formatted && !ft_formatted) {
+ TRACE_ABORT(-EACCES, ft_t_info, "tape is not formatted");
+ }
+ if (!mt_fun_entry->write_protected) {
+ TRACE_CATCH(zft_check_write_access(&zft_pos),);
+ }
+ if (mt_fun_entry->need_idle_state && !(zft_offline || !ft_formatted)) {
+ TRACE_CATCH(zft_def_idle_state(),);
+ }
+ if (!zft_qic_mode && !mt_fun_entry->raw_mode) {
+ TRACE_ABORT(-EACCES, ft_t_info,
+"Drive needs to be in QIC-80 compatibility mode for this command");
+ }
+ result = (mt_fun_entry->function)(&mtop->mt_count);
+ if (zft_tape_at_lbot(&zft_pos)) {
+ TRACE_CATCH(zft_update_header_segments(),);
+ }
+ if (result >= 0) {
+ zft_resid = 0;
+ }
+ TRACE_EXIT result;
+}
+
+/*
+ * standard MTIOCGET ioctl
+ */
+static int mtiocget(struct mtget *mtget, int arg_size)
+{
+ const zft_volinfo *volume;
+ __s64 max_tape_pos;
+ TRACE_FUN(ft_t_flow);
+
+ if (arg_size != sizeof(struct mtget)) {
+ TRACE_ABORT(-EINVAL, ft_t_info, "bad argument size: %d",
+ arg_size);
+ }
+ mtget->mt_type = ft_drive_type.vendor_id + 0x800000;
+ mtget->mt_dsreg = ft_last_status.space;
+ mtget->mt_erreg = ft_last_error.space; /* error register */
+ mtget->mt_resid = zft_resid; /* residuum of writes, reads and
+ * MTIOCTOP commands
+ */
+ if (!zft_offline) { /* neither no_tape nor soft offline */
+ mtget->mt_gstat = GMT_ONLINE(~0UL);
+ /* should rather return the status of the cartridge
+ * than the access mode of the file, therefor use
+ * ft_write_protected, not zft_write_protected
+ */
+ if (ft_write_protected) {
+ mtget->mt_gstat |= GMT_WR_PROT(~0UL);
+ }
+ if(zft_header_read) { /* this catches non-formatted */
+ volume = zft_find_volume(zft_pos.seg_pos);
+ mtget->mt_fileno = volume->count;
+ max_tape_pos = zft_capacity - zft_blk_sz;
+ if (zft_use_compression) {
+ max_tape_pos -= ZFT_CMPR_OVERHEAD;
+ }
+ if (zft_tape_at_eod(&zft_pos)) {
+ mtget->mt_gstat |= GMT_EOD(~0UL);
+ }
+ if (zft_pos.tape_pos > max_tape_pos) {
+ mtget->mt_gstat |= GMT_EOT(~0UL);
+ }
+ mtget->mt_blkno = zft_div_blksz(zft_pos.volume_pos,
+ volume->blk_sz);
+ if (zft_just_before_eof) {
+ mtget->mt_gstat |= GMT_EOF(~0UL);
+ }
+ if (zft_tape_at_lbot(&zft_pos)) {
+ mtget->mt_gstat |= GMT_BOT(~0UL);
+ }
+ } else {
+ mtget->mt_fileno = mtget->mt_blkno = -1;
+ if (mtget->mt_dsreg & QIC_STATUS_AT_BOT) {
+ mtget->mt_gstat |= GMT_BOT(~0UL);
+ }
+ }
+ } else {
+ if (ft_no_tape) {
+ mtget->mt_gstat = GMT_DR_OPEN(~0UL);
+ } else {
+ mtget->mt_gstat = 0UL;
+ }
+ mtget->mt_fileno = mtget->mt_blkno = -1;
+ }
+ TRACE_EXIT 0;
+}
+
+#ifdef MTIOCRDFTSEG
+/*
+ * Read a floppy tape segment. This is useful for manipulating the
+ * volume table, and read the old header segment before re-formatting
+ * the cartridge.
+ */
+static int mtiocrdftseg(struct mtftseg * mtftseg, int arg_size)
+{
+ TRACE_FUN(ft_t_flow);
+
+ TRACE(ft_t_noise, "Mag tape ioctl command: MTIOCRDFTSEG");
+ if (zft_qic_mode) {
+ TRACE_ABORT(-EACCES, ft_t_info,
+ "driver needs to be in raw mode for this ioctl");
+ }
+ if (arg_size != sizeof(struct mtftseg)) {
+ TRACE_ABORT(-EINVAL, ft_t_info, "bad argument size: %d",
+ arg_size);
+ }
+ if (zft_offline) {
+ TRACE_EXIT -ENXIO;
+ }
+ if (mtftseg->mt_mode != FT_RD_SINGLE &&
+ mtftseg->mt_mode != FT_RD_AHEAD) {
+ TRACE_ABORT(-EINVAL, ft_t_info, "invalid read mode");
+ }
+ if (!ft_formatted) {
+ TRACE_EXIT -EACCES; /* -ENXIO ? */
+
+ }
+ if (!zft_header_read) {
+ TRACE_CATCH(zft_def_idle_state(),);
+ }
+ if (mtftseg->mt_segno > ft_last_data_segment) {
+ TRACE_ABORT(-EINVAL, ft_t_info, "segment number is too large");
+ }
+ mtftseg->mt_result = ftape_read_segment(mtftseg->mt_segno,
+ zft_deblock_buf,
+ mtftseg->mt_mode);
+ if (mtftseg->mt_result < 0) {
+ /* a negativ result is not an ioctl error. if
+ * the user wants to read damaged tapes,
+ * it's up to her/him
+ */
+ TRACE_EXIT 0;
+ }
+#if LINUX_VERSION_CODE > KERNEL_VER(2,1,3)
+ if (copy_to_user(mtftseg->mt_data,
+ zft_deblock_buf,
+ mtftseg->mt_result) != 0) {
+ TRACE_EXIT -EFAULT;
+ }
+#else
+ TRACE_CATCH(verify_area(VERIFY_WRITE, mtftseg->mt_data,
+ mtftseg->mt_result),);
+ memcpy_tofs(mtftseg->mt_data, zft_deblock_buf,
+ mtftseg->mt_result);
+#endif
+ TRACE_EXIT 0;
+}
+#endif
+
+#ifdef MTIOCWRFTSEG
+/*
+ * write a floppy tape segment. This version features writing of
+ * deleted address marks, and gracefully ignores the (software)
+ * ft_formatted flag to support writing of header segments after
+ * formatting.
+ */
+static int mtiocwrftseg(struct mtftseg * mtftseg, int arg_size)
+{
+ int result;
+ TRACE_FUN(ft_t_flow);
+
+ TRACE(ft_t_noise, "Mag tape ioctl command: MTIOCWRFTSEG");
+ if (zft_write_protected || zft_qic_mode) {
+ TRACE_EXIT -EACCES;
+ }
+ if (arg_size != sizeof(struct mtftseg)) {
+ TRACE_ABORT(-EINVAL, ft_t_info, "bad argument size: %d",
+ arg_size);
+ }
+ if (zft_offline) {
+ TRACE_EXIT -ENXIO;
+ }
+ if (mtftseg->mt_mode != FT_WR_ASYNC &&
+ mtftseg->mt_mode != FT_WR_MULTI &&
+ mtftseg->mt_mode != FT_WR_SINGLE &&
+ mtftseg->mt_mode != FT_WR_DELETE) {
+ TRACE_ABORT(-EINVAL, ft_t_info, "invalid write mode");
+ }
+ /*
+ * We don't check for ft_formatted, because this gives
+ * only the software status of the driver.
+ *
+ * We assume that the user knows what it is
+ * doing. And rely on the low level stuff to fail
+ * when the tape isn't formatted. We only make sure
+ * that The header segment buffer is allocated,
+ * because it holds the bad sector map.
+ */
+ if (zft_hseg_buf == NULL) {
+ TRACE_EXIT -ENXIO;
+ }
+ if (mtftseg->mt_mode != FT_WR_DELETE) {
+#if LINUX_VERSION_CODE > KERNEL_VER(2,1,3)
+ if (copy_from_user(zft_deblock_buf,
+ mtftseg->mt_data,
+ FT_SEGMENT_SIZE) != 0) {
+ TRACE_EXIT -EFAULT;
+ }
+#else
+ TRACE_CATCH(verify_area(VERIFY_READ,
+ mtftseg->mt_data,
+ FT_SEGMENT_SIZE),);
+ memcpy_fromfs(zft_deblock_buf, mtftseg->mt_data,
+ FT_SEGMENT_SIZE);
+#endif
+ }
+ mtftseg->mt_result = ftape_write_segment(mtftseg->mt_segno,
+ zft_deblock_buf,
+ mtftseg->mt_mode);
+ if (mtftseg->mt_result >= 0 && mtftseg->mt_mode == FT_WR_SINGLE) {
+ /*
+ * a negativ result is not an ioctl error. if
+ * the user wants to write damaged tapes,
+ * it's up to her/him
+ */
+ if ((result = ftape_loop_until_writes_done()) < 0) {
+ mtftseg->mt_result = result;
+ }
+ }
+ TRACE_EXIT 0;
+}
+#endif
+
+#ifdef MTIOCVOLINFO
+/*
+ * get information about volume positioned at.
+ */
+static int mtiocvolinfo(struct mtvolinfo *volinfo, int arg_size)
+{
+ const zft_volinfo *volume;
+ TRACE_FUN(ft_t_flow);
+
+ TRACE(ft_t_noise, "Mag tape ioctl command: MTIOCVOLINFO");
+ if (arg_size != sizeof(struct mtvolinfo)) {
+ TRACE_ABORT(-EINVAL,
+ ft_t_info, "bad argument size: %d", arg_size);
+ }
+ if (zft_offline) {
+ TRACE_EXIT -ENXIO;
+ }
+ if (!ft_formatted) {
+ TRACE_EXIT -EACCES;
+ }
+ TRACE_CATCH(zft_def_idle_state(),);
+ volume = zft_find_volume(zft_pos.seg_pos);
+ volinfo->mt_volno = volume->count;
+ volinfo->mt_blksz = volume->blk_sz == 1 ? 0 : volume->blk_sz;
+ volinfo->mt_size = volume->size >> 10;
+ volinfo->mt_rawsize = ((zft_calc_tape_pos(volume->end_seg + 1) >> 10) -
+ (zft_calc_tape_pos(volume->start_seg) >> 10));
+ volinfo->mt_cmpr = volume->use_compression;
+ TRACE_EXIT 0;
+}
+#endif
+
+#ifdef ZFT_OBSOLETE
+static int mtioc_zftape_getblksz(struct mtblksz *blksz, int arg_size)
+{
+ TRACE_FUN(ft_t_flow);
+
+ TRACE(ft_t_noise, "\n"
+ KERN_INFO "Mag tape ioctl command: MTIOC_ZTAPE_GETBLKSZ\n"
+ KERN_INFO "This ioctl is here merely for compatibility.\n"
+ KERN_INFO "Please use MTIOCVOLINFO instead");
+ if (arg_size != sizeof(struct mtblksz)) {
+ TRACE_ABORT(-EINVAL,
+ ft_t_info, "bad argument size: %d", arg_size);
+ }
+ if (zft_offline) {
+ TRACE_EXIT -ENXIO;
+ }
+ if (!ft_formatted) {
+ TRACE_EXIT -EACCES;
+ }
+ TRACE_CATCH(zft_def_idle_state(),);
+ blksz->mt_blksz = zft_find_volume(zft_pos.seg_pos)->blk_sz;
+ TRACE_EXIT 0;
+}
+#endif
+
+#ifdef MTIOCGETSIZE
+/*
+ * get the capacity of the tape cartridge.
+ */
+static int mtiocgetsize(struct mttapesize *size, int arg_size)
+{
+ TRACE_FUN(ft_t_flow);
+
+ TRACE(ft_t_noise, "Mag tape ioctl command: MTIOC_ZFTAPE_GETSIZE");
+ if (arg_size != sizeof(struct mttapesize)) {
+ TRACE_ABORT(-EINVAL,
+ ft_t_info, "bad argument size: %d", arg_size);
+ }
+ if (zft_offline) {
+ TRACE_EXIT -ENXIO;
+ }
+ if (!ft_formatted) {
+ TRACE_EXIT -EACCES;
+ }
+ TRACE_CATCH(zft_def_idle_state(),);
+ size->mt_capacity = (unsigned int)(zft_capacity>>10);
+ size->mt_used = (unsigned int)(zft_get_eom_pos()>>10);
+ TRACE_EXIT 0;
+}
+#endif
+
+static int mtiocpos(struct mtpos *mtpos, int arg_size)
+{
+ int result;
+ TRACE_FUN(ft_t_flow);
+
+ TRACE(ft_t_noise, "Mag tape ioctl command: MTIOCPOS");
+ if (arg_size != sizeof(struct mtpos)) {
+ TRACE_ABORT(-EINVAL,
+ ft_t_info, "bad argument size: %d", arg_size);
+ }
+ result = mt_tell((int *)&mtpos->mt_blkno);
+ TRACE_EXIT result;
+}
+
+#ifdef MTIOCFTFORMAT
+/*
+ * formatting of floppy tape cartridges. This is intended to be used
+ * together with the MTIOCFTCMD ioctl and the new mmap feature
+ */
+
+/*
+ * This function uses ftape_decode_header_segment() to inform the low
+ * level ftape module about the new parameters.
+ *
+ * It erases the hseg_buf. The calling process must specify all
+ * parameters to assure proper operation.
+ *
+ * return values: -EINVAL - wrong argument size
+ * -EINVAL - if ftape_decode_header_segment() failed.
+ */
+static int set_format_parms(struct ftfmtparms *p, __u8 *hseg_buf)
+{
+ ft_trace_t old_level = TRACE_LEVEL;
+ TRACE_FUN(ft_t_flow);
+
+ TRACE(ft_t_noise, "MTIOCFTFORMAT operation FTFMT_SETPARMS");
+ memset(hseg_buf, 0, FT_SEGMENT_SIZE);
+ PUT4(hseg_buf, FT_SIGNATURE, FT_HSEG_MAGIC);
+
+ /* fill in user specified parameters
+ */
+ hseg_buf[FT_FMT_CODE] = (__u8)p->ft_fmtcode;
+ PUT2(hseg_buf, FT_SPT, p->ft_spt);
+ hseg_buf[FT_TPC] = (__u8)p->ft_tpc;
+ hseg_buf[FT_FHM] = (__u8)p->ft_fhm;
+ hseg_buf[FT_FTM] = (__u8)p->ft_ftm;
+
+ /* fill in sane defaults to make ftape happy.
+ */
+ hseg_buf[FT_FSM] = (__u8)128; /* 128 is hard wired all over ftape */
+ if (p->ft_fmtcode == fmt_big) {
+ PUT4(hseg_buf, FT_6_HSEG_1, 0);
+ PUT4(hseg_buf, FT_6_HSEG_2, 1);
+ PUT4(hseg_buf, FT_6_FRST_SEG, 2);
+ PUT4(hseg_buf, FT_6_LAST_SEG, p->ft_spt * p->ft_tpc - 1);
+ } else {
+ PUT2(hseg_buf, FT_HSEG_1, 0);
+ PUT2(hseg_buf, FT_HSEG_2, 1);
+ PUT2(hseg_buf, FT_FRST_SEG, 2);
+ PUT2(hseg_buf, FT_LAST_SEG, p->ft_spt * p->ft_tpc - 1);
+ }
+
+ /* Synchronize with the low level module. This is particularly
+ * needed for unformatted cartridges as the QIC std was previously
+ * unknown BUT is needed to set data rate and to calculate timeouts.
+ */
+ TRACE_CATCH(ftape_calibrate_data_rate(p->ft_qicstd&QIC_TAPE_STD_MASK),
+ _res = -EINVAL);
+
+ /* The following will also recalcualte the timeouts for the tape
+ * length and QIC std we want to format to.
+ * abort with -EINVAL rather than -EIO
+ */
+ SET_TRACE_LEVEL(ft_t_warn);
+ TRACE_CATCH(ftape_decode_header_segment(hseg_buf),
+ SET_TRACE_LEVEL(old_level); _res = -EINVAL);
+ SET_TRACE_LEVEL(old_level);
+ TRACE_EXIT 0;
+}
+
+/*
+ * Return the internal SOFTWARE status of the kernel driver. This does
+ * NOT query the tape drive about its status.
+ */
+static int get_format_parms(struct ftfmtparms *p, __u8 *hseg_buffer)
+{
+ TRACE_FUN(ft_t_flow);
+
+ TRACE(ft_t_noise, "MTIOCFTFORMAT operation FTFMT_GETPARMS");
+ p->ft_qicstd = ft_qic_std;
+ p->ft_fmtcode = ft_format_code;
+ p->ft_fhm = hseg_buffer[FT_FHM];
+ p->ft_ftm = hseg_buffer[FT_FTM];
+ p->ft_spt = ft_segments_per_track;
+ p->ft_tpc = ft_tracks_per_tape;
+ TRACE_EXIT 0;
+}
+
+static int mtiocftformat(struct mtftformat *mtftformat, int arg_size)
+{
+ int result;
+ union fmt_arg *arg = &mtftformat->fmt_arg;
+ TRACE_FUN(ft_t_flow);
+
+ TRACE(ft_t_noise, "Mag tape ioctl command: MTIOCFTFORMAT");
+ if (zft_offline) {
+ if (ft_no_tape) {
+ TRACE_ABORT(-ENXIO, ft_t_info, "no tape present");
+ } else {
+ TRACE_ABORT(-ENXIO, ft_t_info, "drive is offline");
+ }
+ }
+ if (zft_qic_mode) {
+ TRACE_ABORT(-EACCES, ft_t_info,
+ "driver needs to be in raw mode for this ioctl");
+ }
+ if (zft_hseg_buf == NULL) {
+ TRACE_CATCH(zft_vcalloc_once(&zft_hseg_buf, FT_SEGMENT_SIZE),);
+ }
+ zft_header_read = 0;
+ switch(mtftformat->fmt_op) {
+ case FTFMT_SET_PARMS:
+ TRACE_CATCH(set_format_parms(&arg->fmt_parms, zft_hseg_buf),);
+ TRACE_EXIT 0;
+ case FTFMT_GET_PARMS:
+ TRACE_CATCH(get_format_parms(&arg->fmt_parms, zft_hseg_buf),);
+ TRACE_EXIT 0;
+ case FTFMT_FORMAT_TRACK:
+ if ((ft_formatted && zft_check_write_access(&zft_pos) < 0) ||
+ (!ft_formatted && zft_write_protected)) {
+ TRACE_ABORT(-EACCES, ft_t_info, "Write access denied");
+ }
+ TRACE_CATCH(ftape_format_track(arg->fmt_track.ft_track,
+ arg->fmt_track.ft_gap3),);
+ TRACE_EXIT 0;
+ case FTFMT_STATUS:
+ TRACE_CATCH(ftape_format_status(&arg->fmt_status.ft_segment),);
+ TRACE_EXIT 0;
+ case FTFMT_VERIFY:
+ TRACE_CATCH(ftape_verify_segment(arg->fmt_verify.ft_segment,
+ (SectorMap *)&arg->fmt_verify.ft_bsm),);
+ TRACE_EXIT 0;
+ default:
+ TRACE_ABORT(-EINVAL, ft_t_err, "Invalid format operation");
+ }
+ TRACE_EXIT result;
+}
+#endif
+
+#ifdef MTIOCFTCMD
+/*
+ * send a QIC-117 command to the drive, with optional timeouts,
+ * parameter and result bits. This is intended to be used together
+ * with the formatting ioctl.
+ */
+static int mtiocftcmd(struct mtftcmd *ftcmd, int arg_size)
+{
+ int i;
+ TRACE_FUN(ft_t_flow);
+
+ TRACE(ft_t_noise, "Mag tape ioctl command: MTIOCFTCMD");
+ if (!suser()) {
+ TRACE_ABORT(-EPERM, ft_t_info,
+ "only the superuser may send raw qic-117 commands");
+ }
+ if (zft_qic_mode) {
+ TRACE_ABORT(-EACCES, ft_t_info,
+ "driver needs to be in raw mode for this ioctl");
+ }
+ if (arg_size != sizeof(struct mtftcmd)) {
+ TRACE_ABORT(-EINVAL,
+ ft_t_info, "bad argument size: %d", arg_size);
+ }
+ if (ftcmd->ft_wait_before) {
+ TRACE_CATCH(ftape_ready_wait(ftcmd->ft_wait_before,
+ &ftcmd->ft_status),);
+ }
+ if (ftcmd->ft_status & QIC_STATUS_ERROR)
+ goto ftmtcmd_error;
+ if (ftcmd->ft_result_bits != 0) {
+ TRACE_CATCH(ftape_report_operation(&ftcmd->ft_result,
+ ftcmd->ft_cmd,
+ ftcmd->ft_result_bits),);
+ } else {
+ TRACE_CATCH(ftape_command(ftcmd->ft_cmd),);
+ if (ftcmd->ft_status & QIC_STATUS_ERROR)
+ goto ftmtcmd_error;
+ for (i = 0; i < ftcmd->ft_parm_cnt; i++) {
+ TRACE_CATCH(ftape_parameter(ftcmd->ft_parms[i]&0x0f),);
+ if (ftcmd->ft_status & QIC_STATUS_ERROR)
+ goto ftmtcmd_error;
+ }
+ }
+ if (ftcmd->ft_wait_after != 0) {
+ TRACE_CATCH(ftape_ready_wait(ftcmd->ft_wait_after,
+ &ftcmd->ft_status),);
+ }
+ftmtcmd_error:
+ if (ftcmd->ft_status & QIC_STATUS_ERROR) {
+ TRACE(ft_t_noise, "error status set");
+ TRACE_CATCH(ftape_report_error(&ftcmd->ft_error,
+ &ftcmd->ft_cmd, 1),);
+ }
+ TRACE_EXIT 0; /* this is not an i/o error */
+}
+#endif
+
+/* IOCTL routine called by kernel-interface code
+ */
+int _zft_ioctl(unsigned int command, void * arg)
+{
+ int result;
+ union { struct mtop mtop;
+ struct mtget mtget;
+ struct mtpos mtpos;
+#ifdef MTIOCRDFTSEG
+ struct mtftseg mtftseg;
+#endif
+#ifdef MTIOCVOLINFO
+ struct mtvolinfo mtvolinfo;
+#endif
+#ifdef MTIOCGETSIZE
+ struct mttapesize mttapesize;
+#endif
+#ifdef MTIOCFTFORMAT
+ struct mtftformat mtftformat;
+#endif
+#ifdef ZFT_OBSOLETE
+ struct mtblksz mtblksz;
+#endif
+#ifdef MTIOCFTCMD
+ struct mtftcmd mtftcmd;
+#endif
+ } krnl_arg;
+ int arg_size = _IOC_SIZE(command);
+ int dir = _IOC_DIR(command);
+ TRACE_FUN(ft_t_flow);
+
+ /* This check will only catch arguments that are too large !
+ */
+ if (dir & (_IOC_READ | _IOC_WRITE) && arg_size > sizeof(krnl_arg)) {
+ TRACE_ABORT(-EINVAL,
+ ft_t_info, "bad argument size: %d", arg_size);
+ }
+ if (dir & _IOC_WRITE) {
+#if LINUX_VERSION_CODE > KERNEL_VER(2,1,3)
+ if (copy_from_user(&krnl_arg, arg, arg_size) != 0) {
+ TRACE_EXIT -EFAULT;
+ }
+#else
+ TRACE_CATCH(verify_area(VERIFY_READ, arg, arg_size),);
+ memcpy_fromfs(&krnl_arg, arg, arg_size);
+#endif
+ }
+ TRACE(ft_t_flow, "called with ioctl command: 0x%08x", command);
+ switch (command) {
+ case MTIOCTOP:
+ result = mtioctop(&krnl_arg.mtop, arg_size);
+ break;
+ case MTIOCGET:
+ result = mtiocget(&krnl_arg.mtget, arg_size);
+ break;
+ case MTIOCPOS:
+ result = mtiocpos(&krnl_arg.mtpos, arg_size);
+ break;
+#ifdef MTIOCVOLINFO
+ case MTIOCVOLINFO:
+ result = mtiocvolinfo(&krnl_arg.mtvolinfo, arg_size);
+ break;
+#endif
+#ifdef ZFT_OBSOLETE
+ case MTIOC_ZFTAPE_GETBLKSZ:
+ result = mtioc_zftape_getblksz(&krnl_arg.mtblksz, arg_size);
+ break;
+#endif
+#ifdef MTIOCRDFTSEG
+ case MTIOCRDFTSEG: /* read a segment via ioctl */
+ result = mtiocrdftseg(&krnl_arg.mtftseg, arg_size);
+ break;
+#endif
+#ifdef MTIOCWRFTSEG
+ case MTIOCWRFTSEG: /* write a segment via ioctl */
+ result = mtiocwrftseg(&krnl_arg.mtftseg, arg_size);
+ break;
+#endif
+#ifdef MTIOCGETSIZE
+ case MTIOCGETSIZE:
+ result = mtiocgetsize(&krnl_arg.mttapesize, arg_size);
+ break;
+#endif
+#ifdef MTIOCFTFORMAT
+ case MTIOCFTFORMAT:
+ result = mtiocftformat(&krnl_arg.mtftformat, arg_size);
+ break;
+#endif
+#ifdef MTIOCFTCMD
+ case MTIOCFTCMD:
+ result = mtiocftcmd(&krnl_arg.mtftcmd, arg_size);
+ break;
+#endif
+ default:
+ result = -EINVAL;
+ break;
+ }
+ if ((result >= 0) && (dir & _IOC_READ)) {
+#if LINUX_VERSION_CODE > KERNEL_VER(2,1,3)
+ if (copy_to_user(arg, &krnl_arg, arg_size) != 0) {
+ TRACE_EXIT -EFAULT;
+ }
+#else
+ TRACE_CATCH(verify_area(VERIFY_WRITE, arg, arg_size),);
+ memcpy_tofs(arg, &krnl_arg, arg_size);
+#endif
+ }
+ TRACE_EXIT result;
+}
diff --git a/drivers/char/ftape/zftape/zftape-ctl.h b/drivers/char/ftape/zftape/zftape-ctl.h
new file mode 100644
index 000000000..91a2f2db7
--- /dev/null
+++ b/drivers/char/ftape/zftape/zftape-ctl.h
@@ -0,0 +1,60 @@
+#ifndef _ZFTAPE_CTL_H
+#define _ZFTAPE_CTL_H
+
+/*
+ * Copyright (C) 1996, 1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-ctl.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:19:02 $
+ *
+ * This file contains the non-standard IOCTL related definitions
+ * for the QIC-40/80 floppy-tape driver for Linux.
+ */
+
+#include <linux/config.h>
+#include <linux/ioctl.h>
+#include <linux/mtio.h>
+
+#include "../zftape/zftape-rw.h"
+
+#ifdef CONFIG_ZFTAPE_MODULE
+#define ftape_status (*zft_status)
+#endif
+
+extern int zft_offline;
+extern int zft_mt_compression;
+extern int zft_write_protected;
+extern int zft_header_read;
+extern unsigned int zft_unit;
+extern int zft_resid;
+
+extern void zft_reset_position(zft_position *pos);
+extern int zft_check_write_access(zft_position *pos);
+extern int zft_def_idle_state(void);
+extern int zft_dirty(void);
+
+/* hooks for the VFS interface
+ */
+extern int _zft_open(unsigned int dev_minor, unsigned int access_mode);
+extern int _zft_close(void);
+extern int _zft_ioctl(unsigned int command, void *arg);
+#endif
+
+
+
diff --git a/drivers/char/ftape/zftape/zftape-eof.c b/drivers/char/ftape/zftape/zftape-eof.c
new file mode 100644
index 000000000..0dfd27ff6
--- /dev/null
+++ b/drivers/char/ftape/zftape/zftape-eof.c
@@ -0,0 +1,207 @@
+/*
+ * I use these routines just to decide when I have to fake a
+ * volume-table to preserve compatability to original ftape.
+ */
+/*
+ * Copyright (C) 1994-1995 Bas Laarhoven.
+ *
+ * Modified for zftape 1996, 1997 Claus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-eof.c,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:19:02 $
+ *
+ * This file contains the eof mark handling code
+ * for the QIC-40/80 floppy-tape driver for Linux.
+ */
+
+#include <linux/string.h>
+#include <linux/errno.h>
+
+#include <linux/zftape.h>
+
+#include "../zftape/zftape-init.h"
+#include "../zftape/zftape-rw.h"
+#include "../zftape/zftape-eof.h"
+
+/* Global vars.
+ */
+
+/* a copy of the failed sector log from the header segment.
+ */
+eof_mark_union *zft_eof_map;
+
+/* number of eof marks (entries in bad sector log) on tape.
+ */
+int zft_nr_eof_marks = -1;
+
+
+/* Local vars.
+ */
+
+static char linux_tape_label[] = "Linux raw format V";
+enum {
+ min_fmt_version = 1, max_fmt_version = 2
+};
+static unsigned ftape_fmt_version = 0;
+
+
+/* Ftape (mis)uses the bad sector log to record end-of-file marks.
+ * Initially (when the tape is erased) all entries in the bad sector
+ * log are added to the tape's bad sector map. The bad sector log then
+ * is cleared.
+ *
+ * The bad sector log normally contains entries of the form:
+ * even 16-bit word: segment number of bad sector
+ * odd 16-bit word: encoded date
+ * There can be a total of 448 entries (1792 bytes).
+ *
+ * My guess is that no program is using this bad sector log (the *
+ * format seems useless as there is no indication of the bad sector
+ * itself, only the segment) However, if any program does use the bad
+ * sector log, the format used by ftape will let the program think
+ * there are some bad sectors and no harm is done.
+ *
+ * The eof mark entries that ftape stores in the bad sector log: even
+ * 16-bit word: segment number of eof mark odd 16-bit word: sector
+ * number of eof mark [1..32]
+ *
+ * The zft_eof_map as maintained is a sorted list of eof mark entries.
+ *
+ *
+ * The tape name field in the header segments is used to store a linux
+ * tape identification string and a version number. This way the tape
+ * can be recognized as a Linux raw format tape when using tools under
+ * other OS's.
+ *
+ * 'Wide' QIC tapes (format code 4) don't have a failed sector list
+ * anymore. That space is used for the (longer) bad sector map that
+ * now is a variable length list too. We now store our end-of-file
+ * marker list after the bad-sector-map on tape. The list is delimited
+ * by a (__u32) 0 entry.
+ */
+
+int zft_ftape_validate_label(char *label)
+{
+ static char tmp_label[45];
+ int result = 0;
+ TRACE_FUN(ft_t_any);
+
+ memcpy(tmp_label, label, FT_LABEL_SZ);
+ tmp_label[FT_LABEL_SZ] = '\0';
+ TRACE(ft_t_noise, "tape label = `%s'", tmp_label);
+ ftape_fmt_version = 0;
+ if (memcmp(label, linux_tape_label, strlen(linux_tape_label)) == 0) {
+ int pos = strlen(linux_tape_label);
+ while (label[pos] >= '0' && label[pos] <= '9') {
+ ftape_fmt_version *= 10;
+ ftape_fmt_version = label[ pos++] - '0';
+ }
+ result = (ftape_fmt_version >= min_fmt_version &&
+ ftape_fmt_version <= max_fmt_version);
+ }
+ TRACE(ft_t_noise, "format version = %d", ftape_fmt_version);
+ TRACE_EXIT result;
+}
+
+static __u8 * find_end_of_eof_list(__u8 * ptr, __u8 * limit)
+{
+ while (ptr + 3 < limit) {
+
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,0,0)
+ if (get_unaligned((__u32*)ptr)) {
+ ++(__u32*)ptr;
+ } else {
+ return ptr;
+ }
+#else
+ if (*(__u32*)ptr) {
+ ++(__u32*)ptr;
+ } else {
+ return ptr;
+ }
+#endif
+ }
+ return NULL;
+}
+
+void zft_ftape_extract_file_marks(__u8* address)
+{
+ int i;
+ TRACE_FUN(ft_t_any);
+
+ zft_eof_map = NULL;
+ if (ft_format_code == fmt_var || ft_format_code == fmt_big) {
+ __u8* end;
+ __u8* start = ftape_find_end_of_bsm_list(address);
+
+ zft_nr_eof_marks = 0;
+ if (start) {
+ start += 3; /* skip end of list mark */
+ end = find_end_of_eof_list(start,
+ address + FT_SEGMENT_SIZE);
+ if (end && end - start <= FT_FSL_SIZE) {
+ zft_nr_eof_marks = ((end - start) /
+ sizeof(eof_mark_union));
+ zft_eof_map = (eof_mark_union *)start;
+ } else {
+ TRACE(ft_t_err,
+ "EOF Mark List is too long or damaged!");
+ }
+ } else {
+ TRACE(ft_t_err,
+ "Bad Sector List is too long or damaged !");
+ }
+ } else {
+ zft_eof_map = (eof_mark_union *)&address[FT_FSL];
+ zft_nr_eof_marks = GET2(address, FT_FSL_CNT);
+ }
+ TRACE(ft_t_noise, "number of file marks: %d", zft_nr_eof_marks);
+ if (ftape_fmt_version == 1) {
+ TRACE(ft_t_info, "swapping version 1 fields");
+ /* version 1 format uses swapped sector and segment
+ * fields, correct that !
+ */
+ for (i = 0; i < zft_nr_eof_marks; ++i) {
+ __u16 tmp = GET2(&zft_eof_map[i].mark.segment,0);
+ PUT2(&zft_eof_map[i].mark.segment, 0,
+ GET2(&zft_eof_map[i].mark.date,0));
+ PUT2(&zft_eof_map[i].mark.date, 0, tmp);
+ }
+ }
+ for (i = 0; i < zft_nr_eof_marks; ++i) {
+ TRACE(ft_t_noise, "eof mark: %5d/%2d",
+ GET2(&zft_eof_map[i].mark.segment, 0),
+ GET2(&zft_eof_map[i].mark.date,0));
+ }
+ TRACE_EXIT;
+}
+
+void zft_clear_ftape_file_marks(void)
+{
+ TRACE_FUN(ft_t_flow);
+ /* Clear failed sector log: remove all tape marks. We
+ * don't use old ftape-style EOF-marks.
+ */
+ TRACE(ft_t_info, "Clearing old ftape's eof map");
+ memset(zft_eof_map, 0, zft_nr_eof_marks * sizeof(__u32));
+ zft_nr_eof_marks = 0;
+ PUT2(zft_hseg_buf, FT_FSL_CNT, 0); /* nr of eof-marks */
+ zft_header_changed = 1;
+ zft_update_label(zft_hseg_buf);
+ TRACE_EXIT;
+}
diff --git a/drivers/char/ftape/ftape-eof.h b/drivers/char/ftape/zftape/zftape-eof.h
index 58df1f019..26568c26c 100644
--- a/drivers/char/ftape/ftape-eof.h
+++ b/drivers/char/ftape/zftape/zftape-eof.h
@@ -1,10 +1,9 @@
-
-
-#ifndef _FTAPE_EOF_H
-#define _FTAPE_EOF_H
+#ifndef _ZFTAPE_EOF_H
+#define _ZFTAPE_EOF_H
/*
* Copyright (C) 1994-1995 Bas Laarhoven.
+ * adaptaed for zftape 1996, 1997 by Claus Heine
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -21,35 +20,33 @@
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
- $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-eof.h,v $
- $Author: bas $
- *
- $Revision: 1.12 $
- $Date: 1995/04/22 07:30:15 $
- $State: Beta $
+ * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-eof.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:19:03 $
*
* Definitions and declarations for the end of file markers
* for the QIC-40/80 floppy-tape driver for Linux.
*/
+#include <linux/ftape-header-segment.h>
+#include "../zftape/zftape-buffers.h"
+/* failed sector log size (only used if format code != 4).
+ */
+
+typedef union {
+ ft_fsl_entry mark;
+ __u32 entry;
+} eof_mark_union;
+
/* ftape-eof.c defined global vars.
*/
-extern int failed_sector_log_changed;
-extern int eof_mark;
+extern int zft_nr_eof_marks;
+extern eof_mark_union *zft_eof_map;
/* ftape-eof.c defined global functions.
*/
-extern void clear_eof_mark_if_set(unsigned segment, unsigned byte_count);
-extern void reset_eof_list(void);
-extern int check_for_eof(unsigned segment);
-extern int ftape_weof(unsigned count, unsigned segment, unsigned sector);
-extern int ftape_erase(void);
-extern void put_file_mark_in_map(unsigned segment, unsigned sector);
-extern void extract_file_marks(byte * address);
-extern int update_failed_sector_log(byte * buffer);
-extern int ftape_seek_eom(void);
-extern int ftape_seek_eof(unsigned count);
-extern int ftape_file_no(daddr_t * file, daddr_t * block);
-extern int ftape_validate_label(char *label);
+extern void zft_ftape_extract_file_marks(__u8* address);
+extern int zft_ftape_validate_label(char* label);
+extern void zft_clear_ftape_file_marks(void);
#endif
diff --git a/drivers/char/ftape/zftape/zftape-init.c b/drivers/char/ftape/zftape/zftape-init.c
new file mode 100644
index 000000000..c6a4f4506
--- /dev/null
+++ b/drivers/char/ftape/zftape/zftape-init.c
@@ -0,0 +1,512 @@
+/*
+ * Copyright (C) 1996, 1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * This file contains the code that registers the zftape frontend
+ * to the ftape floppy tape driver for Linux
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/version.h>
+#include <linux/fs.h>
+#include <asm/segment.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/major.h>
+#include <linux/malloc.h>
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
+#endif
+#include <linux/fcntl.h>
+#include <linux/wrapper.h>
+
+#include <linux/zftape.h>
+#if LINUX_VERSION_CODE >=KERNEL_VER(2,1,16)
+#include <linux/init.h>
+#else
+#define __initdata
+#define __initfunc(__arg) __arg
+#endif
+
+#include "../zftape/zftape-init.h"
+#include "../zftape/zftape-read.h"
+#include "../zftape/zftape-write.h"
+#include "../zftape/zftape-ctl.h"
+#include "../zftape/zftape-buffers.h"
+#include "../zftape/zftape_syms.h"
+
+char zft_src[] __initdata = "$Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-init.c,v $";
+char zft_rev[] __initdata = "$Revision: 1.8 $";
+char zft_dat[] __initdata = "$Date: 1997/11/06 00:48:56 $";
+
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,18)
+MODULE_AUTHOR("(c) 1996, 1997 Claus-Justus Heine "
+ "(claus@momo.math.rwth-aachen.de)");
+MODULE_DESCRIPTION(ZFTAPE_VERSION " - "
+ "VFS interface for the Linux floppy tape driver. "
+ "Support for QIC-113 compatible volume table "
+ "and builtin compression (lzrw3 algorithm)");
+MODULE_SUPPORTED_DEVICE("char-major-27");
+#endif
+
+/* Global vars.
+ */
+struct zft_cmpr_ops *zft_cmpr_ops = NULL;
+const ftape_info *zft_status;
+
+/* Local vars.
+ */
+static int busy_flag = 0;
+static int orig_sigmask;
+
+/* the interface to the kernel vfs layer
+ */
+
+/* Note about llseek():
+ *
+ * st.c and tpqic.c update fp->f_pos but don't implment llseek() and
+ * initialize the llseek component of the file_ops struct with NULL.
+ * This means that the user will get the default seek, but the tape
+ * device will not respect the new position, but happily read from the
+ * old position. Think a zftape specific llseek() function would be
+ * better, returning -ESPIPE. TODO.
+ */
+
+static int zft_open (struct inode *ino, struct file *filep);
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,31)
+static int zft_close(struct inode *ino, struct file *filep);
+#else
+static void zft_close(struct inode *ino, struct file *filep);
+#endif
+static int zft_ioctl(struct inode *ino, struct file *filep,
+ unsigned int command, unsigned long arg);
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,56)
+static int zft_mmap(struct file *filep, struct vm_area_struct *vma);
+#else
+static int zft_mmap(struct inode *ino, struct file *filep,
+ struct vm_area_struct *vma);
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,60)
+static ssize_t zft_read (struct file *fp, char *buff,
+ size_t req_len, loff_t *ppos);
+static ssize_t zft_write(struct file *fp, const char *buff,
+ size_t req_len, loff_t *ppos);
+#elif LINUX_VERSION_CODE >= KERNEL_VER(2,1,0)
+static long zft_read (struct inode *ino, struct file *fp, char *buff,
+ unsigned long req_len);
+static long zft_write(struct inode *ino, struct file *fp, const char *buff,
+ unsigned long req_len);
+#else
+static int zft_read (struct inode *ino, struct file *fp, char *buff,
+ int req_len);
+#if LINUX_VERSION_CODE >= KERNEL_VER(1,3,0)
+static int zft_write(struct inode *ino, struct file *fp, const char *buff,
+ int req_len);
+#else
+static int zft_write(struct inode *ino, struct file *fp, char *buff,
+ int req_len);
+#endif
+#endif
+
+static struct file_operations zft_cdev =
+{
+ NULL, /* llseek */
+ zft_read, /* read */
+ zft_write, /* write */
+ NULL, /* readdir */
+ NULL, /* select */
+ zft_ioctl, /* ioctl */
+ zft_mmap, /* mmap */
+ zft_open, /* open */
+ zft_close, /* release */
+ NULL, /* fsync */
+};
+
+/* Open floppy tape device
+ */
+static int zft_open(struct inode *ino, struct file *filep)
+{
+ int result;
+ TRACE_FUN(ft_t_flow);
+
+#if LINUX_VERSION_CODE < KERNEL_VER(2,1,18)
+ if (!MOD_IN_USE) {
+ MOD_INC_USE_COUNT; /* lock module in memory */
+ }
+#else
+ MOD_INC_USE_COUNT; /* sets MOD_VISITED and MOD_USED_ONCE,
+ * locking is done with can_unload()
+ */
+#endif
+ TRACE(ft_t_flow, "called for minor %d", MINOR(ino->i_rdev));
+ if (busy_flag) {
+ TRACE_ABORT(-EBUSY, ft_t_warn, "failed: already busy");
+ }
+ busy_flag = 1;
+ if ((MINOR(ino->i_rdev) & ~(ZFT_MINOR_OP_MASK | FTAPE_NO_REWIND))
+ >
+ FTAPE_SEL_D) {
+ busy_flag = 0;
+#if defined(MODULE) && LINUX_VERSION_CODE < KERNEL_VER(2,1,18)
+ if (!zft_dirty()) {
+ MOD_DEC_USE_COUNT; /* unlock module in memory */
+ }
+#endif
+ TRACE_ABORT(-ENXIO, ft_t_err, "failed: illegal unit nr");
+ }
+ orig_sigmask = current->blocked;
+ current->blocked = _BLOCK_ALL;
+ result = _zft_open(MINOR(ino->i_rdev), filep->f_flags & O_ACCMODE);
+ if (result < 0) {
+ current->blocked = orig_sigmask; /* restore mask */
+ busy_flag = 0;
+#if defined(MODULE) && LINUX_VERSION_CODE < KERNEL_VER(2,1,18)
+ if (!zft_dirty()) {
+ MOD_DEC_USE_COUNT; /* unlock module in memory */
+ }
+#endif
+ TRACE_ABORT(result, ft_t_err, "_ftape_open failed");
+ } else {
+ /* Mask signals that will disturb proper operation of the
+ * program that is calling.
+ */
+ current->blocked = orig_sigmask | _DO_BLOCK;
+ TRACE_EXIT 0;
+ }
+}
+
+/* Close floppy tape device
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,31)
+static int zft_close(struct inode *ino, struct file *filep)
+#else
+static void zft_close(struct inode *ino, struct file *filep)
+#endif
+{
+ int result;
+ TRACE_FUN(ft_t_flow);
+
+ if (!busy_flag || MINOR(ino->i_rdev) != zft_unit) {
+ TRACE(ft_t_err, "failed: not busy or wrong unit");
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,31)
+ TRACE_EXIT 0;
+#else
+ TRACE_EXIT; /* keep busy_flag !(?) */
+#endif
+ }
+ current->blocked = _BLOCK_ALL;
+ result = _zft_close();
+ if (result < 0) {
+ TRACE(ft_t_err, "_zft_close failed");
+ }
+ current->blocked = orig_sigmask; /* restore before open state */
+ busy_flag = 0;
+#if defined(MODULE) && LINUX_VERSION_CODE < KERNEL_VER(2,1,18)
+ if (!zft_dirty()) {
+ MOD_DEC_USE_COUNT; /* unlock module in memory */
+ }
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,31)
+ TRACE_EXIT 0;
+#else
+ TRACE_EXIT;
+#endif
+}
+
+/* Ioctl for floppy tape device
+ */
+static int zft_ioctl(struct inode *ino, struct file *filep,
+ unsigned int command, unsigned long arg)
+{
+ int result = -EIO;
+ int old_sigmask;
+ TRACE_FUN(ft_t_flow);
+
+ if (!busy_flag || MINOR(ino->i_rdev) != zft_unit || ft_failure) {
+ TRACE_ABORT(-EIO, ft_t_err,
+ "failed: not busy, failure or wrong unit");
+ }
+ old_sigmask = current->blocked; /* save mask */
+ current->blocked = _BLOCK_ALL;
+ /* This will work as long as sizeof(void *) == sizeof(long) */
+ result = _zft_ioctl(command, (void *) arg);
+ current->blocked = old_sigmask; /* restore mask */
+ TRACE_EXIT result;
+}
+
+/* Ioctl for floppy tape device
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,56)
+static int zft_mmap(struct file *filep, struct vm_area_struct *vma)
+#else
+static int zft_mmap(struct inode *ino,
+ struct file *filep,
+ struct vm_area_struct *vma)
+#endif
+{
+ int result = -EIO;
+ int old_sigmask;
+ TRACE_FUN(ft_t_flow);
+
+ if (!busy_flag ||
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,56)
+ MINOR(filep->f_dentry->d_inode->i_rdev) != zft_unit ||
+#else
+ MINOR(ino->i_rdev) != zft_unit ||
+#endif
+ ft_failure)
+ {
+ TRACE_ABORT(-EIO, ft_t_err,
+ "failed: not busy, failure or wrong unit");
+ }
+ old_sigmask = current->blocked; /* save mask */
+ current->blocked = _BLOCK_ALL;
+ if ((result = ftape_mmap(vma)) >= 0) {
+#ifndef MSYNC_BUG_WAS_FIXED
+ static struct vm_operations_struct dummy = { NULL, };
+ vma->vm_ops = &dummy;
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,45)
+ vma->vm_dentry = dget(filep->f_dentry);
+#else
+ vma_set_inode (vma, ino);
+ inode_inc_count (ino);
+#endif
+ }
+ current->blocked = old_sigmask; /* restore mask */
+ TRACE_EXIT result;
+}
+
+/* Read from floppy tape device
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,60)
+static ssize_t zft_read(struct file *fp, char *buff,
+ size_t req_len, loff_t *ppos)
+#elif LINUX_VERSION_CODE >= KERNEL_VER(2,1,0)
+static long zft_read(struct inode *ino, struct file *fp, char *buff,
+ unsigned long req_len)
+#else
+static int zft_read(struct inode *ino, struct file *fp, char *buff,
+ int req_len)
+#endif
+{
+ int result = -EIO;
+ int old_sigmask;
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,60)
+ struct inode *ino = fp->f_dentry->d_inode;
+#endif
+ TRACE_FUN(ft_t_flow);
+
+ TRACE(ft_t_data_flow, "called with count: %ld", (unsigned long)req_len);
+ if (!busy_flag || MINOR(ino->i_rdev) != zft_unit || ft_failure) {
+ TRACE_ABORT(-EIO, ft_t_err,
+ "failed: not busy, failure or wrong unit");
+ }
+ old_sigmask = current->blocked; /* save mask */
+ current->blocked = _BLOCK_ALL;
+ result = _zft_read(buff, req_len);
+ current->blocked = old_sigmask; /* restore mask */
+ TRACE(ft_t_data_flow, "return with count: %d", result);
+ TRACE_EXIT result;
+}
+
+/* Write to tape device
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,60)
+static ssize_t zft_write(struct file *fp, const char *buff,
+ size_t req_len, loff_t *ppos)
+#elif LINUX_VERSION_CODE >= KERNEL_VER(2,1,0)
+static long zft_write(struct inode *ino, struct file *fp, const char *buff,
+ unsigned long req_len)
+#elif LINUX_VERSION_CODE >= KERNEL_VER(1,3,0)
+static int zft_write(struct inode *ino, struct file *fp, const char *buff,
+ int req_len)
+#else
+static int zft_write(struct inode *ino, struct file *fp, char *buff,
+ int req_len)
+#endif
+{
+ int result = -EIO;
+ int old_sigmask;
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,60)
+ struct inode *ino = fp->f_dentry->d_inode;
+#endif
+ TRACE_FUN(ft_t_flow);
+
+ TRACE(ft_t_flow, "called with count: %ld", (unsigned long)req_len);
+ if (!busy_flag || MINOR(ino->i_rdev) != zft_unit || ft_failure) {
+ TRACE_ABORT(-EIO, ft_t_err,
+ "failed: not busy, failure or wrong unit");
+ }
+ old_sigmask = current->blocked; /* save mask */
+ current->blocked = _BLOCK_ALL;
+ result = _zft_write(buff, req_len);
+ current->blocked = old_sigmask; /* restore mask */
+ TRACE(ft_t_data_flow, "return with count: %d", result);
+ TRACE_EXIT result;
+}
+
+/* END OF VFS INTERFACE
+ *
+ *****************************************************************************/
+
+/* driver/module initialization
+ */
+
+/* the compression module has to call this function to hook into the zftape
+ * code
+ */
+int zft_cmpr_register(struct zft_cmpr_ops *new_ops)
+{
+ TRACE_FUN(ft_t_flow);
+
+ if (zft_cmpr_ops != NULL) {
+ TRACE_EXIT -EBUSY;
+ } else {
+ zft_cmpr_ops = new_ops;
+ TRACE_EXIT 0;
+ }
+}
+
+struct zft_cmpr_ops *zft_cmpr_unregister(void)
+{
+ struct zft_cmpr_ops *old_ops = zft_cmpr_ops;
+ TRACE_FUN(ft_t_flow);
+
+ zft_cmpr_ops = NULL;
+ TRACE_EXIT old_ops;
+}
+
+/* lock the zft-compressor() module.
+ */
+int zft_cmpr_lock(int try_to_load)
+{
+ if (zft_cmpr_ops == NULL) {
+#ifdef CONFIG_KERNELD
+ if (try_to_load) {
+ request_module("zft-compressor");
+ if (zft_cmpr_ops == NULL) {
+ return -ENOSYS;
+ }
+ } else {
+ return -ENOSYS;
+ }
+#else
+ return -ENOSYS;
+#endif
+ }
+ (*zft_cmpr_ops->lock)();
+ return 0;
+}
+
+#ifdef CONFIG_ZFT_COMPRESSOR
+extern int zft_compressor_init(void);
+#endif
+
+/* Called by modules package when installing the driver or by kernel
+ * during the initialization phase
+ */
+__initfunc(int zft_init(void))
+{
+ TRACE_FUN(ft_t_flow);
+
+#ifdef MODULE
+ printk(KERN_INFO ZFTAPE_VERSION "\n");
+ if (TRACE_LEVEL >= ft_t_info) {
+ printk(
+KERN_INFO
+"(c) 1996, 1997 Claus-Justus Heine (claus@momo.math.rwth-aachen.de)\n"
+KERN_INFO
+"vfs interface for ftape floppy tape driver.\n"
+KERN_INFO
+"Support for QIC-113 compatible volume table, dynamic memory allocation\n"
+KERN_INFO
+"and builtin compression (lzrw3 algorithm).\n"
+KERN_INFO
+"Compiled for Linux version %s"
+#ifdef MODVERSIONS
+ " with versioned symbols"
+#endif
+ "\n", UTS_RELEASE);
+ }
+#else /* !MODULE */
+ /* print a short no-nonsense boot message */
+ printk(KERN_INFO ZFTAPE_VERSION " for Linux " UTS_RELEASE "\n");
+#endif /* MODULE */
+ TRACE(ft_t_info, "zft_init @ 0x%p", zft_init);
+ TRACE(ft_t_info,
+ "installing zftape VFS interface for ftape driver ...");
+ TRACE_CATCH(register_chrdev(QIC117_TAPE_MAJOR, "zft", &zft_cdev),);
+#if LINUX_VERSION_CODE >= KERNEL_VER(1,2,0)
+# if LINUX_VERSION_CODE < KERNEL_VER(2,1,18)
+ register_symtab(&zft_symbol_table); /* add global zftape symbols */
+# endif
+#endif
+#ifdef CONFIG_ZFT_COMPRESSOR
+ (void)zft_compressor_init();
+#endif
+ zft_status = ftape_get_status(); /* fetch global data of ftape
+ * hardware driver
+ */
+ TRACE_EXIT 0;
+}
+
+
+#ifdef MODULE
+#if LINUX_VERSION_CODE <= KERNEL_VER(1,2,13) && defined(MODULE)
+char kernel_version[] = UTS_RELEASE;
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,18)
+/* Called by modules package before trying to unload the module
+ */
+static int can_unload(void)
+{
+ return (zft_dirty() || busy_flag) ? -EBUSY : 0;
+}
+#endif
+/* Called by modules package when installing the driver
+ */
+int init_module(void)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,18)
+ if (!mod_member_present(&__this_module, can_unload)) {
+ return -EBUSY;
+ }
+ __this_module.can_unload = can_unload;
+#endif
+ return zft_init();
+}
+
+/* Called by modules package when removing the driver
+ */
+void cleanup_module(void)
+{
+ TRACE_FUN(ft_t_flow);
+
+ if (unregister_chrdev(QIC117_TAPE_MAJOR, "zft") != 0) {
+ TRACE(ft_t_warn, "failed");
+ } else {
+ TRACE(ft_t_info, "successful");
+ }
+ zft_uninit_mem(); /* release remaining memory, if any */
+ printk(KERN_INFO "zftape successfully unloaded.\n");
+ TRACE_EXIT;
+}
+
+#endif /* MODULE */
diff --git a/drivers/char/ftape/zftape/zftape-init.h b/drivers/char/ftape/zftape/zftape-init.h
new file mode 100644
index 000000000..39d83f78c
--- /dev/null
+++ b/drivers/char/ftape/zftape/zftape-init.h
@@ -0,0 +1,85 @@
+#ifndef _ZFTAPE_INIT_H
+#define _ZFTAPE_INIT_H
+
+/*
+ * Copyright (C) 1996, 1997 Claus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-init.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:19:05 $
+ *
+ * This file contains definitions and macro for the vfs
+ * interface defined by zftape
+ *
+ */
+
+#include <linux/ftape-header-segment.h>
+
+#include "../lowlevel/ftape-tracing.h"
+#include "../lowlevel/ftape-ctl.h"
+#include "../lowlevel/ftape-read.h"
+#include "../lowlevel/ftape-write.h"
+#include "../lowlevel/ftape-bsm.h"
+#include "../lowlevel/ftape-io.h"
+#include "../lowlevel/ftape-buffer.h"
+#include "../lowlevel/ftape-format.h"
+
+#include "../zftape/zftape-rw.h"
+
+#ifdef MODULE
+#define ftape_status (*zft_status)
+#endif
+
+extern const ftape_info *zft_status; /* needed for zftape-vtbl.h */
+
+#include "../zftape/zftape-vtbl.h"
+
+struct zft_cmpr_ops {
+ int (*write)(int *write_cnt,
+ __u8 *dst_buf, const int seg_sz,
+ const __u8 *src_buf, const int req_len,
+ const zft_position *pos, const zft_volinfo *volume);
+ int (*read)(int *read_cnt,
+ __u8 *dst_buf, const int req_len,
+ const __u8 *src_buf, const int seg_sz,
+ const zft_position *pos, const zft_volinfo *volume);
+ int (*seek)(unsigned int new_block_pos,
+ zft_position *pos, const zft_volinfo *volume,
+ __u8 *buffer);
+ void (*lock) (void);
+ void (*reset) (void);
+ void (*cleanup)(void);
+};
+
+extern struct zft_cmpr_ops *zft_cmpr_ops;
+/* zftape-init.c defined global functions.
+ */
+extern int zft_cmpr_register(struct zft_cmpr_ops *new_ops);
+extern struct zft_cmpr_ops *zft_cmpr_unregister(void);
+extern int zft_cmpr_lock(int try_to_load);
+
+#ifdef MODULE
+
+asmlinkage extern int init_module(void);
+asmlinkage extern void cleanup_module(void);
+
+#endif
+
+#endif
+
+
diff --git a/drivers/char/ftape/zftape/zftape-read.c b/drivers/char/ftape/zftape/zftape-read.c
new file mode 100644
index 000000000..c7d319fac
--- /dev/null
+++ b/drivers/char/ftape/zftape/zftape-read.c
@@ -0,0 +1,390 @@
+/*
+ * Copyright (C) 1996, 1997 Claus-Justus Heine
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-read.c,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:19:06 $
+ *
+ * This file contains the high level reading code
+ * for the QIC-117 floppy-tape driver for Linux.
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
+#endif
+
+#include <linux/zftape.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,6)
+#include <asm/uaccess.h>
+#else
+#include <asm/segment.h>
+#endif
+
+#include "../zftape/zftape-init.h"
+#include "../zftape/zftape-eof.h"
+#include "../zftape/zftape-ctl.h"
+#include "../zftape/zftape-write.h"
+#include "../zftape/zftape-read.h"
+#include "../zftape/zftape-rw.h"
+#include "../zftape/zftape-vtbl.h"
+
+/* Global vars.
+ */
+int zft_just_before_eof = 0;
+
+/* Local vars.
+ */
+static int buf_len_rd = 0;
+
+void zft_zap_read_buffers(void)
+{
+ buf_len_rd = 0;
+}
+
+int zft_read_header_segments(void)
+{
+ TRACE_FUN(ft_t_flow);
+
+ zft_header_read = 0;
+ TRACE_CATCH(zft_vmalloc_once(&zft_hseg_buf, FT_SEGMENT_SIZE),);
+ TRACE_CATCH(ftape_read_header_segment(zft_hseg_buf),);
+ TRACE(ft_t_info, "Segments written since first format: %d",
+ (int)GET4(zft_hseg_buf, FT_SEG_CNT));
+ zft_qic113 = (ft_format_code != fmt_normal &&
+ ft_format_code != fmt_1100ft &&
+ ft_format_code != fmt_425ft);
+ TRACE(ft_t_info, "ft_first_data_segment: %d, ft_last_data_segment: %d",
+ ft_first_data_segment, ft_last_data_segment);
+ zft_capacity = zft_get_capacity();
+ zft_old_ftape = zft_ftape_validate_label(&zft_hseg_buf[FT_LABEL]);
+ if (zft_old_ftape) {
+ TRACE(ft_t_info,
+"Found old ftaped tape, emulating eof marks, entering read-only mode");
+ zft_ftape_extract_file_marks(zft_hseg_buf);
+ TRACE_CATCH(zft_fake_volume_headers(zft_eof_map,
+ zft_nr_eof_marks),);
+ } else {
+ /* the specs say that the volume table must be
+ * initialized with zeroes during formatting, so it
+ * MUST be readable, i.e. contain vaid ECC
+ * information.
+ */
+ TRACE_CATCH(ftape_read_segment(ft_first_data_segment,
+ zft_deblock_buf,
+ FT_RD_SINGLE),);
+ TRACE_CATCH(zft_extract_volume_headers(zft_deblock_buf),);
+ }
+ zft_header_read = 1;
+ zft_set_flags(zft_unit);
+ zft_reset_position(&zft_pos);
+ TRACE_EXIT 0;
+}
+
+int zft_fetch_segment_fraction(const unsigned int segment, void *buffer,
+ const ft_read_mode_t read_mode,
+ const unsigned int start,
+ const unsigned int size)
+{
+ int seg_sz;
+ TRACE_FUN(ft_t_flow);
+
+ if (segment == zft_deblock_segment) {
+ TRACE(ft_t_data_flow,
+ "re-using segment %d already in deblock buffer",
+ segment);
+ seg_sz = zft_get_seg_sz(segment);
+ if (start > seg_sz) {
+ TRACE_ABORT(-EINVAL, ft_t_bug,
+ "trying to read beyond end of segment:\n"
+ KERN_INFO "seg_sz : %d\n"
+ KERN_INFO "start : %d\n"
+ KERN_INFO "segment: %d",
+ seg_sz, start, segment);
+ }
+ if ((start + size) > seg_sz) {
+ TRACE_EXIT seg_sz - start;
+ }
+ TRACE_EXIT size;
+ }
+ seg_sz = ftape_read_segment_fraction(segment, buffer, read_mode,
+ start, size);
+ TRACE(ft_t_data_flow, "segment %d, result %d", segment, seg_sz);
+ if ((int)seg_sz >= 0 && start == 0 && size == FT_SEGMENT_SIZE) {
+ /* this implicitly assumes that we are always called with
+ * buffer == zft_deblock_buf
+ */
+ zft_deblock_segment = segment;
+ } else {
+ zft_deblock_segment = -1;
+ }
+ TRACE_EXIT seg_sz;
+}
+
+/*
+ * out:
+ *
+ * int *read_cnt: the number of bytes we removed from the
+ * zft_deblock_buf (result)
+ *
+ * int *to_do : the remaining size of the read-request. Is changed.
+ *
+ * in:
+ *
+ * char *buff : buff is the address of the upper part of the user
+ * buffer, that hasn't been filled with data yet.
+ * int buf_pos_read: copy of buf_pos_rd
+ * int buf_len_read: copy of buf_len_rd
+ * char *zft_deblock_buf: ftape_zft_deblock_buf
+ *
+ * returns the amount of data actually copied to the user-buffer
+ *
+ * to_do MUST NOT SHRINK except to indicate an EOT. In this case to_do
+ * has to be set to 0. We cannot return -ENOSPC, because we return the
+ * amount of data actually * copied to the user-buffer
+ */
+static int zft_simple_read (int *read_cnt,
+ __u8 *dst_buf,
+ const int to_do,
+ const __u8 *src_buf,
+ const int seg_sz,
+ const zft_position *pos,
+ const zft_volinfo *volume)
+{
+ TRACE_FUN(ft_t_flow);
+
+ if (seg_sz - pos->seg_byte_pos < to_do) {
+ *read_cnt = seg_sz - pos->seg_byte_pos;
+ } else {
+ *read_cnt = to_do;
+ }
+#if LINUX_VERSION_CODE > KERNEL_VER(2,1,3)
+ if (copy_to_user(dst_buf,
+ src_buf + pos->seg_byte_pos, *read_cnt) != 0) {
+ TRACE_EXIT -EFAULT;
+ }
+#else
+ TRACE_CATCH(verify_area(VERIFY_WRITE, dst_buf, *read_cnt),);
+ memcpy_tofs(dst_buf, src_buf + pos->seg_byte_pos, *read_cnt);
+#endif
+ TRACE(ft_t_noise, "nr bytes just read: %d", *read_cnt);
+ TRACE_EXIT *read_cnt;
+}
+
+/* req_len: gets clipped due to EOT of EOF.
+ * req_clipped: is a flag indicating whether req_len was clipped or not
+ * volume: contains information on current volume (blk_sz etc.)
+ */
+static int check_read_access(int *req_len,
+ const zft_volinfo **volume,
+ int *req_clipped,
+ const zft_position *pos)
+{
+ static __s64 remaining = 0;
+ static int eod;
+ TRACE_FUN(ft_t_flow);
+
+ if (zft_io_state != zft_reading) {
+ if (zft_offline) { /* offline includes no_tape */
+ TRACE_ABORT(-ENXIO, ft_t_warn,
+ "tape is offline or no cartridge");
+ }
+ if (!ft_formatted) {
+ TRACE_ABORT(-EACCES,
+ ft_t_warn, "tape is not formatted");
+ }
+ /* now enter defined state, read header segment if not
+ * already done and flush write buffers
+ */
+ TRACE_CATCH(zft_def_idle_state(),);
+ zft_io_state = zft_reading;
+ if (zft_tape_at_eod(pos)) {
+ eod = 1;
+ TRACE_EXIT 1;
+ }
+ eod = 0;
+ *volume = zft_find_volume(pos->seg_pos);
+ /* get the space left until EOF */
+ remaining = zft_check_for_eof(*volume, pos);
+ buf_len_rd = 0;
+ TRACE(ft_t_noise, "remaining: " LL_X ", vol_no: %d",
+ LL(remaining), (*volume)->count);
+ } else if (zft_tape_at_eod(pos)) {
+ if (++eod > 2) {
+ TRACE_EXIT -EIO; /* st.c also returns -EIO */
+ } else {
+ TRACE_EXIT 1;
+ }
+ }
+ if ((*req_len % (*volume)->blk_sz) != 0) {
+ /* this message is informational only. The user gets the
+ * proper return value
+ */
+ TRACE_ABORT(-EINVAL, ft_t_info,
+ "req_len %d not a multiple of block size %d",
+ *req_len, (*volume)->blk_sz);
+ }
+ /* As GNU tar doesn't accept partial read counts when the
+ * multiple volume flag is set, we make sure to return the
+ * requested amount of data. Except, of course, at the end of
+ * the tape or file mark.
+ */
+ remaining -= *req_len;
+ if (remaining <= 0) {
+ TRACE(ft_t_noise,
+ "clipped request from %d to %d.",
+ *req_len, (int)(*req_len + remaining));
+ *req_len += remaining;
+ *req_clipped = 1;
+ } else {
+ *req_clipped = 0;
+ }
+ TRACE_EXIT 0;
+}
+
+/* this_segs_size: the current segment's size.
+ * buff: the USER-SPACE buffer provided by the calling function.
+ * req_len: how much data should be read at most.
+ * volume: contains information on current volume (blk_sz etc.)
+ */
+static int empty_deblock_buf(__u8 *usr_buf, const int req_len,
+ const __u8 *src_buf, const int seg_sz,
+ zft_position *pos,
+ const zft_volinfo *volume)
+{
+ int cnt;
+ int result = 0;
+ TRACE_FUN(ft_t_flow);
+
+ TRACE(ft_t_data_flow, "this_segs_size: %d", seg_sz);
+ if (zft_use_compression && volume->use_compression) {
+ TRACE_CATCH(zft_cmpr_lock(1 /* try to load */),);
+ TRACE_CATCH(result= (*zft_cmpr_ops->read)(&cnt,
+ usr_buf, req_len,
+ src_buf, seg_sz,
+ pos, volume),);
+ } else {
+ TRACE_CATCH(result= zft_simple_read (&cnt,
+ usr_buf, req_len,
+ src_buf, seg_sz,
+ pos, volume),);
+ }
+ pos->volume_pos += result;
+ pos->tape_pos += cnt;
+ pos->seg_byte_pos += cnt;
+ buf_len_rd -= cnt; /* remaining bytes in buffer */
+ TRACE(ft_t_data_flow, "buf_len_rd: %d, cnt: %d", buf_len_rd, cnt);
+ if(pos->seg_byte_pos >= seg_sz) {
+ pos->seg_pos++;
+ pos->seg_byte_pos = 0;
+ }
+ TRACE(ft_t_data_flow, "bytes moved out of deblock-buffer: %d", cnt);
+ TRACE_EXIT result;
+}
+
+
+/* note: we store the segment id of the segment that is inside the
+ * deblock buffer. This spares a lot of ftape_read_segment()s when we
+ * use small block-sizes. The block-size may be 1kb (SECTOR_SIZE). In
+ * this case a MTFSR 28 maybe still inside the same segment.
+ */
+int _zft_read(char* buff, int req_len)
+{
+ int req_clipped;
+ int result = 0;
+ int bytes_read = 0;
+ static unsigned int seg_sz = 0;
+ static const zft_volinfo *volume = NULL;
+ TRACE_FUN(ft_t_flow);
+
+ zft_resid = req_len;
+ result = check_read_access(&req_len, &volume,
+ &req_clipped, &zft_pos);
+ switch(result) {
+ case 0:
+ break; /* nothing special */
+ case 1:
+ TRACE(ft_t_noise, "EOD reached");
+ TRACE_EXIT 0; /* EOD */
+ default:
+ TRACE_ABORT(result, ft_t_noise,
+ "check_read_access() failed with result %d",
+ result);
+ TRACE_EXIT result;
+ }
+ while (req_len > 0) {
+ /* Allow escape from this loop on signal !
+ */
+ FT_SIGNAL_EXIT(_DONT_BLOCK);
+ /* buf_len_rd == 0 means that we need to read a new
+ * segment.
+ */
+ if (buf_len_rd == 0) {
+ while((result = zft_fetch_segment(zft_pos.seg_pos,
+ zft_deblock_buf,
+ FT_RD_AHEAD)) == 0) {
+ zft_pos.seg_pos ++;
+ zft_pos.seg_byte_pos = 0;
+ }
+ if (result < 0) {
+ zft_resid -= bytes_read;
+ TRACE_ABORT(result, ft_t_noise,
+ "zft_fetch_segment(): %d",
+ result);
+ }
+ seg_sz = result;
+ buf_len_rd = seg_sz - zft_pos.seg_byte_pos;
+ }
+ TRACE_CATCH(result = empty_deblock_buf(buff,
+ req_len,
+ zft_deblock_buf,
+ seg_sz,
+ &zft_pos,
+ volume),
+ zft_resid -= bytes_read);
+ TRACE(ft_t_data_flow, "bytes just read: %d", result);
+ bytes_read += result; /* what we got so far */
+ buff += result; /* index in user-buffer */
+ req_len -= result; /* what's left from req_len */
+ } /* while (req_len > 0) */
+ if (req_clipped) {
+ TRACE(ft_t_data_flow,
+ "maybe partial count because of eof mark");
+ if (zft_just_before_eof && bytes_read == 0) {
+ /* req_len was > 0, but user didn't get
+ * anything the user has read in the eof-mark
+ */
+ zft_move_past_eof(&zft_pos);
+ ftape_abort_operation();
+ } else {
+ /* don't skip to the next file before the user
+ * tried to read a second time past EOF Just
+ * mark that we are at EOF and maybe decrement
+ * zft_seg_pos to stay in the same volume;
+ */
+ zft_just_before_eof = 1;
+ zft_position_before_eof(&zft_pos, volume);
+ TRACE(ft_t_noise, "just before eof");
+ }
+ }
+ zft_resid -= result; /* for MTSTATUS */
+ TRACE_EXIT bytes_read;
+}
diff --git a/drivers/char/ftape/zftape/zftape-read.h b/drivers/char/ftape/zftape/zftape-read.h
new file mode 100644
index 000000000..ad2e9dbc4
--- /dev/null
+++ b/drivers/char/ftape/zftape/zftape-read.h
@@ -0,0 +1,53 @@
+#ifndef _ZFTAPE_READ_H
+#define _ZFTAPE_READ_H
+
+/*
+ * Copyright (C) 1996, 1997 Claus-Justus Heine
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-read.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:19:07 $
+ *
+ * This file contains the definitions for the read functions
+ * for the zftape driver for Linux.
+ *
+ */
+
+#include "../lowlevel/ftape-read.h"
+
+/* ftape-read.c defined global vars.
+ */
+extern int zft_just_before_eof;
+
+/* ftape-read.c defined global functions.
+ */
+extern void zft_zap_read_buffers(void);
+extern int zft_read_header_segments(void);
+extern int zft_fetch_segment_fraction(const unsigned int segment,
+ void *buffer,
+ const ft_read_mode_t read_mode,
+ const unsigned int start,
+ const unsigned int size);
+#define zft_fetch_segment(segment, address, read_mode) \
+ zft_fetch_segment_fraction(segment, address, read_mode, \
+ 0, FT_SEGMENT_SIZE)
+/* hook for the VFS interface
+ */
+extern int _zft_read(char* buff, int req_len);
+
+#endif /* _ZFTAPE_READ_H */
diff --git a/drivers/char/ftape/zftape/zftape-rw.c b/drivers/char/ftape/zftape/zftape-rw.c
new file mode 100644
index 000000000..d8ae9f1e7
--- /dev/null
+++ b/drivers/char/ftape/zftape/zftape-rw.c
@@ -0,0 +1,377 @@
+/*
+ * Copyright (C) 1996, 1997 Claus-Justus Heine
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-rw.c,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:19:08 $
+ *
+ * This file contains some common code for the r/w code for
+ * zftape.
+ */
+
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <asm/segment.h>
+
+#include <linux/zftape.h>
+#include "../zftape/zftape-init.h"
+#include "../zftape/zftape-eof.h"
+#include "../zftape/zftape-ctl.h"
+#include "../zftape/zftape-write.h"
+#include "../zftape/zftape-read.h"
+#include "../zftape/zftape-rw.h"
+#include "../zftape/zftape-vtbl.h"
+
+/* Global vars.
+ */
+
+__u8 *zft_deblock_buf = NULL;
+__u8 *zft_hseg_buf = NULL;
+int zft_deblock_segment = -1;
+zft_status_enum zft_io_state = zft_idle;
+int zft_header_changed = 0;
+int zft_bad_sector_map_changed = 0;
+int zft_qic113 = 0; /* conform to old specs. and old zftape */
+int zft_use_compression = 0;
+zft_position zft_pos = {
+ -1, /* seg_pos */
+ 0, /* seg_byte_pos */
+ 0, /* tape_pos */
+ 0 /* volume_pos */
+};
+unsigned int zft_blk_sz = CONFIG_ZFT_DFLT_BLK_SZ;
+__s64 zft_capacity = 0;
+
+unsigned int zft_written_segments = 0;
+int zft_label_changed = 0;
+
+/* Local vars.
+ */
+
+unsigned int zft_get_seg_sz(unsigned int segment)
+{
+ int size;
+ TRACE_FUN(ft_t_any);
+
+ size = FT_SEGMENT_SIZE -
+ count_ones(ftape_get_bad_sector_entry(segment))*FT_SECTOR_SIZE;
+ if (size > 0) {
+ TRACE_EXIT (unsigned)size;
+ } else {
+ TRACE_EXIT 0;
+ }
+}
+
+/* ftape_set_flags(). Claus-Justus Heine, 1994/1995
+ */
+void zft_set_flags(unsigned minor_unit)
+{
+ TRACE_FUN(ft_t_flow);
+
+ zft_use_compression = zft_qic_mode = 0;
+ switch (minor_unit & ZFT_MINOR_OP_MASK) {
+ case (ZFT_Q80_MODE | ZFT_ZIP_MODE):
+ case ZFT_ZIP_MODE:
+ zft_use_compression = 1;
+ case 0:
+ case ZFT_Q80_MODE:
+ zft_qic_mode = 1;
+ if (zft_mt_compression) { /* override the default */
+ zft_use_compression = 1;
+ }
+ break;
+ case ZFT_RAW_MODE:
+ TRACE(ft_t_noise, "switching to raw mode");
+ break;
+ default:
+ TRACE(ft_t_warn, "Warning:\n"
+ KERN_INFO "Wrong combination of minor device bits.\n"
+ KERN_INFO "Switching to raw read-only mode.");
+ zft_write_protected = 1;
+ break;
+ }
+ TRACE_EXIT;
+}
+
+/* computes the segment and byte offset inside the segment
+ * corresponding to tape_pos.
+ *
+ * tape_pos gives the offset in bytes from the beginning of the
+ * ft_first_data_segment *seg_byte_pos is the offset in the current
+ * segment in bytes
+ *
+ * Of, if this routine was called often one should cache the last data
+ * pos it was called with, but actually this is only needed in
+ * ftape_seek_block(), that is, almost never.
+ */
+int zft_calc_seg_byte_coord(int *seg_byte_pos, __s64 tape_pos)
+{
+ int segment;
+ int seg_sz;
+ TRACE_FUN(ft_t_flow);
+
+ if (tape_pos == 0) {
+ *seg_byte_pos = 0;
+ segment = ft_first_data_segment;
+ } else {
+ seg_sz = 0;
+
+ for (segment = ft_first_data_segment;
+ ((tape_pos > 0) && (segment <= ft_last_data_segment));
+ segment++) {
+ seg_sz = zft_get_seg_sz(segment);
+ tape_pos -= seg_sz;
+ }
+ if(tape_pos >= 0) {
+ /* the case tape_pos > != 0 means that the
+ * argument tape_pos lies beyond the EOT.
+ */
+ *seg_byte_pos= 0;
+ } else { /* tape_pos < 0 */
+ segment--;
+ *seg_byte_pos= tape_pos + seg_sz;
+ }
+ }
+ TRACE_EXIT(segment);
+}
+
+/* ftape_calc_tape_pos().
+ *
+ * computes the offset in bytes from the beginning of the
+ * ft_first_data_segment inverse to ftape_calc_seg_byte_coord
+ *
+ * We should do some caching. But how:
+ *
+ * Each time the header segments are read in, this routine is called
+ * with ft_tracks_per_tape*segments_per_track argumnet. So this should be
+ * the time to reset the cache.
+ *
+ * Also, it might be in the future that the bad sector map gets
+ * changed. -> reset the cache
+ */
+static int seg_pos = 0;
+static __s64 tape_pos = 0;
+
+__s64 zft_get_capacity(void)
+{
+ seg_pos = ft_first_data_segment;
+ tape_pos = 0;
+
+ while (seg_pos <= ft_last_data_segment) {
+ tape_pos += zft_get_seg_sz(seg_pos ++);
+ }
+ return tape_pos;
+}
+
+__s64 zft_calc_tape_pos(int segment)
+{
+ int d1, d2, d3;
+ TRACE_FUN(ft_t_any);
+
+ if (segment > ft_last_data_segment) {
+ TRACE_EXIT zft_capacity;
+ }
+ if (segment < ft_first_data_segment) {
+ TRACE_EXIT 0;
+ }
+ d2 = segment - seg_pos;
+ if (-d2 > 10) {
+ d1 = segment - ft_first_data_segment;
+ if (-d2 > d1) {
+ tape_pos = 0;
+ seg_pos = ft_first_data_segment;
+ d2 = d1;
+ }
+ }
+ if (d2 > 10) {
+ d3 = ft_last_data_segment - segment;
+ if (d2 > d3) {
+ tape_pos = zft_capacity;
+ seg_pos = ft_last_data_segment + 1;
+ d2 = -d3;
+ }
+ }
+ if (d2 > 0) {
+ while (seg_pos < segment) {
+ tape_pos += zft_get_seg_sz(seg_pos++);
+ }
+ } else {
+ while (seg_pos > segment) {
+ tape_pos -= zft_get_seg_sz(--seg_pos);
+ }
+ }
+ TRACE(ft_t_noise, "new cached pos: %d", seg_pos);
+
+ TRACE_EXIT tape_pos;
+}
+
+/* copy Z-label string to buffer, keeps track of the correct offset in
+ * `buffer'
+ */
+void zft_update_label(__u8 *buffer)
+{
+ TRACE_FUN(ft_t_flow);
+
+ if (strncmp(&buffer[FT_LABEL], ZFTAPE_LABEL,
+ sizeof(ZFTAPE_LABEL)-1) != 0) {
+ TRACE(ft_t_info, "updating label from \"%s\" to \"%s\"",
+ &buffer[FT_LABEL], ZFTAPE_LABEL);
+ strcpy(&buffer[FT_LABEL], ZFTAPE_LABEL);
+ memset(&buffer[FT_LABEL] + sizeof(ZFTAPE_LABEL) - 1, ' ',
+ FT_LABEL_SZ - sizeof(ZFTAPE_LABEL + 1));
+ PUT4(buffer, FT_LABEL_DATE, 0);
+ zft_label_changed = zft_header_changed = 1; /* changed */
+ }
+ TRACE_EXIT;
+}
+
+int zft_verify_write_segments(unsigned int segment,
+ __u8 *data, size_t size,
+ __u8 *buffer)
+{
+ int result;
+ __u8 *write_buf;
+ __u8 *src_buf;
+ int single;
+ int seg_pos;
+ int seg_sz;
+ int remaining;
+ ft_write_mode_t write_mode;
+ TRACE_FUN(ft_t_flow);
+
+ seg_pos = segment;
+ seg_sz = zft_get_seg_sz(seg_pos);
+ src_buf = data;
+ single = size <= seg_sz;
+ remaining = size;
+ do {
+ TRACE(ft_t_noise, "\n"
+ KERN_INFO "remaining: %d\n"
+ KERN_INFO "seg_sz : %d\n"
+ KERN_INFO "segment : %d",
+ remaining, seg_sz, seg_pos);
+ if (remaining == seg_sz) {
+ write_buf = src_buf;
+ write_mode = single ? FT_WR_SINGLE : FT_WR_MULTI;
+ remaining = 0;
+ } else if (remaining > seg_sz) {
+ write_buf = src_buf;
+ write_mode = FT_WR_ASYNC; /* don't start tape */
+ remaining -= seg_sz;
+ } else { /* remaining < seg_sz */
+ write_buf = buffer;
+ memcpy(write_buf, src_buf, remaining);
+ memset(&write_buf[remaining],'\0',seg_sz-remaining);
+ write_mode = single ? FT_WR_SINGLE : FT_WR_MULTI;
+ remaining = 0;
+ }
+ if ((result = ftape_write_segment(seg_pos,
+ write_buf,
+ write_mode)) != seg_sz) {
+ TRACE(ft_t_err, "Error: "
+ "Couldn't write segment %d", seg_pos);
+ TRACE_EXIT result < 0 ? result : -EIO; /* bail out */
+ }
+ zft_written_segments ++;
+ seg_sz = zft_get_seg_sz(++seg_pos);
+ src_buf += result;
+ } while (remaining > 0);
+ if (ftape_get_status()->fti_state == writing) {
+ TRACE_CATCH(ftape_loop_until_writes_done(),);
+ TRACE_CATCH(ftape_abort_operation(),);
+ zft_prevent_flush();
+ }
+ seg_pos = segment;
+ src_buf = data;
+ remaining = size;
+ do {
+ TRACE_CATCH(result = ftape_read_segment(seg_pos, buffer,
+ single ? FT_RD_SINGLE
+ : FT_RD_AHEAD),);
+ if (memcmp(src_buf, buffer,
+ remaining > result ? result : remaining) != 0) {
+ TRACE_ABORT(-EIO, ft_t_err,
+ "Failed to verify written segment %d",
+ seg_pos);
+ }
+ remaining -= result;
+ TRACE(ft_t_noise, "verify successful:\n"
+ KERN_INFO "segment : %d\n"
+ KERN_INFO "segsize : %d\n"
+ KERN_INFO "remaining: %d",
+ seg_pos, result, remaining);
+ src_buf += seg_sz;
+ seg_pos++;
+ } while (remaining > 0);
+ TRACE_EXIT size;
+}
+
+
+/* zft_erase(). implemented compression-handling
+ *
+ * calculate the first data-segment when using/not using compression.
+ *
+ * update header-segment and compression-map-segment.
+ */
+int zft_erase(void)
+{
+ int result = 0;
+ TRACE_FUN(ft_t_flow);
+
+ if (!zft_header_read) {
+ TRACE_CATCH(zft_vmalloc_once((void **)&zft_hseg_buf,
+ FT_SEGMENT_SIZE),);
+ /* no need to read the vtbl and compression map */
+ TRACE_CATCH(ftape_read_header_segment(zft_hseg_buf),);
+ if ((zft_old_ftape =
+ zft_ftape_validate_label(&zft_hseg_buf[FT_LABEL]))) {
+ zft_ftape_extract_file_marks(zft_hseg_buf);
+ }
+ TRACE(ft_t_noise,
+ "ft_first_data_segment: %d, ft_last_data_segment: %d",
+ ft_first_data_segment, ft_last_data_segment);
+ zft_qic113 = (ft_format_code != fmt_normal &&
+ ft_format_code != fmt_1100ft &&
+ ft_format_code != fmt_425ft);
+ }
+ if (zft_old_ftape) {
+ zft_clear_ftape_file_marks();
+ zft_old_ftape = 0; /* no longer old ftape */
+ }
+ PUT2(zft_hseg_buf, FT_CMAP_START, 0);
+ zft_volume_table_changed = 1;
+ zft_capacity = zft_get_capacity();
+ zft_init_vtbl();
+ /* the rest must be done in ftape_update_header_segments
+ */
+ zft_header_read = 1;
+ zft_header_changed = 1; /* force update of timestamp */
+ result = zft_update_header_segments();
+
+ ftape_abort_operation();
+
+ zft_reset_position(&zft_pos);
+ zft_set_flags (zft_unit);
+ TRACE_EXIT result;
+}
+
+unsigned int zft_get_time(void)
+{
+ unsigned int date = FT_TIME_STAMP(2097, 11, 30, 23, 59, 59); /* fun */
+ return date;
+}
diff --git a/drivers/char/ftape/zftape/zftape-rw.h b/drivers/char/ftape/zftape/zftape-rw.h
new file mode 100644
index 000000000..a8622867b
--- /dev/null
+++ b/drivers/char/ftape/zftape/zftape-rw.h
@@ -0,0 +1,102 @@
+#ifndef _ZFTAPE_RW_H
+#define _ZFTAPE_RW_H
+
+/*
+ * Copyright (C) 1996, 1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-rw.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:19:09 $
+ *
+ * This file contains the definitions for the read and write
+ * functions for the QIC-117 floppy-tape driver for Linux.
+ *
+ */
+
+#include "../zftape/zftape-buffers.h"
+
+#define SEGMENTS_PER_TAPE (ft_segments_per_track * ft_tracks_per_tape)
+
+/* QIC-113 Rev. G says that `a maximum of 63488 raw bytes may be
+ * compressed into a single frame'.
+ * Maybe we should stick to 32kb to make it more `beautiful'
+ */
+#define ZFT_MAX_BLK_SZ (62*1024) /* bytes */
+#if !defined(CONFIG_ZFT_DFLT_BLK_SZ)
+# define CONFIG_ZFT_DFLT_BLK_SZ (10*1024) /* bytes, default of gnu tar */
+#elif CONFIG_ZFT_DFLT_BLK_SZ == 0
+# undef CONFIG_ZFT_DFLT_BLK_SZ
+# define CONFIG_ZFT_DFLT_BLK_SZ 1
+#elif (CONFIG_ZFT_DFLT_BLK_SZ % 1024) != 0
+# error CONFIG_ZFT_DFLT_BLK_SZ must be 1 or a multiple of 1024
+#endif
+/* The *optional* compression routines need some overhead per tape
+ * block for their purposes. Instead of asking the actual compression
+ * implementation how much it needs, we restrict this overhead to be
+ * maximal of ZFT_CMPT_OVERHEAD size. We need this for EOT
+ * conditions. The tape is assumed to be logical at EOT when the
+ * distance from the physical EOT is less than
+ * one tape block + ZFT_CMPR_OVERHEAD
+ */
+#define ZFT_CMPR_OVERHEAD 16 /* bytes */
+
+typedef enum
+{
+ zft_idle = 0,
+ zft_reading,
+ zft_writing,
+} zft_status_enum;
+
+typedef struct /* all values measured in bytes */
+{
+ int seg_pos; /* segment currently positioned at */
+ int seg_byte_pos; /* offset in current segment */
+ __s64 tape_pos; /* real offset from BOT */
+ __s64 volume_pos; /* pos. in uncompressed data stream in
+ * current volume
+ */
+} zft_position;
+
+extern zft_position zft_pos;
+extern __u8 *zft_deblock_buf;
+extern __u8 *zft_hseg_buf;
+extern int zft_deblock_segment;
+extern zft_status_enum zft_io_state;
+extern int zft_header_changed;
+extern int zft_bad_sector_map_changed;
+extern int zft_qic113; /* conform to old specs. and old zftape */
+extern int zft_use_compression;
+extern unsigned int zft_blk_sz;
+extern __s64 zft_capacity;
+extern unsigned int zft_written_segments;
+extern int zft_label_changed;
+
+/* zftape-rw.c exported functions
+ */
+extern unsigned int zft_get_seg_sz(unsigned int segment);
+extern void zft_set_flags(unsigned int minor_unit);
+extern int zft_calc_seg_byte_coord(int *seg_byte_pos, __s64 tape_pos);
+extern __s64 zft_calc_tape_pos(int segment);
+extern __s64 zft_get_capacity(void);
+extern void zft_update_label(__u8 *buffer);
+extern int zft_erase(void);
+extern int zft_verify_write_segments(unsigned int segment,
+ __u8 *data, size_t size, __u8 *buffer);
+extern unsigned int zft_get_time(void);
+#endif /* _ZFTAPE_RW_H */
+
diff --git a/drivers/char/ftape/zftape/zftape-vtbl.c b/drivers/char/ftape/zftape/zftape-vtbl.c
new file mode 100644
index 000000000..cf0ef2325
--- /dev/null
+++ b/drivers/char/ftape/zftape/zftape-vtbl.c
@@ -0,0 +1,758 @@
+/*
+ * Copyright (c) 1995-1997 Claus-Justus Heine
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139,
+ USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-vtbl.c,v $
+ * $Revision: 1.7.6.1 $
+ * $Date: 1997/11/24 13:48:31 $
+ *
+ * This file defines a volume table as defined in various QIC
+ * standards.
+ *
+ * This is a minimal implementation, just allowing ordinary DOS
+ * :( prgrams to identify the cartridge as used.
+ */
+
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <asm/segment.h>
+
+#include <linux/zftape.h>
+#include "../zftape/zftape-init.h"
+#include "../zftape/zftape-eof.h"
+#include "../zftape/zftape-ctl.h"
+#include "../zftape/zftape-write.h"
+#include "../zftape/zftape-read.h"
+#include "../zftape/zftape-rw.h"
+#include "../zftape/zftape-vtbl.h"
+
+#define ZFT_CMAP_HACK /* leave this defined to hide the compression map */
+
+/*
+ * global variables
+ */
+int zft_qic_mode = 1; /* use the vtbl */
+int zft_old_ftape = 0; /* prevents old ftaped tapes to be overwritten */
+int zft_volume_table_changed = 0; /* for write_header_segments() */
+
+/*
+ * private variables (only exported for inline functions)
+ */
+LIST_HEAD(zft_vtbl);
+
+/* We could also allocate these dynamically when extracting the volume table
+ * sizeof(zft_volinfo) is about 32 or something close to that
+ */
+static zft_volinfo tape_vtbl = { {NULL, NULL}, 0, };
+static zft_volinfo eot_vtbl = { {NULL, NULL}, 0, };
+static zft_volinfo *cur_vtbl = NULL;
+
+inline void zft_new_vtbl_entry(void)
+{
+ struct list_head *tmp = &zft_last_vtbl->node;
+ zft_volinfo *new = zft_kmalloc(sizeof(zft_volinfo));
+
+ list_add(&new->node, tmp);
+ new->count = zft_eom_vtbl->count ++;
+}
+
+void zft_free_vtbl(void)
+{
+ for (;;) {
+ struct list_head *tmp = zft_vtbl.prev;
+ zft_volinfo *vtbl;
+
+ if (tmp == &zft_vtbl)
+ break;
+ list_del(tmp);
+ vtbl = list_entry(tmp, zft_volinfo, node);
+ zft_kfree(vtbl, sizeof(zft_volinfo));
+ }
+ INIT_LIST_HEAD(&zft_vtbl);
+ cur_vtbl = NULL;
+}
+
+/* initialize vtbl, called by ftape_new_cartridge()
+ */
+void zft_init_vtbl(void)
+{
+ zft_volinfo *new;
+
+ zft_free_vtbl();
+
+ /* Create the two dummy vtbl entries
+ */
+ new = zft_kmalloc(sizeof(zft_volinfo));
+ list_add(&new->node, &zft_vtbl);
+ new = zft_kmalloc(sizeof(zft_volinfo));
+ list_add(&new->node, &zft_vtbl);
+ zft_head_vtbl->end_seg = ft_first_data_segment;
+ zft_head_vtbl->blk_sz = zft_blk_sz;
+ zft_head_vtbl->count = -1;
+ zft_eom_vtbl->start_seg = ft_first_data_segment + 1;
+ zft_eom_vtbl->end_seg = ft_last_data_segment + 1;
+ zft_eom_vtbl->blk_sz = zft_blk_sz;
+ zft_eom_vtbl->count = 0;
+
+ /* Reset the pointer for zft_find_volume()
+ */
+ cur_vtbl = zft_eom_vtbl;
+
+ /* initialize the dummy vtbl entries for zft_qic_mode == 0
+ */
+ eot_vtbl.start_seg = ft_last_data_segment + 1;
+ eot_vtbl.end_seg = ft_last_data_segment + 1;
+ eot_vtbl.blk_sz = zft_blk_sz;
+ eot_vtbl.count = -1;
+ tape_vtbl.start_seg = ft_first_data_segment;
+ tape_vtbl.end_seg = ft_last_data_segment;
+ tape_vtbl.blk_sz = zft_blk_sz;
+ tape_vtbl.size = zft_capacity;
+ tape_vtbl.count = 0;
+}
+
+/* check for a valid VTBL signature.
+ */
+static int vtbl_signature_valid(__u8 signature[4])
+{
+ const char *vtbl_ids[] = VTBL_IDS; /* valid signatures */
+ int j;
+
+ for (j = 0;
+ (j < NR_ITEMS(vtbl_ids)) && (memcmp(signature, vtbl_ids[j], 4) != 0);
+ j++);
+ return j < NR_ITEMS(vtbl_ids);
+}
+
+/* We used to store the block-size of the volume in the volume-label,
+ * using the keyword "blocksize". The blocksize written to the
+ * volume-label is in bytes.
+ *
+ * We use this now only for compatability with old zftape version. We
+ * store the blocksize directly as binary number in the vendor
+ * extension part of the volume entry.
+ */
+static int check_volume_label(const char *label, int *blk_sz)
+{
+ int valid_format;
+ char *blocksize;
+ TRACE_FUN(ft_t_flow);
+
+ TRACE(ft_t_noise, "called with \"%s\" / \"%s\"", label, ZFT_VOL_NAME);
+ if (strncmp(label, ZFT_VOL_NAME, strlen(ZFT_VOL_NAME)) != 0) {
+ *blk_sz = 1; /* smallest block size that we allow */
+ valid_format = 0;
+ } else {
+ TRACE(ft_t_noise, "got old style zftape vtbl entry");
+ /* get the default blocksize */
+ /* use the kernel strstr() */
+ blocksize= strstr(label, " blocksize ");
+ if (blocksize) {
+ blocksize += strlen(" blocksize ");
+ for(*blk_sz= 0;
+ *blocksize >= '0' && *blocksize <= '9';
+ blocksize++) {
+ *blk_sz *= 10;
+ *blk_sz += *blocksize - '0';
+ }
+ if (*blk_sz > ZFT_MAX_BLK_SZ) {
+ *blk_sz= 1;
+ valid_format= 0;
+ } else {
+ valid_format = 1;
+ }
+ } else {
+ *blk_sz= 1;
+ valid_format= 0;
+ }
+ }
+ TRACE_EXIT valid_format;
+}
+
+/* check for a zftape volume
+ */
+static int check_volume(__u8 *entry, zft_volinfo *volume)
+{
+ TRACE_FUN(ft_t_flow);
+
+ if(strncmp(&entry[VTBL_EXT+EXT_ZFTAPE_SIG], ZFTAPE_SIG,
+ strlen(ZFTAPE_SIG)) == 0) {
+ TRACE(ft_t_noise, "got new style zftape vtbl entry");
+ volume->blk_sz = GET2(entry, VTBL_EXT+EXT_ZFTAPE_BLKSZ);
+ volume->qic113 = entry[VTBL_EXT+EXT_ZFTAPE_QIC113];
+ TRACE_EXIT 1;
+ } else {
+ TRACE_EXIT check_volume_label(&entry[VTBL_DESC], &volume->blk_sz);
+ }
+}
+
+
+/* create zftape specific vtbl entry, the volume bounds are inserted
+ * in the calling function, zft_create_volume_headers()
+ */
+static void create_zft_volume(__u8 *entry, zft_volinfo *vtbl)
+{
+ TRACE_FUN(ft_t_flow);
+
+ memset(entry, 0, VTBL_SIZE);
+ memcpy(&entry[VTBL_SIG], VTBL_ID, 4);
+ sprintf(&entry[VTBL_DESC], ZFT_VOL_NAME" %03d", vtbl->count);
+ entry[VTBL_FLAGS] = (VTBL_FL_NOT_VERIFIED | VTBL_FL_SEG_SPANNING);
+ entry[VTBL_M_NO] = 1; /* multi_cartridge_count */
+ strcpy(&entry[VTBL_EXT+EXT_ZFTAPE_SIG], ZFTAPE_SIG);
+ PUT2(entry, VTBL_EXT+EXT_ZFTAPE_BLKSZ, vtbl->blk_sz);
+ if (zft_qic113) {
+ PUT8(entry, VTBL_DATA_SIZE, vtbl->size);
+ entry[VTBL_CMPR] = VTBL_CMPR_UNREG;
+ if (vtbl->use_compression) { /* use compression: */
+ entry[VTBL_CMPR] |= VTBL_CMPR_USED;
+ }
+ entry[VTBL_EXT+EXT_ZFTAPE_QIC113] = 1;
+ } else {
+ PUT4(entry, VTBL_DATA_SIZE, vtbl->size);
+ entry[VTBL_K_CMPR] = VTBL_CMPR_UNREG;
+ if (vtbl->use_compression) { /* use compression: */
+ entry[VTBL_K_CMPR] |= VTBL_CMPR_USED;
+ }
+ }
+ if (ft_format_code == fmt_big) {
+ /* SCSI like vtbl, store the number of used
+ * segments as 4 byte value
+ */
+ PUT4(entry, VTBL_SCSI_SEGS, vtbl->end_seg-vtbl->start_seg + 1);
+ } else {
+ /* normal, QIC-80MC like vtbl
+ */
+ PUT2(entry, VTBL_START, vtbl->start_seg);
+ PUT2(entry, VTBL_END, vtbl->end_seg);
+ }
+ TRACE_EXIT;
+}
+
+/* this one creates the volume headers for each volume. It is assumed
+ * that buffer already contains the old volume-table, so that vtbl
+ * entries without the zft_volume flag set can savely be ignored.
+ */
+void zft_create_volume_headers(__u8 *buffer)
+{
+ __u8 *entry;
+ struct list_head *tmp;
+ zft_volinfo *vtbl;
+ TRACE_FUN(ft_t_flow);
+
+#ifdef ZFT_CMAP_HACK
+ if((strncmp(&buffer[VTBL_EXT+EXT_ZFTAPE_SIG], ZFTAPE_SIG,
+ strlen(ZFTAPE_SIG)) == 0) &&
+ buffer[VTBL_EXT+EXT_ZFTAPE_CMAP] != 0) {
+ TRACE(ft_t_noise, "deleting cmap volume");
+ memmove(buffer, buffer + VTBL_SIZE,
+ FT_SEGMENT_SIZE - VTBL_SIZE);
+ }
+#endif
+ entry = buffer;
+ for (tmp = zft_head_vtbl->node.next;
+ tmp != &zft_eom_vtbl->node;
+ tmp = tmp->next) {
+ vtbl = list_entry(tmp, zft_volinfo, node);
+ /* we now fill in the values only for newly created volumes.
+ */
+ if (vtbl->new_volume) {
+ create_zft_volume(entry, vtbl);
+ vtbl->new_volume = 0; /* clear the flag */
+ }
+
+ DUMP_VOLINFO(ft_t_noise, &entry[VTBL_DESC], vtbl);
+ entry += VTBL_SIZE;
+ }
+ memset(entry, 0, FT_SEGMENT_SIZE - zft_eom_vtbl->count * VTBL_SIZE);
+ TRACE_EXIT;
+}
+
+/* write volume table to tape. Calls zft_create_volume_headers()
+ */
+int zft_update_volume_table(unsigned int segment)
+{
+ int result = 0;
+ __u8 *verify_buf = NULL;
+ TRACE_FUN(ft_t_flow);
+
+ TRACE_CATCH(result = ftape_read_segment(ft_first_data_segment,
+ zft_deblock_buf,
+ FT_RD_SINGLE),);
+ zft_create_volume_headers(zft_deblock_buf);
+ TRACE(ft_t_noise, "writing volume table segment %d", segment);
+ if (zft_vmalloc_once(&verify_buf, FT_SEGMENT_SIZE) == 0) {
+ TRACE_CATCH(zft_verify_write_segments(segment,
+ zft_deblock_buf, result,
+ verify_buf),
+ zft_vfree(&verify_buf, FT_SEGMENT_SIZE));
+ zft_vfree(&verify_buf, FT_SEGMENT_SIZE);
+ } else {
+ TRACE_CATCH(ftape_write_segment(segment, zft_deblock_buf,
+ FT_WR_SINGLE),);
+ }
+ TRACE_EXIT 0;
+}
+
+/* non zftape volumes are handled in raw mode. Thus we need to
+ * calculate the raw amount of data contained in those segments.
+ */
+static void extract_alien_volume(__u8 *entry, zft_volinfo *vtbl)
+{
+ TRACE_FUN(ft_t_flow);
+
+ vtbl->size = (zft_calc_tape_pos(zft_last_vtbl->end_seg+1) -
+ zft_calc_tape_pos(zft_last_vtbl->start_seg));
+ vtbl->use_compression = 0;
+ vtbl->qic113 = zft_qic113;
+ if (vtbl->qic113) {
+ TRACE(ft_t_noise,
+ "Fake alien volume's size from " LL_X " to " LL_X,
+ LL(GET8(entry, VTBL_DATA_SIZE)), LL(vtbl->size));
+ } else {
+ TRACE(ft_t_noise,
+ "Fake alien volume's size from %d to " LL_X,
+ (int)GET4(entry, VTBL_DATA_SIZE), LL(vtbl->size));
+ }
+ TRACE_EXIT;
+}
+
+
+/* extract an zftape specific volume
+ */
+static void extract_zft_volume(__u8 *entry, zft_volinfo *vtbl)
+{
+ TRACE_FUN(ft_t_flow);
+
+ if (vtbl->qic113) {
+ vtbl->size = GET8(entry, VTBL_DATA_SIZE);
+ vtbl->use_compression =
+ (entry[VTBL_CMPR] & VTBL_CMPR_USED) != 0;
+ } else {
+ vtbl->size = GET4(entry, VTBL_DATA_SIZE);
+ if (entry[VTBL_K_CMPR] & VTBL_CMPR_UNREG) {
+ vtbl->use_compression =
+ (entry[VTBL_K_CMPR] & VTBL_CMPR_USED) != 0;
+ } else if (entry[VTBL_CMPR] & VTBL_CMPR_UNREG) {
+ vtbl->use_compression =
+ (entry[VTBL_CMPR] & VTBL_CMPR_USED) != 0;
+ } else {
+ TRACE(ft_t_warn, "Geeh! There is something wrong:\n"
+ KERN_INFO "QIC compression (Rev = K): %x\n"
+ KERN_INFO "QIC compression (Rev > K): %x",
+ entry[VTBL_K_CMPR], entry[VTBL_CMPR]);
+ }
+ }
+ TRACE_EXIT;
+}
+
+/* extract the volume table from buffer. "buffer" must already contain
+ * the vtbl-segment
+ */
+int zft_extract_volume_headers(__u8 *buffer)
+{
+ __u8 *entry;
+ TRACE_FUN(ft_t_flow);
+
+ zft_init_vtbl();
+ entry = buffer;
+#ifdef ZFT_CMAP_HACK
+ if ((strncmp(&entry[VTBL_EXT+EXT_ZFTAPE_SIG], ZFTAPE_SIG,
+ strlen(ZFTAPE_SIG)) == 0) &&
+ entry[VTBL_EXT+EXT_ZFTAPE_CMAP] != 0) {
+ TRACE(ft_t_noise, "ignoring cmap volume");
+ entry += VTBL_SIZE;
+ }
+#endif
+ /* the end of the vtbl is indicated by an invalid signature
+ */
+ while (vtbl_signature_valid(&entry[VTBL_SIG]) &&
+ (entry - buffer) < FT_SEGMENT_SIZE) {
+ zft_new_vtbl_entry();
+ if (ft_format_code == fmt_big) {
+ /* SCSI like vtbl, stores only the number of
+ * segments used
+ */
+ unsigned int num_segments= GET4(entry, VTBL_SCSI_SEGS);
+ zft_last_vtbl->start_seg = zft_eom_vtbl->start_seg;
+ zft_last_vtbl->end_seg =
+ zft_last_vtbl->start_seg + num_segments - 1;
+ } else {
+ /* `normal', QIC-80 like vtbl
+ */
+ zft_last_vtbl->start_seg = GET2(entry, VTBL_START);
+ zft_last_vtbl->end_seg = GET2(entry, VTBL_END);
+ }
+ zft_eom_vtbl->start_seg = zft_last_vtbl->end_seg + 1;
+ /* check if we created this volume and get the
+ * blk_sz
+ */
+ zft_last_vtbl->zft_volume = check_volume(entry, zft_last_vtbl);
+ if (zft_last_vtbl->zft_volume == 0) {
+ extract_alien_volume(entry, zft_last_vtbl);
+ } else {
+ extract_zft_volume(entry, zft_last_vtbl);
+ }
+ DUMP_VOLINFO(ft_t_noise, &entry[VTBL_DESC], zft_last_vtbl);
+ entry +=VTBL_SIZE;
+ }
+#if 0
+/*
+ * undefine to test end of tape handling
+ */
+ zft_new_vtbl_entry();
+ zft_last_vtbl->start_seg = zft_eom_vtbl->start_seg;
+ zft_last_vtbl->end_seg = ft_last_data_segment - 10;
+ zft_last_vtbl->blk_sz = zft_blk_sz;
+ zft_last_vtbl->zft_volume = 1;
+ zft_last_vtbl->qic113 = zft_qic113;
+ zft_last_vtbl->size = (zft_calc_tape_pos(zft_last_vtbl->end_seg+1)
+ - zft_calc_tape_pos(zft_last_vtbl->start_seg));
+#endif
+ TRACE_EXIT 0;
+}
+
+/* this functions translates the failed_sector_log, misused as
+ * EOF-marker list, into a virtual volume table. The table mustn't be
+ * written to tape, because this would occupy the first data segment,
+ * which should be the volume table, but is actualy the first segment
+ * that is filled with data (when using standard ftape). We assume,
+ * that we get a non-empty failed_sector_log.
+ */
+int zft_fake_volume_headers (eof_mark_union *eof_map, int num_failed_sectors)
+{
+ unsigned int segment, sector;
+ int have_eom = 0;
+ int vol_no;
+ TRACE_FUN(ft_t_flow);
+
+ if ((num_failed_sectors >= 2) &&
+ (GET2(&eof_map[num_failed_sectors - 1].mark.segment, 0)
+ ==
+ GET2(&eof_map[num_failed_sectors - 2].mark.segment, 0) + 1) &&
+ (GET2(&eof_map[num_failed_sectors - 1].mark.date, 0) == 1)) {
+ /* this should be eom. We keep the remainder of the
+ * tape as another volume.
+ */
+ have_eom = 1;
+ }
+ zft_init_vtbl();
+ zft_eom_vtbl->start_seg = ft_first_data_segment;
+ for(vol_no = 0; vol_no < num_failed_sectors - have_eom; vol_no ++) {
+ zft_new_vtbl_entry();
+
+ segment = GET2(&eof_map[vol_no].mark.segment, 0);
+ sector = GET2(&eof_map[vol_no].mark.date, 0);
+
+ zft_last_vtbl->start_seg = zft_eom_vtbl->start_seg;
+ zft_last_vtbl->end_seg = segment;
+ zft_eom_vtbl->start_seg = segment + 1;
+ zft_last_vtbl->blk_sz = 1;
+ zft_last_vtbl->size =
+ (zft_calc_tape_pos(zft_last_vtbl->end_seg)
+ - zft_calc_tape_pos(zft_last_vtbl->start_seg)
+ + (sector-1) * FT_SECTOR_SIZE);
+ TRACE(ft_t_noise,
+ "failed sector log: segment: %d, sector: %d",
+ segment, sector);
+ DUMP_VOLINFO(ft_t_noise, "Faked volume", zft_last_vtbl);
+ }
+ if (!have_eom) {
+ zft_new_vtbl_entry();
+ zft_last_vtbl->start_seg = zft_eom_vtbl->start_seg;
+ zft_last_vtbl->end_seg = ft_last_data_segment;
+ zft_eom_vtbl->start_seg = ft_last_data_segment + 1;
+ zft_last_vtbl->size = zft_capacity;
+ zft_last_vtbl->size -= zft_calc_tape_pos(zft_last_vtbl->start_seg);
+ zft_last_vtbl->blk_sz = 1;
+ DUMP_VOLINFO(ft_t_noise, "Faked volume",zft_last_vtbl);
+ }
+ TRACE_EXIT 0;
+}
+
+/* update the internal volume table
+ *
+ * if before start of last volume: erase all following volumes if
+ * inside a volume: set end of volume to infinity
+ *
+ * this function is intended to be called every time _ftape_write() is
+ * called
+ *
+ * return: 0 if no new volume was created, 1 if a new volume was
+ * created
+ *
+ * NOTE: we don't need to check for zft_mode as ftape_write() does
+ * that already. This function gets never called without accessing
+ * zftape via the *qft* devices
+ */
+
+int zft_open_volume(zft_position *pos, int blk_sz, int use_compression)
+{
+ TRACE_FUN(ft_t_flow);
+
+ if (!zft_qic_mode) {
+ TRACE_EXIT 0;
+ }
+ if (zft_tape_at_lbot(pos)) {
+ zft_init_vtbl();
+ if(zft_old_ftape) {
+ /* clear old ftape's eof marks */
+ zft_clear_ftape_file_marks();
+ zft_old_ftape = 0; /* no longer old ftape */
+ }
+ zft_reset_position(pos);
+ }
+ if (pos->seg_pos != zft_last_vtbl->end_seg + 1) {
+ TRACE_ABORT(-EIO, ft_t_bug,
+ "BUG: seg_pos: %d, zft_last_vtbl->end_seg: %d",
+ pos->seg_pos, zft_last_vtbl->end_seg);
+ }
+ TRACE(ft_t_noise, "create new volume");
+ if (zft_eom_vtbl->count >= ZFT_MAX_VOLUMES) {
+ TRACE_ABORT(-ENOSPC, ft_t_err,
+ "Error: maxmimal number of volumes exhausted "
+ "(maxmimum is %d)", ZFT_MAX_VOLUMES);
+ }
+ zft_new_vtbl_entry();
+ pos->volume_pos = pos->seg_byte_pos = 0;
+ zft_last_vtbl->start_seg = pos->seg_pos;
+ zft_last_vtbl->end_seg = ft_last_data_segment; /* infinity */
+ zft_last_vtbl->blk_sz = blk_sz;
+ zft_last_vtbl->size = zft_capacity;
+ zft_last_vtbl->zft_volume = 1;
+ zft_last_vtbl->use_compression = use_compression;
+ zft_last_vtbl->qic113 = zft_qic113;
+ zft_last_vtbl->new_volume = 1;
+ zft_last_vtbl->open = 1;
+ zft_volume_table_changed = 1;
+ zft_eom_vtbl->start_seg = ft_last_data_segment + 1;
+ TRACE_EXIT 0;
+}
+
+/* perform mtfsf, mtbsf, not allowed without zft_qic_mode
+ */
+int zft_skip_volumes(int count, zft_position *pos)
+{
+ const zft_volinfo *vtbl;
+ TRACE_FUN(ft_t_flow);
+
+ TRACE(ft_t_noise, "count: %d", count);
+
+ vtbl= zft_find_volume(pos->seg_pos);
+ while (count > 0 && vtbl != zft_eom_vtbl) {
+ vtbl = list_entry(vtbl->node.next, zft_volinfo, node);
+ count --;
+ }
+ while (count < 0 && vtbl != zft_first_vtbl) {
+ vtbl = list_entry(vtbl->node.prev, zft_volinfo, node);
+ count ++;
+ }
+ pos->seg_pos = vtbl->start_seg;
+ pos->seg_byte_pos = 0;
+ pos->volume_pos = 0;
+ pos->tape_pos = zft_calc_tape_pos(pos->seg_pos);
+ zft_just_before_eof = vtbl->size == 0;
+ if (zft_cmpr_ops) {
+ (*zft_cmpr_ops->reset)();
+ }
+ zft_deblock_segment = -1; /* no need to keep cache */
+ TRACE(ft_t_noise, "repositioning to:\n"
+ KERN_INFO "zft_seg_pos : %d\n"
+ KERN_INFO "zft_seg_byte_pos : %d\n"
+ KERN_INFO "zft_tape_pos : " LL_X "\n"
+ KERN_INFO "zft_volume_pos : " LL_X "\n"
+ KERN_INFO "file number : %d",
+ pos->seg_pos, pos->seg_byte_pos,
+ LL(pos->tape_pos), LL(pos->volume_pos), vtbl->count);
+ zft_resid = count < 0 ? -count : count;
+ TRACE_EXIT zft_resid ? -EINVAL : 0;
+}
+
+/* the following simply returns the raw data position of the EOM
+ * marker, MTIOCSIZE ioctl
+ */
+__s64 zft_get_eom_pos(void)
+{
+ if (zft_qic_mode) {
+ return zft_calc_tape_pos(zft_eom_vtbl->start_seg);
+ } else {
+ /* there is only one volume in raw mode */
+ return zft_capacity;
+ }
+}
+
+/* skip to eom, used for MTEOM
+ */
+void zft_skip_to_eom(zft_position *pos)
+{
+ TRACE_FUN(ft_t_flow);
+ pos->seg_pos = zft_eom_vtbl->start_seg;
+ pos->seg_byte_pos =
+ pos->volume_pos =
+ zft_just_before_eof = 0;
+ pos->tape_pos = zft_calc_tape_pos(pos->seg_pos);
+ TRACE(ft_t_noise, "ftape positioned to segment %d, data pos " LL_X,
+ pos->seg_pos, LL(pos->tape_pos));
+ TRACE_EXIT;
+}
+
+/* write an EOF-marker by setting zft_last_vtbl->end_seg to seg_pos.
+ * NOTE: this function assumes that zft_last_vtbl points to a valid
+ * vtbl entry
+ *
+ * NOTE: this routine always positions before the EOF marker
+ */
+int zft_close_volume(zft_position *pos)
+{
+ TRACE_FUN(ft_t_any);
+
+ if (zft_vtbl_empty || !zft_last_vtbl->open) { /* should not happen */
+ TRACE(ft_t_noise, "There are no volumes to finish");
+ TRACE_EXIT -EIO;
+ }
+ if (pos->seg_byte_pos == 0 &&
+ pos->seg_pos != zft_last_vtbl->start_seg) {
+ pos->seg_pos --;
+ pos->seg_byte_pos = zft_get_seg_sz(pos->seg_pos);
+ }
+ zft_last_vtbl->end_seg = pos->seg_pos;
+ zft_last_vtbl->size = pos->volume_pos;
+ zft_volume_table_changed = 1;
+ zft_just_before_eof = 1;
+ zft_eom_vtbl->start_seg = zft_last_vtbl->end_seg + 1;
+ zft_last_vtbl->open = 0; /* closed */
+ TRACE_EXIT 0;
+}
+
+/* write count file-marks at current position.
+ *
+ * The tape is positioned after the eof-marker, that is at byte 0 of
+ * the segment following the eof-marker
+ *
+ * this function is only allowed in zft_qic_mode
+ *
+ * Only allowed when tape is at BOT or EOD.
+ */
+int zft_weof(unsigned int count, zft_position *pos)
+{
+
+ TRACE_FUN(ft_t_flow);
+
+ if (!count) { /* write zero EOF marks should be a real no-op */
+ TRACE_EXIT 0;
+ }
+ zft_volume_table_changed = 1;
+ if (zft_tape_at_lbot(pos)) {
+ zft_init_vtbl();
+ if(zft_old_ftape) {
+ /* clear old ftape's eof marks */
+ zft_clear_ftape_file_marks();
+ zft_old_ftape = 0; /* no longer old ftape */
+ }
+ }
+ if (zft_last_vtbl->open) {
+ zft_close_volume(pos);
+ zft_move_past_eof(pos);
+ count --;
+ }
+ /* now it's easy, just append eof-marks, that is empty
+ * volumes, to the end of the already recorded media.
+ */
+ while (count > 0 &&
+ pos->seg_pos <= ft_last_data_segment &&
+ zft_eom_vtbl->count < ZFT_MAX_VOLUMES) {
+ TRACE(ft_t_noise,
+ "Writing zero sized file at segment %d", pos->seg_pos);
+ zft_new_vtbl_entry();
+ zft_last_vtbl->start_seg = pos->seg_pos;
+ zft_last_vtbl->end_seg = pos->seg_pos;
+ zft_last_vtbl->size = 0;
+ zft_last_vtbl->blk_sz = zft_blk_sz;
+ zft_last_vtbl->zft_volume = 1;
+ zft_last_vtbl->use_compression = 0;
+ pos->tape_pos += zft_get_seg_sz(pos->seg_pos);
+ zft_eom_vtbl->start_seg = ++ pos->seg_pos;
+ count --;
+ }
+ if (count > 0) {
+ /* there are two possibilities: end of tape, or the
+ * maximum number of files is exhausted.
+ */
+ zft_resid = count;
+ TRACE(ft_t_noise,"Number of marks NOT written: %d", zft_resid);
+ if (zft_eom_vtbl->count == ZFT_MAX_VOLUMES) {
+ TRACE_ABORT(-EINVAL, ft_t_warn,
+ "maximum allowed number of files "
+ "exhausted: %d", ZFT_MAX_VOLUMES);
+ } else {
+ TRACE_ABORT(-ENOSPC,
+ ft_t_noise, "reached end of tape");
+ }
+ }
+ TRACE_EXIT 0;
+}
+
+const zft_volinfo *zft_find_volume(unsigned int seg_pos)
+{
+ TRACE_FUN(ft_t_flow);
+
+ TRACE(ft_t_any, "called with seg_pos %d",seg_pos);
+ if (!zft_qic_mode) {
+ if (seg_pos > ft_last_data_segment) {
+ TRACE_EXIT &eot_vtbl;
+ }
+ tape_vtbl.blk_sz = zft_blk_sz;
+ TRACE_EXIT &tape_vtbl;
+ }
+ if (seg_pos < zft_first_vtbl->start_seg) {
+ TRACE_EXIT (cur_vtbl = zft_first_vtbl);
+ }
+ while (seg_pos > cur_vtbl->end_seg) {
+ cur_vtbl = list_entry(cur_vtbl->node.next, zft_volinfo, node);
+ TRACE(ft_t_noise, "%d - %d", cur_vtbl->start_seg, cur_vtbl->end_seg);
+ }
+ while (seg_pos < cur_vtbl->start_seg) {
+ cur_vtbl = list_entry(cur_vtbl->node.prev, zft_volinfo, node);
+ TRACE(ft_t_noise, "%d - %d", cur_vtbl->start_seg, cur_vtbl->end_seg);
+ }
+ if (seg_pos > cur_vtbl->end_seg || seg_pos < cur_vtbl->start_seg) {
+ TRACE(ft_t_bug, "This cannot happen");
+ }
+ DUMP_VOLINFO(ft_t_noise, "", cur_vtbl);
+ TRACE_EXIT cur_vtbl;
+}
+
+/* this function really assumes that we are just before eof
+ */
+void zft_move_past_eof(zft_position *pos)
+{
+ TRACE_FUN(ft_t_flow);
+
+ TRACE(ft_t_noise, "old seg. pos: %d", pos->seg_pos);
+ pos->tape_pos += zft_get_seg_sz(pos->seg_pos++) - pos->seg_byte_pos;
+ pos->seg_byte_pos = 0;
+ pos->volume_pos = 0;
+ if (zft_cmpr_ops) {
+ (*zft_cmpr_ops->reset)();
+ }
+ zft_just_before_eof = 0;
+ zft_deblock_segment = -1; /* no need to cache it anymore */
+ TRACE(ft_t_noise, "new seg. pos: %d", pos->seg_pos);
+ TRACE_EXIT;
+}
diff --git a/drivers/char/ftape/zftape/zftape-vtbl.h b/drivers/char/ftape/zftape/zftape-vtbl.h
new file mode 100644
index 000000000..7248db11c
--- /dev/null
+++ b/drivers/char/ftape/zftape/zftape-vtbl.h
@@ -0,0 +1,229 @@
+#ifndef _ZFTAPE_VTBL_H
+#define _ZFTAPE_VTBL_H
+
+/*
+ * Copyright (c) 1995-1997 Claus-Justus Heine
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139,
+ USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-vtbl.h,v $
+ * $Revision: 1.3 $
+ * $Date: 1997/10/28 14:30:09 $
+ *
+ * This file defines a volume table as defined in the QIC-80
+ * development standards.
+ */
+
+#include <linux/list.h>
+
+#include "../lowlevel/ftape-tracing.h"
+
+#include "../zftape/zftape-eof.h"
+#include "../zftape/zftape-ctl.h"
+#include "../zftape/zftape-rw.h"
+
+#define VTBL_SIZE 128 /* bytes */
+
+/* The following are offsets in the vtbl. */
+#define VTBL_SIG 0
+#define VTBL_START 4
+#define VTBL_END 6
+#define VTBL_DESC 8
+#define VTBL_DATE 52
+#define VTBL_FLAGS 56
+#define VTBL_FL_VENDOR_SPECIFIC (1<<0)
+#define VTBL_FL_MUTLI_CARTRIDGE (1<<1)
+#define VTBL_FL_NOT_VERIFIED (1<<2)
+#define VTBL_FL_REDIR_INHIBIT (1<<3)
+#define VTBL_FL_SEG_SPANNING (1<<4)
+#define VTBL_FL_DIRECTORY_LAST (1<<5)
+#define VTBL_FL_RESERVED_6 (1<<6)
+#define VTBL_FL_RESERVED_7 (1<<7)
+#define VTBL_M_NO 57
+#define VTBL_EXT 58
+#define EXT_ZFTAPE_SIG 0
+#define EXT_ZFTAPE_BLKSZ 10
+#define EXT_ZFTAPE_CMAP 12
+#define EXT_ZFTAPE_QIC113 13
+#define VTBL_PWD 84
+#define VTBL_DIR_SIZE 92
+#define VTBL_DATA_SIZE 96
+#define VTBL_OS_VERSION 104
+#define VTBL_SRC_DRIVE 106
+#define VTBL_DEV 122
+#define VTBL_RESERVED_1 123
+#define VTBL_CMPR 124
+#define VTBL_CMPR_UNREG 0x3f
+#define VTBL_CMPR_USED 0x80
+#define VTBL_FMT 125
+#define VTBL_RESERVED_2 126
+#define VTBL_RESERVED_3 127
+/* compatability with pre revision K */
+#define VTBL_K_CMPR 120
+
+/* the next is used by QIC-3020 tapes with format code 6 (>2^16
+ * segments) It is specified in QIC-113, Rev. G, Section 5 (SCSI
+ * volume table). The difference is simply, that we only store the
+ * number of segments used, not the starting segment.
+ */
+#define VTBL_SCSI_SEGS 4 /* is a 4 byte value */
+
+/* one vtbl is 128 bytes, that results in a maximum number of
+ * 29*1024/128 = 232 volumes.
+ */
+#define ZFT_MAX_VOLUMES (FT_SEGMENT_SIZE/VTBL_SIZE)
+#define VTBL_ID "VTBL"
+#define VTBL_IDS { VTBL_ID, "XTBL", "UTID", "EXVT" } /* other valid ids */
+#define ZFT_VOL_NAME "zftape volume" /* volume label used by me */
+#define ZFTAPE_SIG "LINUX ZFT"
+
+/* global variables
+ */
+typedef struct zft_internal_vtbl
+{
+ struct list_head node;
+ int count;
+ unsigned int start_seg; /* 32 bits are enough for now */
+ unsigned int end_seg; /* 32 bits are enough for now */
+ __s64 size; /* uncompressed size */
+ unsigned int blk_sz; /* block size for this volume */
+ unsigned int zft_volume :1; /* zftape created this volume */
+ unsigned int use_compression:1; /* compressed volume */
+ unsigned int qic113 :1; /* layout of compressed block
+ * info and vtbl conforms to
+ * QIC-113, Rev. G
+ */
+ unsigned int new_volume :1; /* it was created by us, this
+ * run. this allows the
+ * fields that aren't really
+ * used by zftape to be filled
+ * in by some user level
+ * program.
+ */
+ unsigned int open :1; /* just in progress of being
+ * written
+ */
+} zft_volinfo;
+
+extern struct list_head zft_vtbl;
+#define zft_head_vtbl list_entry(zft_vtbl.next, zft_volinfo, node)
+#define zft_eom_vtbl list_entry(zft_vtbl.prev, zft_volinfo, node)
+#define zft_last_vtbl list_entry(zft_eom_vtbl->node.prev, zft_volinfo, node)
+#define zft_first_vtbl list_entry(zft_head_vtbl->node.next, zft_volinfo, node)
+#define zft_vtbl_empty (zft_eom_vtbl->node.prev == &zft_head_vtbl->node)
+
+#define DUMP_VOLINFO(level, desc, info) \
+{ \
+ char tmp[21]; \
+ strncpy(tmp, desc, 20); \
+ tmp[20] = '\0'; \
+ TRACE(level, "Volume %d:\n" \
+ KERN_INFO "description : %s\n" \
+ KERN_INFO "first segment: %d\n" \
+ KERN_INFO "last segment: %d\n" \
+ KERN_INFO "size : " LL_X "\n" \
+ KERN_INFO "block size : %d\n" \
+ KERN_INFO "compression : %d\n" \
+ KERN_INFO "zftape volume: %d\n" \
+ KERN_INFO "QIC-113 conf.: %d", \
+ (info)->count, tmp, (info)->start_seg, (info)->end_seg, \
+ LL((info)->size), (info)->blk_sz, \
+ (info)->use_compression != 0, (info)->zft_volume != 0, \
+ (info)->qic113 != 0); \
+}
+
+extern int zft_qic_mode;
+extern int zft_old_ftape;
+extern int zft_volume_table_changed;
+
+/* exported functions */
+extern void zft_init_vtbl (void);
+extern void zft_free_vtbl (void);
+extern void zft_new_vtbl_entry (void);
+extern int zft_extract_volume_headers(__u8 *buffer);
+extern int zft_update_volume_table (unsigned int segment);
+extern int zft_open_volume (zft_position *pos,
+ int blk_sz, int use_compression);
+extern int zft_close_volume (zft_position *pos);
+extern const zft_volinfo *zft_find_volume(unsigned int seg_pos);
+extern int zft_skip_volumes (int count, zft_position *pos);
+extern __s64 zft_get_eom_pos (void);
+extern void zft_skip_to_eom (zft_position *pos);
+extern int zft_fake_volume_headers (eof_mark_union *eof_map,
+ int num_failed_sectors);
+extern int zft_weof (unsigned int count, zft_position *pos);
+extern void zft_move_past_eof (zft_position *pos);
+
+extern inline int zft_tape_at_eod (const zft_position *pos);
+extern inline int zft_tape_at_lbot (const zft_position *pos);
+extern inline void zft_position_before_eof (zft_position *pos,
+ const zft_volinfo *volume);
+extern inline __s64 zft_check_for_eof(const zft_volinfo *vtbl,
+ const zft_position *pos);
+
+/* this function decrements the zft_seg_pos counter if we are right
+ * at the beginning of a segment. This is to handel fsfm/bsfm -- we
+ * need to position before the eof mark. NOTE: zft_tape_pos is not
+ * changed
+ */
+extern inline void zft_position_before_eof(zft_position *pos,
+ const zft_volinfo *volume)
+{
+ TRACE_FUN(ft_t_flow);
+
+ if (pos->seg_pos == volume->end_seg + 1 && pos->seg_byte_pos == 0) {
+ pos->seg_pos --;
+ pos->seg_byte_pos = zft_get_seg_sz(pos->seg_pos);
+ }
+ TRACE_EXIT;
+}
+
+/* Mmmh. Is the position at the end of the last volume, that is right
+ * before the last EOF mark also logical an EOD condition?
+ */
+extern inline int zft_tape_at_eod(const zft_position *pos)
+{
+ TRACE_FUN(ft_t_any);
+
+ if (zft_qic_mode) {
+ TRACE_EXIT (pos->seg_pos >= zft_eom_vtbl->start_seg ||
+ zft_last_vtbl->open);
+ } else {
+ TRACE_EXIT pos->seg_pos > ft_last_data_segment;
+ }
+}
+
+extern inline int zft_tape_at_lbot(const zft_position *pos)
+{
+ if (zft_qic_mode) {
+ return (pos->seg_pos <= zft_first_vtbl->start_seg &&
+ pos->volume_pos == 0);
+ } else {
+ return (pos->seg_pos <= ft_first_data_segment &&
+ pos->volume_pos == 0);
+ }
+}
+
+/* This one checks for EOF. return remaing space (may be negative)
+ */
+extern inline __s64 zft_check_for_eof(const zft_volinfo *vtbl,
+ const zft_position *pos)
+{
+ return (__s64)(vtbl->size - pos->volume_pos);
+}
+
+#endif /* _ZFTAPE_VTBL_H */
diff --git a/drivers/char/ftape/zftape/zftape-write.c b/drivers/char/ftape/zftape/zftape-write.c
new file mode 100644
index 000000000..46f1ecc09
--- /dev/null
+++ b/drivers/char/ftape/zftape/zftape-write.c
@@ -0,0 +1,496 @@
+/*
+ * Copyright (C) 1996, 1997 Claus Heine
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-write.c,v $
+ * $Revision: 1.3 $
+ * $Date: 1997/11/06 00:50:29 $
+ *
+ * This file contains the writing code
+ * for the QIC-117 floppy-tape driver for Linux.
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
+#endif
+
+#include <linux/zftape.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,6)
+#include <asm/uaccess.h>
+#else
+#include <asm/segment.h>
+#endif
+
+#include "../zftape/zftape-init.h"
+#include "../zftape/zftape-eof.h"
+#include "../zftape/zftape-ctl.h"
+#include "../zftape/zftape-write.h"
+#include "../zftape/zftape-read.h"
+#include "../zftape/zftape-rw.h"
+#include "../zftape/zftape-vtbl.h"
+
+/* Global vars.
+ */
+
+/* Local vars.
+ */
+static int last_write_failed = 0;
+static int need_flush = 0;
+
+void zft_prevent_flush(void)
+{
+ need_flush = 0;
+}
+
+static int zft_write_header_segments(__u8* buffer)
+{
+ int header_1_ok = 0;
+ int header_2_ok = 0;
+ unsigned int time_stamp;
+ TRACE_FUN(ft_t_noise);
+
+ TRACE_CATCH(ftape_abort_operation(),);
+ ftape_seek_to_bot(); /* prevents extra rewind */
+ if (GET4(buffer, 0) != FT_HSEG_MAGIC) {
+ TRACE_ABORT(-EIO, ft_t_err,
+ "wrong header signature found, aborting");
+ }
+ /* Be optimistic: */
+ PUT4(buffer, FT_SEG_CNT,
+ zft_written_segments + GET4(buffer, FT_SEG_CNT) + 2);
+ if ((time_stamp = zft_get_time()) != 0) {
+ PUT4(buffer, FT_WR_DATE, time_stamp);
+ if (zft_label_changed) {
+ PUT4(buffer, FT_LABEL_DATE, time_stamp);
+ }
+ }
+ TRACE(ft_t_noise,
+ "writing first header segment %d", ft_header_segment_1);
+ header_1_ok = zft_verify_write_segments(ft_header_segment_1,
+ buffer, FT_SEGMENT_SIZE,
+ zft_deblock_buf) >= 0;
+ TRACE(ft_t_noise,
+ "writing second header segment %d", ft_header_segment_2);
+ header_2_ok = zft_verify_write_segments(ft_header_segment_2,
+ buffer, FT_SEGMENT_SIZE,
+ zft_deblock_buf) >= 0;
+ if (!header_1_ok) {
+ TRACE(ft_t_warn, "Warning: "
+ "update of first header segment failed");
+ }
+ if (!header_2_ok) {
+ TRACE(ft_t_warn, "Warning: "
+ "update of second header segment failed");
+ }
+ if (!header_1_ok && !header_2_ok) {
+ TRACE_ABORT(-EIO, ft_t_err, "Error: "
+ "update of both header segments failed.");
+ }
+ TRACE_EXIT 0;
+}
+
+int zft_update_header_segments(void)
+{
+ TRACE_FUN(ft_t_noise);
+
+ /* must NOT use zft_write_protected, as it also includes the
+ * file access mode. But we also want to update when soft
+ * write protection is enabled (O_RDONLY)
+ */
+ if (ft_write_protected || zft_old_ftape) {
+ TRACE_ABORT(0, ft_t_noise, "Tape set read-only: no update");
+ }
+ if (!zft_header_read) {
+ TRACE_ABORT(0, ft_t_noise, "Nothing to update");
+ }
+ if (!zft_header_changed) {
+ zft_header_changed = zft_written_segments > 0;
+ }
+ if (!zft_header_changed && !zft_volume_table_changed) {
+ TRACE_ABORT(0, ft_t_noise, "Nothing to update");
+ }
+ TRACE(ft_t_noise, "Updating header segments");
+ if (ftape_get_status()->fti_state == writing) {
+ TRACE_CATCH(ftape_loop_until_writes_done(),);
+ }
+ TRACE_CATCH(ftape_abort_operation(),);
+
+ zft_deblock_segment = -1; /* invalidate the cache */
+ if (zft_header_changed) {
+ TRACE_CATCH(zft_write_header_segments(zft_hseg_buf),);
+ }
+ if (zft_volume_table_changed) {
+ TRACE_CATCH(zft_update_volume_table(ft_first_data_segment),);
+ }
+ zft_header_changed =
+ zft_volume_table_changed =
+ zft_label_changed =
+ zft_written_segments = 0;
+ TRACE_CATCH(ftape_abort_operation(),);
+ ftape_seek_to_bot();
+ TRACE_EXIT 0;
+}
+
+static int read_merge_buffer(int seg_pos, __u8 *buffer, int offset, int seg_sz)
+{
+ int result = 0;
+ const ft_trace_t old_tracing = TRACE_LEVEL;
+ TRACE_FUN(ft_t_flow);
+
+ if (zft_qic_mode) {
+ /* writing in the middle of a volume is NOT allowed
+ *
+ */
+ TRACE(ft_t_noise, "No need to read a segment");
+ memset(buffer + offset, 0, seg_sz - offset);
+ TRACE_EXIT 0;
+ }
+ TRACE(ft_t_any, "waiting");
+ ftape_start_writing(FT_WR_MULTI);
+ TRACE_CATCH(ftape_loop_until_writes_done(),);
+
+ TRACE(ft_t_noise, "trying to read segment %d from offset %d",
+ seg_pos, offset);
+ SET_TRACE_LEVEL(ft_t_bug);
+ result = zft_fetch_segment_fraction(seg_pos, buffer,
+ FT_RD_SINGLE,
+ offset, seg_sz - offset);
+ SET_TRACE_LEVEL(old_tracing);
+ if (result != (seg_sz - offset)) {
+ TRACE(ft_t_noise, "Ignore error: read_segment() result: %d",
+ result);
+ memset(buffer + offset, 0, seg_sz - offset);
+ }
+ TRACE_EXIT 0;
+}
+
+/* flush the write buffer to tape and write an eof-marker at the
+ * current position if not in raw mode. This function always
+ * positions the tape before the eof-marker. _ftape_close() should
+ * then advance to the next segment.
+ *
+ * the parameter "finish_volume" describes whether to position before
+ * or after the possibly created file-mark. We always position after
+ * the file-mark when called from ftape_close() and a flush was needed
+ * (that is ftape_write() was the last tape operation before calling
+ * ftape_flush) But we always position before the file-mark when this
+ * function get's called from outside ftape_close()
+ */
+int zft_flush_buffers(void)
+{
+ int result;
+ int data_remaining;
+ int this_segs_size;
+ TRACE_FUN(ft_t_flow);
+
+ TRACE(ft_t_data_flow,
+ "entered, ftape_state = %d", ftape_get_status()->fti_state);
+ if (ftape_get_status()->fti_state != writing && !need_flush) {
+ TRACE_ABORT(0, ft_t_noise, "no need for flush");
+ }
+ zft_io_state = zft_idle; /* triggers some initializations for the
+ * read and write routines
+ */
+ if (last_write_failed) {
+ ftape_abort_operation();
+ TRACE_EXIT -EIO;
+ }
+ TRACE(ft_t_noise, "flushing write buffers");
+ this_segs_size = zft_get_seg_sz(zft_pos.seg_pos);
+ if (this_segs_size == zft_pos.seg_byte_pos) {
+ zft_pos.seg_pos ++;
+ data_remaining = zft_pos.seg_byte_pos = 0;
+ } else {
+ data_remaining = zft_pos.seg_byte_pos;
+ }
+ /* If there is any data not written to tape yet, append zero's
+ * up to the end of the sector (if using compression) or merge
+ * it with the data existing on the tape Then write the
+ * segment(s) to tape.
+ */
+ TRACE(ft_t_noise, "Position:\n"
+ KERN_INFO "seg_pos : %d\n"
+ KERN_INFO "byte pos : %d\n"
+ KERN_INFO "remaining: %d",
+ zft_pos.seg_pos, zft_pos.seg_byte_pos, data_remaining);
+ if (data_remaining > 0) {
+ do {
+ this_segs_size = zft_get_seg_sz(zft_pos.seg_pos);
+ if (this_segs_size > data_remaining) {
+ TRACE_CATCH(read_merge_buffer(zft_pos.seg_pos,
+ zft_deblock_buf,
+ data_remaining,
+ this_segs_size),
+ last_write_failed = 1);
+ }
+ result = ftape_write_segment(zft_pos.seg_pos,
+ zft_deblock_buf,
+ FT_WR_MULTI);
+ if (result != this_segs_size) {
+ TRACE(ft_t_err, "flush buffers failed");
+ zft_pos.tape_pos -= zft_pos.seg_byte_pos;
+ zft_pos.seg_byte_pos = 0;
+
+ last_write_failed = 1;
+ TRACE_EXIT result;
+ }
+ zft_written_segments ++;
+ TRACE(ft_t_data_flow,
+ "flush, moved out buffer: %d", result);
+ /* need next segment for more data (empty segments?)
+ */
+ if (result < data_remaining) {
+ if (result > 0) {
+ /* move remainder to buffer beginning
+ */
+ memmove(zft_deblock_buf,
+ zft_deblock_buf + result,
+ FT_SEGMENT_SIZE - result);
+ }
+ }
+ data_remaining -= result;
+ zft_pos.seg_pos ++;
+ } while (data_remaining > 0);
+ TRACE(ft_t_any, "result: %d", result);
+ zft_deblock_segment = --zft_pos.seg_pos;
+ if (data_remaining == 0) { /* first byte next segment */
+ zft_pos.seg_byte_pos = this_segs_size;
+ } else { /* after data previous segment, data_remaining < 0 */
+ zft_pos.seg_byte_pos = data_remaining + result;
+ }
+ } else {
+ TRACE(ft_t_noise, "zft_deblock_buf empty");
+ zft_pos.seg_pos --;
+ zft_pos.seg_byte_pos = zft_get_seg_sz (zft_pos.seg_pos);
+ ftape_start_writing(FT_WR_MULTI);
+ }
+ TRACE(ft_t_any, "waiting");
+ if ((result = ftape_loop_until_writes_done()) < 0) {
+ /* that's really bad. What to to with zft_tape_pos?
+ */
+ TRACE(ft_t_err, "flush buffers failed");
+ }
+ TRACE(ft_t_any, "zft_seg_pos: %d, zft_seg_byte_pos: %d",
+ zft_pos.seg_pos, zft_pos.seg_byte_pos);
+ last_write_failed =
+ need_flush = 0;
+ TRACE_EXIT result;
+}
+
+/* return-value: the number of bytes removed from the user-buffer
+ *
+ * out:
+ * int *write_cnt: how much actually has been moved to the
+ * zft_deblock_buf
+ * int req_len : MUST NOT BE CHANGED, except at EOT, in
+ * which case it may be adjusted
+ * in :
+ * char *buff : the user buffer
+ * int buf_pos_write : copy of buf_len_wr int
+ * this_segs_size : the size in bytes of the actual segment
+ * char
+ * *zft_deblock_buf : zft_deblock_buf
+ */
+static int zft_simple_write(int *cnt,
+ __u8 *dst_buf, const int seg_sz,
+ const __u8 *src_buf, const int req_len,
+ const zft_position *pos,const zft_volinfo *volume)
+{
+ int space_left;
+ TRACE_FUN(ft_t_flow);
+
+ /* volume->size holds the tape capacity while volume is open */
+ if (pos->tape_pos + volume->blk_sz > volume->size) {
+ TRACE_EXIT -ENOSPC;
+ }
+ /* remaining space in this segment, NOT zft_deblock_buf
+ */
+ space_left = seg_sz - pos->seg_byte_pos;
+ *cnt = req_len < space_left ? req_len : space_left;
+#if LINUX_VERSION_CODE > KERNEL_VER(2,1,3)
+ if (copy_from_user(dst_buf + pos->seg_byte_pos, src_buf, *cnt) != 0) {
+ TRACE_EXIT -EFAULT;
+ }
+#else
+ TRACE_CATCH(verify_area(VERIFY_READ, src_buf, *cnt),);
+ memcpy_fromfs(dst_buf + pos->seg_byte_pos, src_buf, *cnt);
+#endif
+ TRACE_EXIT *cnt;
+}
+
+static int check_write_access(int req_len,
+ const zft_volinfo **volume,
+ zft_position *pos,
+ const unsigned int blk_sz)
+{
+ int result;
+ TRACE_FUN(ft_t_flow);
+
+ if ((req_len % zft_blk_sz) != 0) {
+ TRACE_ABORT(-EINVAL, ft_t_info,
+ "write-count %d must be multiple of block-size %d",
+ req_len, blk_sz);
+ }
+ if (zft_io_state == zft_writing) {
+ /* all other error conditions have been checked earlier
+ */
+ TRACE_EXIT 0;
+ }
+ zft_io_state = zft_idle;
+ TRACE_CATCH(zft_check_write_access(pos),);
+ /* If we haven't read the header segment yet, do it now.
+ * This will verify the configuration, get the bad sector
+ * table and read the volume table segment
+ */
+ if (!zft_header_read) {
+ TRACE_CATCH(zft_read_header_segments(),);
+ }
+ /* fine. Now the tape is either at BOT or at EOD,
+ * Write start of volume now
+ */
+ TRACE_CATCH(zft_open_volume(pos, blk_sz, zft_use_compression),);
+ *volume = zft_find_volume(pos->seg_pos);
+ DUMP_VOLINFO(ft_t_noise, "", *volume);
+ zft_just_before_eof = 0;
+ /* now merge with old data if neccessary */
+ if (!zft_qic_mode && pos->seg_byte_pos != 0){
+ result = zft_fetch_segment(pos->seg_pos,
+ zft_deblock_buf,
+ FT_RD_SINGLE);
+ if (result < 0) {
+ if (result == -EINTR || result == -ENOSPC) {
+ TRACE_EXIT result;
+ }
+ TRACE(ft_t_noise,
+ "ftape_read_segment() result: %d. "
+ "This might be normal when using "
+ "a newly\nformatted tape", result);
+ memset(zft_deblock_buf, '\0', pos->seg_byte_pos);
+ }
+ }
+ zft_io_state = zft_writing;
+ TRACE_EXIT 0;
+}
+
+static int fill_deblock_buf(__u8 *dst_buf, const int seg_sz,
+ zft_position *pos, const zft_volinfo *volume,
+ const char *usr_buf, const int req_len)
+{
+ int cnt = 0;
+ int result = 0;
+ TRACE_FUN(ft_t_flow);
+
+ if (seg_sz == 0) {
+ TRACE_ABORT(0, ft_t_data_flow, "empty segment");
+ }
+ TRACE(ft_t_data_flow, "\n"
+ KERN_INFO "remaining req_len: %d\n"
+ KERN_INFO " buf_pos: %d",
+ req_len, pos->seg_byte_pos);
+ /* zft_deblock_buf will not contain a valid segment any longer */
+ zft_deblock_segment = -1;
+ if (zft_use_compression) {
+ TRACE_CATCH(zft_cmpr_lock(1 /* try to load */),);
+ TRACE_CATCH(result= (*zft_cmpr_ops->write)(&cnt,
+ dst_buf, seg_sz,
+ usr_buf, req_len,
+ pos, volume),);
+ } else {
+ TRACE_CATCH(result= zft_simple_write(&cnt,
+ dst_buf, seg_sz,
+ usr_buf, req_len,
+ pos, volume),);
+ }
+ pos->volume_pos += result;
+ pos->seg_byte_pos += cnt;
+ pos->tape_pos += cnt;
+ TRACE(ft_t_data_flow, "\n"
+ KERN_INFO "removed from user-buffer : %d bytes.\n"
+ KERN_INFO "copied to zft_deblock_buf: %d bytes.\n"
+ KERN_INFO "zft_tape_pos : " LL_X " bytes.",
+ result, cnt, LL(pos->tape_pos));
+ TRACE_EXIT result;
+}
+
+
+/* called by the kernel-interface routine "zft_write()"
+ */
+int _zft_write(const char* buff, int req_len)
+{
+ int result = 0;
+ int written = 0;
+ int write_cnt;
+ int seg_sz;
+ static const zft_volinfo *volume = NULL;
+ TRACE_FUN(ft_t_flow);
+
+ zft_resid = req_len;
+ last_write_failed = 1; /* reset to 0 when successful */
+ /* check if write is allowed
+ */
+ TRACE_CATCH(check_write_access(req_len, &volume,&zft_pos,zft_blk_sz),);
+ while (req_len > 0) {
+ /* Allow us to escape from this loop with a signal !
+ */
+ FT_SIGNAL_EXIT(_DONT_BLOCK);
+ seg_sz = zft_get_seg_sz(zft_pos.seg_pos);
+ if ((write_cnt = fill_deblock_buf(zft_deblock_buf,
+ seg_sz,
+ &zft_pos,
+ volume,
+ buff,
+ req_len)) < 0) {
+ zft_resid -= written;
+ if (write_cnt == -ENOSPC) {
+ /* leave the remainder to flush_buffers()
+ */
+ TRACE(ft_t_info, "No space left on device");
+ last_write_failed = 0;
+ if (!need_flush) {
+ need_flush = written > 0;
+ }
+ TRACE_EXIT written > 0 ? written : -ENOSPC;
+ } else {
+ TRACE_EXIT result;
+ }
+ }
+ if (zft_pos.seg_byte_pos == seg_sz) {
+ TRACE_CATCH(ftape_write_segment(zft_pos.seg_pos,
+ zft_deblock_buf,
+ FT_WR_ASYNC),
+ zft_resid -= written);
+ zft_written_segments ++;
+ zft_pos.seg_byte_pos = 0;
+ zft_deblock_segment = zft_pos.seg_pos;
+ ++zft_pos.seg_pos;
+ }
+ written += write_cnt;
+ buff += write_cnt;
+ req_len -= write_cnt;
+ } /* while (req_len > 0) */
+ TRACE(ft_t_data_flow, "remaining in blocking buffer: %d",
+ zft_pos.seg_byte_pos);
+ TRACE(ft_t_data_flow, "just written bytes: %d", written);
+ last_write_failed = 0;
+ zft_resid -= written;
+ need_flush = need_flush || written > 0;
+ TRACE_EXIT written; /* bytes written */
+}
diff --git a/drivers/char/ftape/zftape/zftape-write.h b/drivers/char/ftape/zftape/zftape-write.h
new file mode 100644
index 000000000..4a8d47687
--- /dev/null
+++ b/drivers/char/ftape/zftape/zftape-write.h
@@ -0,0 +1,38 @@
+#ifndef _ZFTAPE_WRITE_H
+#define _ZFTAPE_WRITE_H
+
+/*
+ * Copyright (C) 1996, 1997 Claus-Justus Heine
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-write.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:19:13 $
+ *
+ * This file contains the definitions for the write functions
+ * for the zftape driver for Linux.
+ *
+ */
+
+extern int zft_flush_buffers(void);
+extern int zft_update_header_segments(void);
+extern void zft_prevent_flush(void);
+
+/* hook for the VFS interface
+ */
+extern int _zft_write(const char *buff, int req_len);
+#endif /* _ZFTAPE_WRITE_H */
diff --git a/drivers/char/ftape/zftape/zftape_syms.c b/drivers/char/ftape/zftape/zftape_syms.c
new file mode 100644
index 000000000..71b3175a5
--- /dev/null
+++ b/drivers/char/ftape/zftape/zftape_syms.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 1997 Claus-Justus Heine
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape_syms.c,v $
+ * $Revision: 1.3 $
+ * $Date: 1997/10/05 19:19:14 $
+ *
+ * This file contains the the symbols that the zftape frontend to
+ * the ftape floppy tape driver exports
+ */
+
+#include <linux/config.h>
+#define __NO_VERSION__
+#include <linux/module.h>
+
+#include <linux/zftape.h>
+
+#include "../zftape/zftape-init.h"
+#include "../zftape/zftape-read.h"
+#include "../zftape/zftape-buffers.h"
+#include "../zftape/zftape-ctl.h"
+
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,18)
+# define FT_KSYM(sym) EXPORT_SYMBOL(##sym);
+#else
+# define FT_KSYM(sym) X(##sym),
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VER(2,1,18)
+struct symbol_table zft_symbol_table = {
+#include <linux/symtab_begin.h>
+#endif
+/* zftape-init.c */
+FT_KSYM(zft_cmpr_register)
+FT_KSYM(zft_cmpr_unregister)
+/* zftape-read.c */
+FT_KSYM(zft_fetch_segment_fraction)
+/* zftape-buffers.c */
+FT_KSYM(zft_vmalloc_once)
+FT_KSYM(zft_vmalloc_always)
+FT_KSYM(zft_vfree)
+#if LINUX_VERSION_CODE < KERNEL_VER(2,1,18)
+#include <linux/symtab_end.h>
+};
+#endif
diff --git a/drivers/char/ftape/zftape/zftape_syms.h b/drivers/char/ftape/zftape/zftape_syms.h
new file mode 100644
index 000000000..26f87565d
--- /dev/null
+++ b/drivers/char/ftape/zftape/zftape_syms.h
@@ -0,0 +1,39 @@
+#ifndef _ZFTAPE_SYMS_H
+#define _ZFTAPE_SYMS_H
+
+/*
+ * Copyright (C) 1996, 1997 Claus-Justus Heine
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape_syms.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:19:14 $
+ *
+ * This file contains the definitions needed for the symbols
+ * ftape exports
+ *
+ */
+
+#if LINUX_VERSION_CODE < KERNEL_VER(2,1,18)
+#include <linux/module.h>
+/* ftape-vfs.c defined global vars.
+ */
+
+extern struct symbol_table zft_symbol_table;
+#endif
+
+#endif
diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c
index f18d25d9a..25647ab2f 100644
--- a/drivers/char/istallion.c
+++ b/drivers/char/istallion.c
@@ -813,7 +813,7 @@ static int stli_open(struct tty_struct *tty, struct file *filp)
portp->refcount++;
while (test_bit(ST_INITIALIZING, &portp->state)) {
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
return(-ERESTARTSYS);
interruptible_sleep_on(&portp->raw_wait);
}
@@ -1048,7 +1048,7 @@ static int stli_rawopen(stlibrd_t *brdp, stliport_t *portp, unsigned long arg, i
* memory, so we must wait until it is complete.
*/
while (test_bit(ST_CLOSING, &portp->state)) {
- if (current->signal & ~current->blocked) {
+ if (signal_pending(current)) {
restore_flags(flags);
return(-ERESTARTSYS);
}
@@ -1081,7 +1081,7 @@ static int stli_rawopen(stlibrd_t *brdp, stliport_t *portp, unsigned long arg, i
rc = 0;
set_bit(ST_OPENING, &portp->state);
while (test_bit(ST_OPENING, &portp->state)) {
- if (current->signal & ~current->blocked) {
+ if (signal_pending(current)) {
rc = -ERESTARTSYS;
break;
}
@@ -1123,7 +1123,7 @@ static int stli_rawclose(stlibrd_t *brdp, stliport_t *portp, unsigned long arg,
*/
if (wait) {
while (test_bit(ST_CLOSING, &portp->state)) {
- if (current->signal & ~current->blocked) {
+ if (signal_pending(current)) {
restore_flags(flags);
return(-ERESTARTSYS);
}
@@ -1155,7 +1155,7 @@ static int stli_rawclose(stlibrd_t *brdp, stliport_t *portp, unsigned long arg,
*/
rc = 0;
while (test_bit(ST_CLOSING, &portp->state)) {
- if (current->signal & ~current->blocked) {
+ if (signal_pending(current)) {
rc = -ERESTARTSYS;
break;
}
@@ -1188,7 +1188,7 @@ static int stli_cmdwait(stlibrd_t *brdp, stliport_t *portp, unsigned long cmd, v
save_flags(flags);
cli();
while (test_bit(ST_CMDING, &portp->state)) {
- if (current->signal & ~current->blocked) {
+ if (signal_pending(current)) {
restore_flags(flags);
return(-ERESTARTSYS);
}
@@ -1198,7 +1198,7 @@ static int stli_cmdwait(stlibrd_t *brdp, stliport_t *portp, unsigned long cmd, v
stli_sendcmd(brdp, portp, cmd, arg, size, copyback);
while (test_bit(ST_CMDING, &portp->state)) {
- if (current->signal & ~current->blocked) {
+ if (signal_pending(current)) {
restore_flags(flags);
return(-ERESTARTSYS);
}
@@ -1312,7 +1312,7 @@ static int stli_waitcarrier(stlibrd_t *brdp, stliport_t *portp, struct file *fil
(doclocal || (portp->sigs & TIOCM_CD))) {
break;
}
- if (current->signal & ~current->blocked) {
+ if (signal_pending(current)) {
rc = -ERESTARTSYS;
break;
}
diff --git a/drivers/char/joystick.c b/drivers/char/joystick.c
index e85d36a98..c8c44d77a 100644
--- a/drivers/char/joystick.c
+++ b/drivers/char/joystick.c
@@ -1,376 +1,837 @@
/*
+ * $Id: joystick.c,v 1.2 1997/10/31 19:11:48 mj Exp $
+ *
+ * Copyright (C) 1997 Vojtech Pavlik
+ */
- linux/drivers/char/joystick.c
- Copyright (C) 1992, 1993 Arthur C. Smith
- Joystick driver for Linux running on an IBM compatible computer.
-
-VERSION INFO:
-01/08/93 ACS 0.1: Works but needs multi-joystick support
-01/13/93 ACS 0.2: Added multi-joystick support (minor 0 and 1)
- Added delay between measuring joystick axis
- Added scaling ioctl
-02/16/93 ACS 0.3: Modified scaling to use ints to prevent kernel
- panics 8-)
-02/28/93 ACS 0.4: Linux99.6 and fixed race condition in js_read.
- After looking at a schematic of a joystick card
- it became apparent that any write to the joystick
- port started ALL the joystick one shots. If the
- one that we are reading is short enough and the
- first one to be read, the second one will return
- bad data if it's one shot has not expired when
- the joystick port is written for the second time.
- Thus solves the mystery delay problem in 0.2!
-05/05/93 ACS/Eyal 0.5: Upgraded the driver to the 99.9 kernel, added
- joystick support to the make config options,
- updated the driver to return the buttons as
- positive logic, and read both axis at once
- (thanks Eyal!), and added some new ioctls.
-02/12/94 Jeff Tranter 0.6: Made necessary changes to work with 0.99pl15
- kernel (and hopefully 1.0). Also did some
- cleanup: indented code, fixed some typos, wrote
- man page, etc...
-05/17/95 Dan Fandrich 0.7.3: Added I/O port registration, cleaned up code
-04/03/96 Matt Rhoten 0.8: many minor changes:
- new read loop from Hal Maney <maney@norden.com>
- cleaned up #includes to allow #include of
- joystick.h with gcc -Wall and from g++
- made js_init fail if it finds zero joysticks
- general source/comment cleanup
- use of MOD_(INC|DEC)_USE_COUNT
- changes from Bernd Schmidt <crux@Pool.Informatik.RWTH-Aachen.DE>
- to compile correctly under 1.3 in kernel or as module
-06/30/97 Alan Cox 0.9: Ported to 2.1.x
- Reformatted to resemble Linux coding standard
- Removed semaphore bug (we can dump the lot I think)
- Fixed xntp timer adjust during joystick timer0 bug
- Changed variable names to lower case. Kept binary
- compatibility.
- Better ioctl names. Kept binary compatibility.
- Removed 'save_busy'. Just set busy to 1.
-*/
+/*
+ * This is joystick driver for Linux. It supports up to two analog joysticks
+ * on a PC compatible machine. See Documentation/joystick.txt for changelog
+ * and credits.
+ */
+#include <linux/config.h>
+#include <linux/init.h>
#include <linux/module.h>
-#include <linux/joystick.h>
+#include <linux/ioport.h>
+#include <linux/errno.h>
#include <linux/mm.h>
+#include <linux/ptrace.h>
+#include <linux/interrupt.h>
+#include <linux/malloc.h>
+#include <linux/poll.h>
#include <linux/major.h>
-#include <linux/ioport.h>
+#include <linux/joystick.h>
+
#include <asm/io.h>
+#include <asm/ptrace.h>
#include <asm/uaccess.h>
+#include <asm/param.h>
-static struct js_config js_data[JS_MAX]; /* misc data */
-static int js_exist; /* which joysticks' axis exist? */
-static int js_read_semaphore; /* to prevent two processes from trying
- to read different joysticks at the
- same time */
+#define PIT_HZ 1193180L /* PIT clock is 1.19318 MHz */
-/*
- * get_timer0():
- * returns the current value of timer 0. This is a 16 bit counter that starts
- * at LATCH and counts down to 0
+#define JS_MAXTIME PIT_HZ/250 /* timeout for read (4 ms) */
+
+#define JS_BUTTON_PERIOD HZ/50 /* button valid time (20 ms) */
+#define JS_AXIS_MIN_PERIOD HZ/25 /* axis min valid time (40 ms) */
+#define JS_AXIS_MAX_PERIOD HZ/25*2 /* axis max valid time (80 ms) */
+
+#define JS_FIFO_SIZE 16 /* number of FIFO entries */
+#define JS_BUFF_SIZE 32 /* output buffer size */
+#define JS_RETRIES 4 /* number of retries */
+#define JS_DEF_PREC 8 /* initial precision for all axes */
+
+#define JS_NUM 2 /* number of joysticks */
+
+#define JS_AXES 0x0f /* bit mask for all axes */
+#define JS_BUTTONS 0xf0 /* bit mask for all buttons */
+
+#define PIT_MODE 0x43 /* timer mode port */
+#define PIT_DATA 0x40 /* timer 0 data port */
+#define JS_PORT 0x201 /* joystick port */
+
+#define JS_TRIGGER 0xff /* triggers one-shots */
+#define PIT_READ_TIMER 0x00 /* to read timer 0 */
+
+#define DELTA(X,Y,Z) ((X)-(Y)+(((X)>=(Y))?0:Z)) /* cyclic delta */
+#define DELTA_T(X,Y) DELTA((X),(Y),(PIT_HZ/HZ)) /* for time measurement */
+#define DELTA_TX(X,Y,Z) DELTA_T((X),((Y)&0xFF)|(((Z)&0xFF)<<8))
+#define ROT(A,B,C) ((((A)<(C))&&(((B)>(A))&&((B)<(C))))||(((A)>(C))&&(((B)>(A))||((B)<(C)))))
+#define GOF(X) (((X)==JS_BUFF_SIZE-1)?0:(X)+1)
+#define GOFF(X) (((X)==JS_FIFO_SIZE-1)?0:(X)+1)
+#define GOB(X) ((X)?(X)-1:JS_BUFF_SIZE-1)
+
+struct js_data {
+ int ahead;
+ int bhead;
+ int tail;
+ struct js_event buff[JS_BUFF_SIZE];
+ struct js_list *list;
+ struct wait_queue *wait;
+ unsigned int exist;
+};
+
+struct js_axis {
+ int value;
+ struct js_corr corr;
+};
+
+struct js_list {
+ struct js_list *next; /* next-in-list pointer */
+ unsigned long time; /* when the device was open */
+ int tail; /* a tail for js_buff */
+ char startup;
+};
+
+struct js_fifo {
+ unsigned long time;
+ unsigned long event;
+};
+
+static struct js_data jsd[JS_NUM]; /* joystick data */
+static struct timer_list js_timer; /* joystick timer */
+
+static unsigned char js_fifo_head = 0; /* head of the fifo */
+static unsigned char js_fifo_tail = JS_FIFO_SIZE - 1; /* tail of the fifo */
+static struct js_fifo js_fifo[JS_FIFO_SIZE]; /* the fifo */
+
+static unsigned char js_last_buttons = 0; /* last read button state */
+static unsigned long js_axis_time = 0; /* last read axis time */
+static unsigned long js_mark_time = 0;
+
+static unsigned char js_axes_exist; /* all axes that exist */
+static unsigned char js_buttons_exist; /* all buttons that exist */
+
+static struct js_axis js_axis[4];
+static unsigned int js_buttons = 0;
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@atrey.karlin.mff.cuni.cz>");
+MODULE_SUPPORTED_DEVICE("js");
+MODULE_PARM(js, "0-1b");
+
+static char js[] = {0, 0};
+
+/*
+ * get_pit() returns the immediate state of PIT0. Must be run
+ * with interrupts disabled.
*/
-
-extern inline int get_timer0(void)
+
+static inline int get_pit(void)
{
- unsigned long flags;
- int t0, t1;
+ int t, flags;
+
save_flags(flags);
cli();
- outb (0, PIT_MODE);
- t0 = (int) inb (PIT_COUNTER_0);
- t1 = ((int) inb (PIT_COUNTER_0) << 8) + t0;
+ outb(PIT_READ_TIMER, PIT_MODE);
+ t = inb(PIT_DATA);
+ t |= (int) inb(PIT_DATA) << 8;
restore_flags(flags);
- return (t1);
+ return t;
}
/*
- * find_axes():
- *
- * returns which axes are hooked up, in a bitfield. 2^n is set if
- * axis n is hooked up, for 0 <= n < 4.
- *
- * REVIEW: should update this to handle eight-axis (four-stick) game port
- * cards. anyone have one of these to test on? mattrh 3/23/96
+ * count_bits() counts set bits in a byte.
*/
-
-extern inline int find_axes(void)
+
+static int count_bits(unsigned char c)
{
- int j;
- outb (0xff, JS_PORT); /* trigger oneshots */
- /* and see what happens */
- for (j = JS_DEF_TIMEOUT; (0x0f & inb (JS_PORT)) && j; j--);
- /* do nothing; wait for the timeout */
- js_exist = inb (JS_PORT) & 0x0f; /* get joystick status byte */
- js_exist = (~js_exist) & 0x0f;
-/* printk("find_axes: js_exist is %d (0x%04X)\n", js_exist, js_exist);*/
- return js_exist;
+ int i, t = 0;
+ for (i = 0; i < 8; i++)
+ if (c & (1 << i)) t++;
+ return t;
}
-static int js_ioctl (struct inode *inode,
- struct file *file,
- unsigned int cmd,
- unsigned long arg)
+/*
+ * js_correct() performs correction of raw joystick data.
+ */
+
+static int js_correct(int value, struct js_corr *corr)
{
- unsigned int minor = MINOR (inode->i_rdev);
- if (minor >= JS_MAX)
- return -ENODEV;
-
- if ((((inb (JS_PORT) & 0x0f) >> (minor << 1)) & 0x03) == 0x03) /*js minor exists?*/
- return -ENODEV;
- switch (cmd)
- {
-
- case JSIOCSCAL: /*from struct *arg to js_data[minor]*/
- if(copy_from_user(&js_data[minor].js_corr,
- (void *)arg, sizeof(struct js_status)))
- return -EFAULT;
- break;
- case JSIOCGCAL: /*to struct *arg from js_data[minor]*/
- if(copy_to_user((void *) arg, &js_data[minor].js_corr,
- sizeof(struct js_status)))
- return -EFAULT;
- break;
- case JSIOCSTIMEOUT:
- if(copy_from_user(&js_data[minor].js_timeout,
- (void *)arg, sizeof(js_data[0].js_timeout)))
- return -EFAULT;
- break;
- case JSIOCGTIMEOUT:
- if(copy_to_user((void *)arg, &js_data[minor].js_timeout,
- sizeof(js_data[0].js_timeout)))
- return -EFAULT;
- break;
- case JSIOCSTIMELIMIT:
- if(copy_from_user(&js_data[minor].js_timelimit,
- (void *)arg, sizeof(js_data[0].js_timelimit)))
- return -EFAULT;
- break;
- case JSIOCGTIMELIMIT:
- if(copy_to_user((void *)arg, &js_data[minor].js_timelimit,
- sizeof(js_data[0].js_timelimit)))
- return -EFAULT;
- break;
- case JSIOCGCONFIG:
- if(copy_to_user((void *)arg, &js_data[minor],
- sizeof(struct js_config)))
- return -EFAULT;
- break;
- case JSIOCSCONFIG:
- if(copy_from_user(&js_data[minor], (void *)arg,
- sizeof(struct js_config)))
- return -EFAULT;
- /* Must be busy to do this ioctl! */
- js_data[minor].busy = 1;
- break;
- default:
- return -EINVAL;
+ int t;
+
+ if (corr->type == JS_CORR_NONE) return value;
+ t = value > corr->coef[0] ? (value < corr->coef[1] ? corr->coef[0] : value - corr->coef[1] + corr->coef[0]) : value;
+ if (t == corr->coef[0]) return 32768;
+
+ switch (corr->type) {
+ case JS_CORR_BROKEN:
+ t = t < corr->coef[0] ? ((corr->coef[2] * t) >> 14) + corr->coef[3] :
+ ((corr->coef[4] * t) >> 14) + corr->coef[5];
+ break;
+ default:
+ return 0;
}
- return 0;
+
+ if (t < 0) return 0;
+ if (t > 65535) return 65535;
+
+ return t;
}
/*
- * js_open():
- * device open routine. increments module usage count, initializes
- * data for that joystick.
- *
- * returns: 0 or
- * -ENODEV: asked for joystick other than #0 or #1
- * -ENODEV: asked for joystick on axis where there is none
- * -EBUSY: attempt to open joystick already open
+ * js_compare() compares two close axis values and decides
+ * whether they are "same".
*/
-
-static int js_open (struct inode *inode, struct file *file)
+
+static int js_compare(int x, int y, int prec)
{
- unsigned int minor = MINOR (inode->i_rdev);
- int j;
-
- if (minor >= JS_MAX)
- return -ENODEV; /*check for joysticks*/
-
- for (j = JS_DEF_TIMEOUT; (js_exist & inb (JS_PORT)) && j; j--);
- cli(); /*block js_read while js_exist is being modified*/
- /*js minor exists?*/
- if ((((js_exist = inb (JS_PORT)) >> (minor << 1)) & 0x03) == 0x03) {
- js_exist = (~js_exist) & 0x0f;
- sti();
- return -ENODEV;
+ return (x < y + prec) && (y < x + prec);
+}
+
+/*
+ * js_probe() probes for joysticks
+ */
+
+inline int js_probe(void)
+{
+ int t;
+
+ outb(JS_TRIGGER, JS_PORT);
+ t = get_pit();
+ while (DELTA_T(t, get_pit()) < JS_MAXTIME);
+ t = inb(JS_PORT);
+
+ if (js[0] || js[1]) {
+ jsd[0].exist = js[0] & ~(t & JS_AXES);
+ jsd[1].exist = js[1] & ~(t & JS_AXES);
+ } else
+ switch (t & JS_AXES) {
+ case 0x0c: jsd[0].exist = 0x33; jsd[1].exist = 0x00; break; /* joystick 0 connected */
+ case 0x03: jsd[0].exist = 0x00; jsd[1].exist = 0xcc; break; /* joystick 1 connected */
+ case 0x04: jsd[0].exist = 0xfb; jsd[1].exist = 0x00; break; /* 3-axis joystick connected */
+ case 0x00: jsd[0].exist = 0x33; jsd[1].exist = 0xcc; break; /* joysticks 0 and 1 connected */
+ default: jsd[0].exist = 0x00; jsd[1].exist = 0x00; return -1; /* no joysticks */
}
- js_exist = (~js_exist) & 0x0f;
- sti();
- if (js_data[minor].busy)
- return -EBUSY;
- js_data[minor].busy = JS_TRUE;
- js_data[minor].js_corr.x = JS_DEF_CORR; /*default scale*/
- js_data[minor].js_corr.y = JS_DEF_CORR;
- js_data[minor].js_timeout = JS_DEF_TIMEOUT;
- js_data[minor].js_timelimit = JS_DEF_TIMELIMIT;
- js_data[minor].js_expiretime = jiffies;
+ js_axes_exist = (jsd[0].exist | jsd[1].exist) & JS_AXES;
+ js_buttons_exist = (jsd[0].exist | jsd[1].exist) & JS_BUTTONS;
- MOD_INC_USE_COUNT;
return 0;
}
-static int js_release (struct inode *inode, struct file *file)
+/*
+ * js_do_timer() controls the action by adding entries to the event
+ * fifo each time a button changes its state or axis valid time
+ * expires.
+ */
+
+static void js_do_timer(unsigned long data)
{
- unsigned int minor = MINOR (inode->i_rdev);
- inode->i_atime = CURRENT_TIME;
- js_data[minor].busy = JS_FALSE;
- MOD_DEC_USE_COUNT;
- return 0;
+ int t = ~inb(JS_PORT) & js_buttons_exist;
+ if ((js_last_buttons != t) && (js_fifo_head != js_fifo_tail)) {
+ js_fifo[js_fifo_head].event = js_last_buttons = t;
+ js_fifo[js_fifo_head].time = jiffies;
+ js_fifo_head++;
+ if (js_fifo_head == JS_FIFO_SIZE) js_fifo_head = 0;
+ if (!js_mark_time) {
+ js_mark_time = jiffies;
+ mark_bh(JS_BH);
+ }
+ }
+ else
+ if ((jiffies > js_axis_time + JS_AXIS_MAX_PERIOD) && !js_mark_time) {
+ js_mark_time = jiffies;
+ mark_bh(JS_BH);
+ }
+ js_timer.expires = jiffies + JS_BUTTON_PERIOD;
+ add_timer(&js_timer);
}
/*
- * js_read() reads the buttons x, and y axis from both joysticks if a
- * given interval has expired since the last read or is equal to
- * -1l. The buttons are in port 0x201 in the high nibble. The axis are
- * read by writing to 0x201 and then measuring the time it takes the
- * one shots to clear.
+ * js_do_bh() does the main processing and adds events to output buffers.
*/
-static long js_read (struct inode *inode, struct file *file, char *buf, unsigned long count)
+static void js_do_bh(void)
{
- int j, chk, jsmask;
- int t0, t_x0, t_y0, t_x1, t_y1;
- unsigned int minor, minor2;
- int buttons;
- if (count != JS_RETURN)
- return -EINVAL;
- minor = MINOR (inode->i_rdev);
- inode->i_atime = CURRENT_TIME;
- if (jiffies >= js_data[minor].js_expiretime)
- {
- minor2 = minor << 1;
- j = js_data[minor].js_timeout;
- for (; (js_exist & inb (JS_PORT)) && j; j--);
- if (j == 0)
- return -ENODEV; /*no joystick here*/
- /*Make sure no other proc is using port*/
+ int i, j, k;
+ unsigned int t;
+
+ if (jiffies > js_axis_time + JS_AXIS_MIN_PERIOD) {
+
+ unsigned int old_axis[4];
+ unsigned int t_low, t_high;
+ unsigned int flags, joy_state;
+ unsigned int t1l, t1h, jsm;
+ unsigned char jss;
+ unsigned char again;
+ unsigned char retries = 0;
+
+ for (i = 0; i < 4; i++)
+ old_axis[i] = js_axis[i].value;
+
+ do {
+ i = 0;
+ again = 0;
+ t_low = 0;
+ t_high = 0;
+ joy_state = JS_AXES;
+
+/*
+ * Measure the axes.
+ */
+
+ save_flags(flags);
+ cli(); /* no interrupts */
+ outb(JS_TRIGGER, JS_PORT); /* trigger one-shots */
+ outb(PIT_READ_TIMER, PIT_MODE); /* read timer */
+ t = (t1l = inb(PIT_DATA)) |
+ (t1h = inb(PIT_DATA)) << 8;
+ restore_flags(flags);
+
+ do {
+ jss = inb(JS_PORT);
+ if ((jss ^ joy_state) & js_axes_exist) {
+ t_low = (t_low << 8) | t1l;
+ t_high = (t_high << 8) | t1h;
+ joy_state = (joy_state << 8) | jss;
+ i++;
+ }
+
+ cli();
+ outb(PIT_READ_TIMER, PIT_MODE);
+ t1l = inb(PIT_DATA);
+ t1h = inb(PIT_DATA);
+ restore_flags(flags);
+
+ } while ((jss & js_axes_exist) && (DELTA_TX(t, t1l, t1h) < JS_MAXTIME));
+
+/*
+ * Process the gathered axis data in joy_state.
+ */
+
+ joy_state ^= ((joy_state >> 8) | 0xff000000L); /* More magic */
+
+ for (; i > 0; i--) {
+ for (j = 0; j < 4; j++)
+ if (joy_state & js_axes_exist & (1 << j)) {
+ jsm = js_correct(DELTA_TX(t, t_low, t_high), &js_axis[j].corr);
+ if (!js_compare(jsm, js_axis[j].value, js_axis[j].corr.prec)) {
+ if (jsm < js_axis[j].value || !retries)
+ js_axis[j].value = jsm;
+ again = 1;
+ }
+ }
+ joy_state = joy_state >> 8;
+ t_low = t_low >> 8;
+ t_high = t_high >> 8;
+ }
+
+ } while (retries++ < JS_RETRIES && again);
+
+/*
+ * Check if joystick lost.
+ */
+
+ for (i = 0; i < JS_NUM; i++) {
+
+ if (jsd[i].exist && ((jss & jsd[i].exist & JS_AXES) == (jsd[i].exist & JS_AXES))) {
+ printk(KERN_WARNING "js%d: joystick lost.\n", i);
+ js_buttons_exist &= ~jsd[i].exist;
+ js_axes_exist &= ~jsd[i].exist;
+ jsd[i].exist = 0;
+ wake_up_interruptible(&jsd[i].wait);
+ }
+
+ if ((jss & jsd[i].exist & JS_AXES)) {
+ printk(KERN_WARNING "js%d: joystick broken. Check cables.\n", i);
+ }
+
+ }
+
+/*
+ * Put changed axes into output buffer.
+ */
+
+ if (retries > 1)
+ for (i = 0; i < JS_NUM; i++)
+ if (jsd[i].list) {
+ k = 0;
+ for (j = 0; j < 4; j++)
+ if ((1 << j) & jsd[i].exist) {
+ if (!js_compare(js_axis[j].value, old_axis[j], js_axis[j].corr.prec)) {
+ jsd[i].buff[jsd[i].ahead].time = js_mark_time;
+ jsd[i].buff[jsd[i].ahead].type = JS_EVENT_AXIS;
+ jsd[i].buff[jsd[i].ahead].number = k;
+ jsd[i].buff[jsd[i].ahead].value = js_axis[j].value;
+ jsd[i].ahead++;
+ if (jsd[i].ahead == JS_BUFF_SIZE) jsd[i].ahead = 0;
+ }
+ k++;
+ }
+ }
+ js_axis_time = jiffies;
+ }
+ js_mark_time = 0;
+
+/*
+ * And now process the button fifo.
+ */
+
+ while (js_fifo_head != (t = GOFF(js_fifo_tail))) {
+ for (i = 0; i < JS_NUM; i++)
+ if (jsd[i].list) {
+ k = 0;
+ for (j = 4; j < 8; j++)
+ if ((1 << j) & jsd[i].exist) {
+ if ((1 << j) & (js_buttons ^ js_fifo[t].event)) {
+ jsd[i].buff[jsd[i].ahead].time = js_fifo[t].time;
+ jsd[i].buff[jsd[i].ahead].type = JS_EVENT_BUTTON;
+ jsd[i].buff[jsd[i].ahead].number = k;
+ jsd[i].buff[jsd[i].ahead].value = (js_fifo[t].event >> j) & 1;
+ jsd[i].ahead++;
+ if (jsd[i].ahead == JS_BUFF_SIZE) jsd[i].ahead = 0;
+ }
+ k++;
+ }
+ }
+ js_buttons = js_fifo[js_fifo_tail = t].event;
+ }
+
+/*
+ * Sync ahead with bhead and cut too long tails.
+ */
- cli();
- js_read_semaphore++;
- sti();
-
- buttons = ~(inb (JS_PORT) >> 4);
- js_data[0].js_save.buttons = buttons & 0x03;
- js_data[1].js_save.buttons = (buttons >> 2) & 0x03;
- j = js_data[minor].js_timeout;
- jsmask = 0;
-
- cli(); /*no interrupts!*/
- outb (0xff, JS_PORT); /*trigger one-shots*/
- /*get init timestamp*/
- t_x0 = t_y0 = t_x1 = t_y1 = t0 = get_timer0 ();
- /*wait for an axis' bit to clear or timeout*/
- while (j-- && (chk = (inb (JS_PORT) & js_exist ) | jsmask))
- {
- if (!(chk & JS_X_0)) {
- t_x0 = get_timer0();
- jsmask |= JS_X_0;
+ for (i = 0; i < JS_NUM; i++)
+ if (jsd[i].list)
+ if (jsd[i].bhead != jsd[i].ahead) {
+ if (ROT(jsd[i].bhead, jsd[i].tail, jsd[i].ahead) || (jsd[i].tail == jsd[i].bhead)) {
+ struct js_list *curl;
+ curl = jsd[i].list;
+ while (curl) {
+ if (ROT(jsd[i].bhead, curl->tail, jsd[i].ahead) || (curl->tail == jsd[i].bhead)) {
+ curl->tail = jsd[i].ahead;
+ curl->startup = jsd[i].exist;
+ }
+ curl = curl->next;
+ }
+ jsd[i].tail = jsd[i].ahead;
+ }
+ jsd[i].bhead = jsd[i].ahead;
+ wake_up_interruptible(&jsd[i].wait);
+ }
+
+}
+
+/*
+ * js_lseek() just returns with error.
+ */
+
+static loff_t js_lseek(struct file *file, loff_t offset, int origin)
+{
+ return -ESPIPE;
+}
+
+/*
+ * js_read() copies one or more entries from jsd[].buff to user
+ * space.
+ */
+
+static ssize_t js_read(struct file *file, char *buf, size_t count, loff_t *ppos)
+{
+ unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
+ struct wait_queue wait = { current, NULL };
+ struct js_list *curl = file->private_data;
+ struct js_event *buff = (void *) buf;
+ unsigned long blocks = count / sizeof(struct js_event);
+ unsigned long i = 0, j;
+ int t, u = curl->tail;
+ int retval = 0;
+
+/*
+ * Check user data.
+ */
+
+ if (MAJOR(file->f_dentry->d_inode->i_rdev) != JOYSTICK_MAJOR)
+ return -EINVAL;
+ if (file->f_pos < 0)
+ return -EINVAL;
+ if (!blocks)
+ return -EINVAL;
+ if (!curl)
+ return -EINVAL;
+
+ if (minor > JS_NUM)
+ return -ENODEV;
+ if (!jsd[minor].exist)
+ return -ENODEV;
+
+/*
+ * Handle (non)blocking i/o.
+ */
+
+ if (count != sizeof(struct JS_DATA_TYPE)) {
+
+ if ((GOF(curl->tail) == jsd[minor].ahead && !curl->startup) || (curl->startup && !js_axis_time)) {
+ add_wait_queue(&jsd[minor].wait, &wait);
+ current->state = TASK_INTERRUPTIBLE;
+ while ((GOF(curl->tail) == jsd[minor].ahead && !curl->startup) || (curl->startup && !js_axis_time)) {
+ if (file->f_flags & O_NONBLOCK) {
+ retval = -EAGAIN;
+ break;
+ }
+ if (current->signal & ~current->blocked) {
+ retval = -ERESTARTSYS;
+ break;
+ }
+ schedule();
+ if (!jsd[minor].exist) {
+ retval = -ENODEV;
+ break;
+ }
}
- if (!(chk & JS_Y_0)) {
- t_y0 = get_timer0();
- jsmask |= JS_Y_0;
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&jsd[minor].wait, &wait);
+ }
+
+ if (retval) return retval;
+
+/*
+ * Do the i/o.
+ */
+
+ if (curl->startup) {
+ struct js_event tmpevent;
+
+ t = 0;
+ for (j = 0; j < 4 && (i < blocks) && !retval; j++)
+ if (jsd[minor].exist & (1 << j)) {
+ if (curl->startup & (1 << j)) {
+ tmpevent.type = JS_EVENT_AXIS | JS_EVENT_INIT;
+ tmpevent.number = t;
+ tmpevent.value = js_axis[j].value;
+ if (copy_to_user(&buff[i], &tmpevent, sizeof(struct js_event)))
+ retval = -EFAULT;
+ if (put_user((__u32)((jiffies - curl->time) * (1000/HZ)), &buff[i].time))
+ retval = -EFAULT;
+ curl->startup &= ~(1 << j);
+ i++;
+ }
+ t++;
}
- if (!(chk & JS_X_1)) {
- t_x1 = get_timer0();
- jsmask |= JS_X_1;
+
+ t = 0;
+ for (j = 4; j < 8 && (i < blocks) && !retval; j++)
+ if (jsd[minor].exist & (1 << j)) {
+ if (curl->startup & (1 << j)) {
+ tmpevent.type = JS_EVENT_BUTTON | JS_EVENT_INIT;
+ tmpevent.number = t;
+ tmpevent.value = (js_buttons >> j) & 1;
+ if (copy_to_user(&buff[i], &tmpevent, sizeof(struct js_event)))
+ retval = -EFAULT;
+ if (put_user((__u32)((jiffies - curl->time) * (1000/HZ)), &buff[i].time))
+ retval = -EFAULT;
+ curl->startup &= ~(1 << j);
+ i++;
+ }
+ t++;
}
- if (!(chk & JS_Y_1)) {
- t_y1 = get_timer0();
- jsmask |= JS_Y_1;
+ }
+
+
+ while ((jsd[minor].ahead != (t = GOF(curl->tail))) && (i < blocks) && !retval) {
+ if (copy_to_user(&buff[i], &jsd[minor].buff[t], sizeof(struct js_event)))
+ retval = -EFAULT;
+ if (put_user((__u32)((jsd[minor].buff[t].time - curl->time) * (1000/HZ)), &buff[i].time))
+ retval = -EFAULT;
+ curl->tail = t;
+ i++;
+ }
+
+ }
+
+ else
+
+/*
+ * Handle version 0.x compatibility.
+ */
+
+ {
+ struct JS_DATA_TYPE *bufo = (void *) buf;
+ int buttons = 0;
+
+ while (~jsd[minor].exist & (1<<i)) i++;
+ copy_to_user(&bufo->x, &js_axis[i].value, sizeof(int));
+
+ i++;
+ while (~jsd[minor].exist & (1<<i)) i++;
+ copy_to_user(&bufo->y, &js_axis[i].value, sizeof(int));
+
+ i = 0;
+ for (j = 4; j < 8; j++)
+ if ((1 << j) & jsd[minor].exist)
+ buttons |= (!!(js_last_buttons & (1 << j))) << (i++);
+ copy_to_user(&bufo->buttons, &buttons, sizeof(int));
+
+ curl->tail = GOB(jsd[minor].ahead);
+ retval = sizeof(struct JS_DATA_TYPE);
+ }
+
+/*
+ * Check main tail and move it.
+ */
+
+ if (u == jsd[minor].tail) {
+ t = curl->tail;
+ curl = jsd[minor].list;
+ while (curl && curl->tail != jsd[minor].tail) {
+ if (ROT(jsd[minor].ahead, t, curl->tail) ||
+ (jsd[minor].ahead == curl->tail)) t = curl->tail;
+ curl = curl->next;
+ }
+ if (!curl) jsd[minor].tail = t;
+ }
+
+ return retval ? retval : i*sizeof(struct js_event);
+}
+
+/*
+ * js_poll() does select() support.
+ */
+
+static unsigned int js_poll(struct file *file, poll_table *wait)
+{
+ struct js_list *curl;
+ unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
+ curl = file->private_data;
+
+ poll_wait(&jsd[minor].wait, wait);
+ if (GOF(curl->tail) != jsd[minor].ahead)
+ return POLLIN | POLLRDNORM;
+ return 0;
+}
+
+/*
+ * js_ioctl handles misc ioctl calls.
+ */
+
+static int js_ioctl(struct inode *inode,
+ struct file *file,
+ unsigned int cmd,
+ unsigned long arg)
+{
+ unsigned int minor = MINOR(inode->i_rdev);
+ int i, j;
+
+ if (MAJOR(inode->i_rdev) != JOYSTICK_MAJOR)
+ return -EINVAL;
+ if (minor > JS_NUM)
+ return -ENODEV;
+ if (!jsd[minor].exist)
+ return -ENODEV;
+
+ switch (cmd) {
+ case JSIOCGVERSION:
+ if(put_user(JS_VERSION, (__u32 *) arg)) return -EFAULT;
+ break;
+ case JSIOCGAXES:
+ if(put_user(count_bits(jsd[minor].exist & JS_AXES), (__u8 *) arg)) return -EFAULT;
+ break;
+ case JSIOCGBUTTONS:
+ if(put_user(count_bits(jsd[minor].exist & JS_BUTTONS), (__u8 *) arg)) return -EFAULT;
+ break;
+ case JSIOCSCORR:
+ j = 0;
+ for (i = 0; i < 4; i++)
+ if ((1 << i) & jsd[minor].exist) {
+ if (copy_from_user(&js_axis[i].corr, (void *) arg + j * sizeof(struct js_corr),
+ sizeof(struct js_corr))) return -EFAULT;
+ j++;
+ }
+ js_axis_time = 0;
+ break;
+ case JSIOCGCORR:
+ j = 0;
+ for (i = 0; i < 4; i++)
+ if ((1 << i) & jsd[minor].exist) {
+ if (copy_to_user((void *) arg + j * sizeof(struct js_corr), &js_axis[i].corr,
+ sizeof(struct js_corr))) return -EFAULT;
+ j++;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * js_open() performs necessary initialization and adds
+ * an entry to the linked list.
+ */
+
+static int js_open(struct inode *inode, struct file *file)
+{
+ unsigned int minor = MINOR(inode->i_rdev);
+ struct js_list *curl;
+ int t;
+
+ if (MAJOR(inode->i_rdev) != JOYSTICK_MAJOR)
+ return -EINVAL;
+ if (minor > JS_NUM)
+ return -ENODEV;
+ if (!jsd[minor].exist) {
+ js_probe();
+ if (jsd[minor].exist) printk(KERN_INFO "js%d: %d-axis joystick at %#x\n",
+ minor, count_bits(jsd[minor].exist & JS_AXES), JS_PORT);
+ else return -ENODEV;
+ }
+
+ MOD_INC_USE_COUNT;
+
+ if (!jsd[0].list && !jsd[1].list) {
+ js_timer.expires = jiffies + JS_BUTTON_PERIOD;
+ add_timer(&js_timer);
+ }
+
+ curl = jsd[minor].list;
+ jsd[minor].list = kmalloc(sizeof(struct js_list), GFP_KERNEL);
+ jsd[minor].list->next = curl;
+ jsd[minor].list->startup = jsd[minor].exist;
+ jsd[minor].list->tail = t = GOB(jsd[minor].ahead);
+ jsd[minor].list->time = jiffies;
+
+ file->private_data = jsd[minor].list;
+
+ return 0;
+}
+
+/*
+ * js_release() removes an entry from list and deallocates memory
+ * used by it.
+ */
+
+static int js_release(struct inode *inode, struct file *file)
+{
+ unsigned int minor = MINOR(inode->i_rdev);
+ struct js_list **curp, *curl;
+ int t;
+
+ curp = &jsd[minor].list;
+ curl = file->private_data;
+
+ while (*curp && (*curp != curl)) curp = &((*curp)->next);
+ *curp = (*curp)->next;
+
+ if (jsd[minor].list) {
+ if (curl->tail == jsd[minor].tail) {
+ curl = jsd[minor].list;
+ t = curl->tail;
+ while (curl && curl->tail != jsd[minor].tail) {
+ if (ROT(jsd[minor].ahead, t, curl->tail) ||
+ (jsd[minor].ahead == curl->tail)) t = curl->tail;
+ curl = curl->next;
}
+ if (!curl) jsd[minor].tail = t;
}
- sti(); /* allow interrupts */
-
- js_read_semaphore = 0; /* allow other reads to progress */
- if (j == 0)
- return -ENODEV; /*read timed out*/
- js_data[0].js_expiretime = jiffies +
- js_data[0].js_timelimit; /*update data*/
- js_data[1].js_expiretime = jiffies +
- js_data[1].js_timelimit;
- js_data[0].js_save.x = DELTA_TIME (t0, t_x0) >>
- js_data[0].js_corr.x;
- js_data[0].js_save.y = DELTA_TIME (t0, t_y0) >>
- js_data[0].js_corr.y;
- js_data[1].js_save.x = DELTA_TIME (t0, t_x1) >>
- js_data[1].js_corr.x;
- js_data[1].js_save.y = DELTA_TIME (t0, t_y1) >>
- js_data[1].js_corr.y;
}
- if(copy_to_user(buf, &js_data[minor].js_save, JS_RETURN))
- return -EFAULT;
- return JS_RETURN;
+ kfree(file->private_data);
+ if (!jsd[0].list && !jsd[1].list) del_timer(&js_timer);
+
+ MOD_DEC_USE_COUNT;
+ return 0;
}
+/*
+ * The operations structure.
+ */
static struct file_operations js_fops =
{
- NULL, /* js_lseek*/
+ js_lseek, /* js_lseek */
js_read, /* js_read */
- NULL, /* js_write*/
- NULL, /* js_readaddr*/
- NULL, /* js_select */
- js_ioctl, /* js_ioctl*/
+ NULL, /* js_write */
+ NULL, /* js_readdir */
+ js_poll, /* js_poll */
+ js_ioctl, /* js_ioctl */
NULL, /* js_mmap */
- js_open, /* js_open*/
- js_release, /* js_release*/
- NULL /* js_fsync */
+ js_open, /* js_open */
+ js_release, /* js_release */
+ NULL /* js_sync */
};
-#ifdef MODULE
+/*
+ * js_setup() parses kernel command line parametres.
+ */
-#define joystick_init init_module
+#ifndef MODULE
+__initfunc(void js_setup(char *str, int *ints))
-void cleanup_module (void)
{
- if (unregister_chrdev (JOYSTICK_MAJOR, "joystick"))
- printk ("joystick: cleanup_module failed\n");
- release_region(JS_PORT, 1);
+ js[0] = ((ints[0] > 0) ? ints[1] : 0 );
+ js[1] = ((ints[0] > 1) ? ints[2] : 0 );
}
+#endif
-#endif /* MODULE */
+/*
+ * js_init() registres the driver and calls the probe function.
+ * also initializes some crucial variables.
+ */
-int joystick_init(void)
+#ifdef MODULE
+int init_module(void)
+#else
+__initfunc(int js_init(void))
+#endif
{
- int js_num;
- int js_count;
+ int i;
if (check_region(JS_PORT, 1)) {
- printk("js_init: port already in use\n");
+ printk(KERN_ERR "js: port %#x already in use\n", JS_PORT);
return -EBUSY;
}
- js_num = find_axes();
- js_count = !!(js_num & 0x3) + !!(js_num & 0xC);
-
-
- if (js_count == 0)
- {
- printk("No joysticks found.\n");
+ if (js_probe() < 0) {
+ printk(KERN_INFO "js: no joysticks found\n");
return -ENODEV;
- /* if the user boots the machine, which runs insmod, and THEN
- decides to hook up the joystick, well, then we do the wrong
- thing. But it's a good idea to avoid giving out a false sense
- of security by letting the module load otherwise. */
}
- if (register_chrdev (JOYSTICK_MAJOR, "joystick", &js_fops)) {
- printk ("Unable to get major=%d for joystick\n",
- JOYSTICK_MAJOR);
+ if (register_chrdev(JOYSTICK_MAJOR, "js", &js_fops)) {
+ printk(KERN_ERR "js: unable to get major %d for joystick\n", JOYSTICK_MAJOR);
return -EBUSY;
}
- request_region(JS_PORT, 1, "joystick");
-
- for (js_num = 0; js_num < JS_MAX; js_num++)
- js_data[js_num].busy = JS_FALSE;
- js_read_semaphore = 0;
- printk (KERN_INFO "Found %d joystick%c.\n",
- js_count,
- (js_num == 1) ? ' ' : 's');
+ for (i = 0; i < JS_NUM; i++) {
+ if (jsd[i].exist) printk(KERN_INFO "js%d: %d-axis joystick at %#x\n",
+ i, count_bits(jsd[i].exist & JS_AXES), JS_PORT);
+ jsd[i].ahead = jsd[i].bhead = 0;
+ jsd[i].tail = JS_BUFF_SIZE - 1;
+ jsd[i].list = NULL;
+ jsd[i].wait = NULL;
+ memset(jsd[i].buff, 0, JS_BUFF_SIZE * sizeof(struct js_event));
+ }
+
+ for (i = 0; i < 4; i++) {
+ js_axis[i].corr.type = JS_CORR_NONE;
+ js_axis[i].corr.prec = JS_DEF_PREC;
+ }
+
+ request_region(JS_PORT, 1, "js");
+ init_bh(JS_BH, &js_do_bh);
+ enable_bh(JS_BH);
+ init_timer(&js_timer);
+ js_timer.function = js_do_timer;
+
return 0;
}
+/*
+ * cleanup_module() handles module removal.
+ */
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ if (MOD_IN_USE)
+ printk(KERN_NOTICE "js: device busy, remove delayed\n");
+ else {
+ del_timer(&js_timer);
+ disable_bh(JS_BH);
+ if (unregister_chrdev(JOYSTICK_MAJOR, "js"))
+ printk(KERN_ERR "js: module cleanup failed\n");
+ release_region(JS_PORT, 1);
+ }
+}
+#endif
diff --git a/drivers/char/lp.c b/drivers/char/lp.c
index 49a6c65db..fdfb961c7 100644
--- a/drivers/char/lp.c
+++ b/drivers/char/lp.c
@@ -11,6 +11,9 @@
* lp_read (Status readback) support added by Carsten Gross,
* carsten@sol.wohnheim.uni-ulm.de
* Support for parport by Philip Blundell <Philip.Blundell@pobox.com>
+ * Reverted interrupt to polling at runtime if more than one device is parport
+ * registered and joined the interrupt and polling code.
+ * by Andrea Arcangeli <arcangeli@mbox.queen.it>
*/
/* This driver is about due for a rewrite. */
@@ -251,7 +254,7 @@ static inline int lp_write_buf(unsigned int minor, const char *buf, int count)
sti();
}
- if (current->signal & ~current->blocked) {
+ if (signal_pending(current)) {
if (total_bytes_written + bytes_written)
return total_bytes_written + bytes_written;
else
@@ -269,9 +272,9 @@ static inline int lp_write_buf(unsigned int minor, const char *buf, int count)
return total_bytes_written;
}
-static long lp_write(struct inode * inode, struct file * file,
- const char * buf, unsigned long count)
+static ssize_t lp_write(struct file * file, const char * buf, size_t count, loff_t *ppos)
{
+ struct inode *inode = file->f_dentry->d_inode;
unsigned int minor = MINOR(inode->i_rdev);
int retv;
@@ -312,9 +315,9 @@ static void lp_select_in_high(int minor) {
}
/* Status readback confirming to ieee1284 */
-static long lp_read(struct inode * inode, struct file * file,
- char * buf, unsigned long count)
+static ssize_t lp_read(struct file * file, char * buf, size_t count, loff_t *ppos)
{
+ struct inode *inode = file->f_dentry->d_inode;
unsigned char z=0, Byte=0, status;
char *temp;
int retval;
@@ -376,7 +379,7 @@ static long lp_read(struct inode * inode, struct file * file,
#ifdef LP_READ_DEBUG
printk(KERN_DEBUG "lp_read: (Autofeed low) timeout\n");
#endif
- if (current->signal & ~current->blocked) {
+ if (signal_pending(current)) {
lp_select_in_high(minor);
parport_release(lp_table[minor].dev);
if (temp !=buf)
diff --git a/drivers/char/lp_m68k.c b/drivers/char/lp_m68k.c
index bdb3405ca..a36903c7a 100644
--- a/drivers/char/lp_m68k.c
+++ b/drivers/char/lp_m68k.c
@@ -222,7 +222,7 @@ static long lp_write_interrupt(struct inode *inode, struct file *file,
lp_table[dev]->do_print = 0;
rc = total_bytes_written + lp_table[dev]->bytes_written;
- if (current->signal & ~current->blocked) {
+ if (signal_pending(current)) {
if (rc)
return rc;
else
@@ -320,7 +320,7 @@ static long lp_write_polled(struct inode *inode, struct file *file,
}
/* check for signals before going to sleep */
- if (current->signal & ~current->blocked) {
+ if (signal_pending(current)) {
if (temp != buf)
return temp-buf;
else
diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index 94c207e10..177862c2a 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -36,11 +36,10 @@ void isdn_init(void);
void pcwatchdog_init(void);
#endif
-static long do_write_mem(struct file * file,
- void *p, unsigned long realp,
- const char * buf, unsigned long count)
+static ssize_t do_write_mem(struct file * file, void *p, unsigned long realp,
+ const char * buf, size_t count, loff_t *ppos)
{
- unsigned long written;
+ ssize_t written;
written = 0;
#if defined(__sparc__) || defined(__mc68000__)
@@ -58,8 +57,8 @@ static long do_write_mem(struct file * file,
if (copy_from_user(p, buf, count) < 0)
return -EFAULT;
written += count;
- file->f_pos += written;
- return count;
+ *ppos += written;
+ return written;
}
@@ -67,12 +66,12 @@ static long do_write_mem(struct file * file,
* This funcion reads the *physical* memory. The f_pos points directly to the
* memory location.
*/
-static long read_mem(struct inode * inode, struct file * file,
- char * buf, unsigned long count)
+static ssize_t read_mem(struct file * file, char * buf,
+ size_t count, loff_t *ppos)
{
- unsigned long p = file->f_pos;
+ unsigned long p = *ppos;
unsigned long end_mem;
- unsigned long read;
+ ssize_t read;
end_mem = __pa(high_memory);
if (p >= end_mem)
@@ -99,14 +98,14 @@ static long read_mem(struct inode * inode, struct file * file,
if (copy_to_user(buf, __va(p), count) < 0)
return -EFAULT;
read += count;
- file->f_pos += read;
+ *ppos += read;
return read;
}
-static long write_mem(struct inode * inode, struct file * file,
- const char * buf, unsigned long count)
+static ssize_t write_mem(struct file * file, const char * buf,
+ size_t count, loff_t *ppos)
{
- unsigned long p = file->f_pos;
+ unsigned long p = *ppos;
unsigned long end_mem;
end_mem = __pa(high_memory);
@@ -114,7 +113,7 @@ static long write_mem(struct inode * inode, struct file * file,
return 0;
if (count > end_mem - p)
count = end_mem - p;
- return do_write_mem(file,__va(p),p,buf,count);
+ return do_write_mem(file, __va(p), p, buf, count, ppos);
}
static int mmap_mem(struct file * file, struct vm_area_struct * vma)
@@ -135,9 +134,10 @@ static int mmap_mem(struct file * file, struct vm_area_struct * vma)
#endif
#ifdef __powerpc__
if (offset >= __pa(high_memory))
- pgprot_val(vma->vm_page_prot) |= _PAGE_NO_CACHE | _PAGE_GUARDED;
+ pgprot_val(vma->vm_page_prot) |= _PAGE_NO_CACHE|_PAGE_GUARDED;
#endif
- if (remap_page_range(vma->vm_start, offset, vma->vm_end - vma->vm_start, vma->vm_page_prot))
+ if (remap_page_range(vma->vm_start, offset, vma->vm_end-vma->vm_start,
+ vma->vm_page_prot))
return -EAGAIN;
vma->vm_dentry = dget(file->f_dentry);
return 0;
@@ -146,61 +146,63 @@ static int mmap_mem(struct file * file, struct vm_area_struct * vma)
/*
* This function reads the *virtual* memory as seen by the kernel.
*/
-static long read_kmem(struct inode *inode, struct file *file,
- char *buf, unsigned long count)
+static ssize_t read_kmem(struct file *file, char *buf,
+ size_t count, loff_t *ppos)
{
- unsigned long p = file->f_pos;
- unsigned long read = 0;
- long virtr;
+ unsigned long p = *ppos;
+ ssize_t read = 0;
+ ssize_t virtr;
if (p < (unsigned long) high_memory) {
- unsigned long tmp;
-
+ read = count;
if (count > (unsigned long) high_memory - p)
- tmp = (unsigned long) high_memory - p;
- else
- tmp = count;
- read = tmp;
+ read = (unsigned long) high_memory - p;
+
#if defined(__sparc__) || defined(__mc68000__)
/* we don't have page 0 mapped on sparc and m68k.. */
- while (p < PAGE_SIZE && tmp > 0) {
- put_user(0,buf);
- buf++;
- p++;
- tmp--;
+ if (p < PAGE_SIZE && read > 0) {
+ size_t tmp = PAGE_SIZE - p;
+ if (tmp > read) tmp = read;
+ clear_user(buf, tmp);
+ buf += tmp;
+ p += tmp;
+ read -= tmp;
+ count -= tmp;
}
#endif
- copy_to_user(buf, (char *) p, tmp);
- buf += tmp;
+ copy_to_user(buf, (char *)p, read);
+ p += read;
+ buf += read;
+ count -= read;
}
- virtr = vread(buf, (char *) (unsigned long) file->f_pos, count - read);
+ virtr = vread(buf, (char *)p, count);
if (virtr < 0)
return virtr;
- file->f_pos += virtr + read;
+ *ppos += p + virtr;
return virtr + read;
}
/*
* This function writes to the *virtual* memory as seen by the kernel.
*/
-static long write_kmem(struct inode * inode, struct file * file,
- const char * buf, unsigned long count)
+static ssize_t write_kmem(struct file * file, const char * buf,
+ size_t count, loff_t *ppos)
{
- unsigned long p = file->f_pos;
+ unsigned long p = *ppos;
if (p >= (unsigned long) high_memory)
return 0;
if (count > (unsigned long) high_memory - p)
count = (unsigned long) high_memory - p;
- return do_write_mem(file,(void*)p,p,buf,count);
+ return do_write_mem(file, (void*)p, p, buf, count, ppos);
}
-static long read_port(struct inode * inode, struct file * file,
- char * buf, unsigned long count)
+static ssize_t read_port(struct file * file, char * buf,
+ size_t count, loff_t *ppos)
{
- unsigned int i = file->f_pos;
- char * tmp = buf;
+ unsigned long i = *ppos;
+ char *tmp = buf;
if (verify_area(VERIFY_WRITE,buf,count))
return -EFAULT;
@@ -210,14 +212,14 @@ static long read_port(struct inode * inode, struct file * file,
i++;
tmp++;
}
- file->f_pos = i;
+ *ppos = i;
return tmp-buf;
}
-static long write_port(struct inode * inode, struct file * file,
- const char * buf, unsigned long count)
+static ssize_t write_port(struct file * file, const char * buf,
+ size_t count, loff_t *ppos)
{
- unsigned int i = file->f_pos;
+ unsigned long i = *ppos;
const char * tmp = buf;
if (verify_area(VERIFY_READ,buf,count))
@@ -230,18 +232,18 @@ static long write_port(struct inode * inode, struct file * file,
i++;
tmp++;
}
- file->f_pos = i;
+ *ppos = i;
return tmp-buf;
}
-static long read_null(struct inode * node, struct file * file,
- char * buf, unsigned long count)
+static ssize_t read_null(struct file * file, char * buf,
+ size_t count, loff_t *ppos)
{
return 0;
}
-static long write_null(struct inode * inode, struct file * file,
- const char * buf, unsigned long count)
+static ssize_t write_null(struct file * file, const char * buf,
+ size_t count, loff_t *ppos)
{
return count;
}
@@ -249,7 +251,7 @@ static long write_null(struct inode * inode, struct file * file,
/*
* For fun, we are using the MMU for this.
*/
-static inline unsigned long read_zero_pagealigned(char * buf, unsigned long size)
+static inline size_t read_zero_pagealigned(char * buf, size_t size)
{
struct vm_area_struct * vma;
unsigned long addr=(unsigned long)buf;
@@ -292,8 +294,8 @@ static inline unsigned long read_zero_pagealigned(char * buf, unsigned long size
return size;
}
-static long read_zero(struct inode * node, struct file * file,
- char * buf, unsigned long count)
+static ssize_t read_zero(struct file * file, char * buf,
+ size_t count, loff_t *ppos)
{
unsigned long left, unwritten, written = 0;
@@ -339,21 +341,23 @@ static int mmap_zero(struct file * file, struct vm_area_struct * vma)
return 0;
}
-static long write_full(struct inode * inode, struct file * file,
- const char * buf, unsigned long count)
+static ssize_t write_full(struct file * file, const char * buf,
+ size_t count, loff_t *ppos)
{
return -ENOSPC;
}
/*
- * Special lseek() function for /dev/null and /dev/zero. Most notably, you can fopen()
- * both devices with "a" now. This was previously impossible. SRB.
+ * Special lseek() function for /dev/null and /dev/zero. Most notably, you
+ * can fopen() both devices with "a" now. This was previously impossible.
+ * -- SRB.
*/
-static long long null_lseek(struct file * file, long long offset, int orig)
+static loff_t null_lseek(struct file * file, loff_t offset, int orig)
{
- return file->f_pos=0;
+ return file->f_pos = 0;
}
+
/*
* The memory devices use the full 32/64 bits of the offset, and so we cannot
* check against negative addresses: they are ok. The return value is weird,
@@ -362,7 +366,7 @@ static long long null_lseek(struct file * file, long long offset, int orig)
* also note that seeking relative to the "end of file" isn't supported:
* it has no meaning, so it returns -EINVAL.
*/
-static long long memory_lseek(struct file * file, long long offset, int orig)
+static loff_t memory_lseek(struct file * file, loff_t offset, int orig)
{
switch (orig) {
case 0:
diff --git a/drivers/char/misc.c b/drivers/char/misc.c
index 826863ec0..e1819d9f2 100644
--- a/drivers/char/misc.c
+++ b/drivers/char/misc.c
@@ -78,6 +78,7 @@ extern void pcwatchdog_init(void);
extern int rtc_init(void);
extern int dsp56k_init(void);
extern int nvram_init(void);
+extern int radio_init(void);
extern void hfmodem_init(void);
#ifdef CONFIG_PROC_FS
@@ -117,7 +118,7 @@ static int misc_open(struct inode * inode, struct file * file)
return -ENODEV;
}
- if ((file->f_op = c->fops))
+ if ((file->f_op = c->fops) && file->f_op->open)
return file->f_op->open(inode,file);
else
return -ENODEV;
@@ -259,6 +260,9 @@ __initfunc(int misc_init(void))
#ifdef CONFIG_NVRAM
nvram_init();
#endif
+#ifdef CONFIG_MISC_RADIO
+ radio_init();
+#endif
#ifdef CONFIG_HFMODEM
hfmodem_init();
#endif
diff --git a/drivers/char/msbusmouse.c b/drivers/char/msbusmouse.c
index a8c32ce6d..75b7e7f32 100644
--- a/drivers/char/msbusmouse.c
+++ b/drivers/char/msbusmouse.c
@@ -129,15 +129,14 @@ static int open_mouse(struct inode * inode, struct file * file)
return 0;
}
-
-static long write_mouse(struct inode * inode, struct file * file,
- const char * buffer, unsigned long count)
+static ssize_t write_mouse(struct file * file,
+ const char * buffer, size_t count, loff_t *ppos)
{
return -EINVAL;
}
-static long read_mouse(struct inode * inode, struct file * file,
- char * buffer, unsigned long count)
+static ssize_t read_mouse(struct file * file,
+ char * buffer, size_t count, loff_t *ppos)
{
int i, dx, dy;
diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c
index 32dc3a51d..3581b81c3 100644
--- a/drivers/char/n_tty.c
+++ b/drivers/char/n_tty.c
@@ -88,9 +88,8 @@ void n_tty_flush_buffer(struct tty_struct * tty)
/*
* Return number of characters buffered to be delivered to user
- *
*/
-int n_tty_chars_in_buffer(struct tty_struct *tty)
+ssize_t n_tty_chars_in_buffer(struct tty_struct *tty)
{
if (tty->icanon) {
if (!tty->canon_data) return 0;
@@ -817,10 +816,10 @@ static inline int input_available_p(struct tty_struct *tty, int amt)
*/
static inline void copy_from_read_buf(struct tty_struct *tty,
unsigned char **b,
- unsigned int *nr)
+ size_t *nr)
{
- int n;
+ ssize_t n;
n = MIN(*nr, MIN(tty->read_cnt, N_TTY_BUF_SIZE - tty->read_tail));
if (!n)
@@ -832,15 +831,15 @@ static inline void copy_from_read_buf(struct tty_struct *tty,
*nr -= n;
}
-static int read_chan(struct tty_struct *tty, struct file *file,
- unsigned char *buf, unsigned int nr)
+static ssize_t read_chan(struct tty_struct *tty, struct file *file,
+ unsigned char *buf, size_t nr)
{
struct wait_queue wait = { current, NULL };
int c;
unsigned char *b = buf;
int minimum, time;
- int retval = 0;
- int size;
+ ssize_t retval = 0;
+ ssize_t size;
do_it_again:
@@ -924,7 +923,7 @@ do_it_again:
retval = -EAGAIN;
break;
}
- if (current->signal & ~current->blocked) {
+ if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
@@ -1004,13 +1003,13 @@ do_it_again:
return (size ? size : retval);
}
-static int write_chan(struct tty_struct * tty, struct file * file,
- const unsigned char * buf, unsigned int nr)
+static ssize_t write_chan(struct tty_struct * tty, struct file * file,
+ const unsigned char * buf, size_t nr)
{
struct wait_queue wait = { current, NULL };
- int c, num;
+ int c;
const unsigned char *b = buf;
- int retval = 0;
+ ssize_t retval = 0, num;
/* Job control check -- must be done at start (POSIX.1 7.1.1.4). */
if (L_TOSTOP(tty) && file->f_dentry->d_inode->i_rdev != CONSOLE_DEV) {
@@ -1022,7 +1021,7 @@ static int write_chan(struct tty_struct * tty, struct file * file,
add_wait_queue(&tty->write_wait, &wait);
while (1) {
current->state = TASK_INTERRUPTIBLE;
- if (current->signal & ~current->blocked) {
+ if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
diff --git a/drivers/char/nvram.c b/drivers/char/nvram.c
index 402981749..329ac7b32 100644
--- a/drivers/char/nvram.c
+++ b/drivers/char/nvram.c
@@ -227,11 +227,11 @@ static long long nvram_llseek(struct file *file,loff_t offset, int origin )
return( (offset >= 0) ? (file->f_pos = offset) : -EINVAL );
}
-static long nvram_read( struct inode * inode, struct file * file,
- char * buf, unsigned long count )
+static ssize_t nvram_read( struct file * file,
+ char * buf, size_t count, loff_t *ppos )
{
unsigned long flags;
- unsigned i = file->f_pos;
+ unsigned i = *ppos;
char *tmp = buf;
save_flags(flags);
@@ -244,17 +244,16 @@ static long nvram_read( struct inode * inode, struct file * file,
for( ; count-- > 0 && i < NVRAM_BYTES; ++i, ++tmp )
put_user( nvram_read_int(i), tmp );
- file->f_pos = i;
+ *ppos = i;
restore_flags(flags);
return( tmp - buf );
}
-static long nvram_write( struct inode * inode, struct file * file,
- const char * buf, unsigned long count )
+static ssize_t nvram_write( struct file * file, const char * buf, size_t count, loff_t *ppos )
{
unsigned long flags;
- unsigned i = file->f_pos;
+ unsigned i = *ppos;
const char *tmp = buf;
char c;
@@ -271,7 +270,7 @@ static long nvram_write( struct inode * inode, struct file * file,
nvram_write_int( c, i );
}
nvram_set_checksum_int();
- file->f_pos = i;
+ *ppos = i;
restore_flags(flags);
return( tmp - buf );
diff --git a/drivers/char/pc110pad.c b/drivers/char/pc110pad.c
index 71b75deb2..c2ed5482a 100644
--- a/drivers/char/pc110pad.c
+++ b/drivers/char/pc110pad.c
@@ -13,6 +13,7 @@
* 0.1 1997-05-19 Robin O'Leary <robin@acm.org> - PS/2 emulation
* 0.2 1997-06-03 Robin O'Leary <robin@acm.org> - tap gesture
* 0.3 1997-06-27 Alan Cox <alan@cymru.net> - 2.1 commit
+ * 0.4 1997-11-09 Alan Cox <alan@cymru.net> - Single Unix VFS API changes
*/
#include <linux/config.h>
@@ -532,7 +533,7 @@ static int open_pad(struct inode * inode, struct file * file)
/*
* writes are disallowed
*/
-static long write_pad(struct inode * inode, struct file * file, const char * buffer, unsigned long count)
+static ssize_t write_pad(struct file * file, const char * buffer, size_t count, loff_t *ppos)
{
return -EINVAL;
}
@@ -553,7 +554,7 @@ void new_sample(int d[3])
/*
* Read pad data. Currently never blocks.
*/
-static long read_pad(struct inode * inode, struct file * file, char * buffer, unsigned long count)
+static ssize_t read_pad(struct file * file, char * buffer, size_t count, loff_t *ppos)
{
int r;
diff --git a/drivers/char/pcwd.c b/drivers/char/pcwd.c
index 2f8a5a75b..213306349 100644
--- a/drivers/char/pcwd.c
+++ b/drivers/char/pcwd.c
@@ -381,13 +381,12 @@ static int pcwd_open(struct inode *ino, struct file *filep)
return(0);
}
-static long pcwd_read(struct inode *inode, struct file *file, char *buf,
- unsigned long count)
+static ssize_t pcwd_read(struct file *file, char *buf, size_t count, loff_t *ppos)
{
unsigned short c = inb(current_readport);
unsigned char cp;
- switch(MINOR(inode->i_rdev))
+ switch(MINOR(file->f_dentry->d_inode->i_rdev))
{
case TEMP_MINOR:
cp = c;
diff --git a/drivers/char/pcxx.c b/drivers/char/pcxx.c
index 9a68bb472..eecdaee5a 100644
--- a/drivers/char/pcxx.c
+++ b/drivers/char/pcxx.c
@@ -368,7 +368,7 @@ static int pcxx_waitcarrier(struct tty_struct *tty,struct file *filp,struct chan
(info->asyncflags & ASYNC_CLOSING) == 0 &&
(do_clocal || (info->imodem & info->dcd)))
break;
- if(current->signal & ~current->blocked) {
+ if(signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
diff --git a/drivers/char/pms.c b/drivers/char/pms.c
new file mode 100644
index 000000000..78682a353
--- /dev/null
+++ b/drivers/char/pms.c
@@ -0,0 +1,1069 @@
+/*
+ * Media Vision Pro Movie Studio
+ * or
+ * "all you need is an I2C bus some RAM and a prayer"
+ *
+ * This draws heavily on code
+ *
+ * (c) Wolfgang Koehler, wolf@first.gmd.de, Dec. 1994
+ * Kiefernring 15
+ * 14478 Potsdam, Germany
+ *
+ * Most of this code is directly derived from his userspace driver.
+ * His driver works so send any reports to alan@cymru.net unless the
+ * userspace driver also doesnt work for you...
+ */
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include <linux/sched.h>
+#include <linux/videodev.h>
+#include <linux/version.h>
+#include <asm/uaccess.h>
+
+
+#define MOTOROLA 1
+#define PHILIPS2 2
+#define PHILIPS1 3
+#define MVVMEMORYWIDTH 0x40 /* 512 bytes */
+
+struct pms_device
+{
+ struct video_device v;
+ struct video_picture picture;
+ int height;
+ int width;
+};
+
+struct i2c_info
+{
+ u8 slave;
+ u8 sub;
+ u8 data;
+ u8 hits;
+};
+
+static int i2c_count = 0;
+static struct i2c_info i2cinfo[64];
+
+static int decoder = PHILIPS2;
+static int standard = 0; /* 0 - auto 1 - ntsc 2 - pal 3 - secam */
+
+/*
+ * I/O ports and Shared Memory
+ */
+
+static int io_port = 0x250;
+static int data_port = 0x251;
+static int mem_base = 0xC8000;
+
+
+
+extern __inline__ void mvv_write(u8 index, u8 value)
+{
+ outw(index|(value<<8), io_port);
+}
+
+extern __inline__ u8 mvv_read(u8 index)
+{
+ outb(index, io_port);
+ return inb(data_port);
+}
+
+extern int i2c_stat(u8 slave)
+{
+ int counter;
+ int i;
+
+ outb(0x28, io_port);
+
+ counter=0;
+ while((inb(data_port)&0x01)==0)
+ if(counter++==256)
+ break;
+
+ while((inb(data_port)&0x01)!=0)
+ if(counter++==256)
+ break;
+
+ outb(slave, io_port);
+
+ counter=0;
+ while((inb(data_port)&0x01)==0)
+ if(counter++==256)
+ break;
+
+ while((inb(data_port)&0x01)!=0)
+ if(counter++==256)
+ break;
+
+ for(i=0;i<12;i++)
+ {
+ char st=inb(data_port);
+ if((st&2)!=0)
+ return -1;
+ if((st&1)==0)
+ break;
+ }
+ outb(0x29, io_port);
+ return inb(data_port);
+}
+
+int i2c_write(u16 slave, u16 sub, u16 data)
+{
+ int skip=0;
+ int count;
+ int i;
+
+ for(i=0;i<i2c_count;i++)
+ {
+ if((i2cinfo[i].slave==slave) &&
+ (i2cinfo[i].sub == sub))
+ {
+ if(i2cinfo[i].data==data)
+ skip=1;
+ i2cinfo[i].data=data;
+ i=i2c_count+1;
+ }
+ }
+
+ if(i==i2c_count && i2c_count<64)
+ {
+ i2cinfo[i2c_count].slave=slave;
+ i2cinfo[i2c_count].sub=sub;
+ i2cinfo[i2c_count].data=data;
+ i2c_count++;
+ }
+
+ if(skip)
+ return 0;
+
+ mvv_write(0x29, sub);
+ mvv_write(0x2A, data);
+ mvv_write(0x28, slave);
+
+ outb(0x28, io_port);
+
+ count=0;
+ while((inb(data_port)&1)==0)
+ if(count>255)
+ break;
+ while((inb(data_port)&1)!=0)
+ if(count>255)
+ break;
+
+ count=inb(data_port);
+
+ if(count&2)
+ return -1;
+ return count;
+}
+
+int i2c_read(int slave, int sub)
+{
+ int i=0;
+ for(i=0;i<i2c_count;i++)
+ {
+ if(i2cinfo[i].slave==slave && i2cinfo[i].sub==sub)
+ return i2cinfo[i].data;
+ }
+ return 0;
+}
+
+
+void i2c_andor(int slave, int sub, int and, int or)
+{
+ u8 tmp;
+
+ tmp=i2c_read(slave, sub);
+ tmp = (tmp&and)|or;
+ i2c_write(slave, sub, tmp);
+}
+
+/*
+ * Control functions
+ */
+
+
+static void pms_videosource(short source)
+{
+ mvv_write(0x2E, source?0x31:0x30);
+}
+
+static void pms_hue(short hue)
+{
+ switch(decoder)
+ {
+ case MOTOROLA:
+ i2c_write(0x8A, 0x00, hue);
+ break;
+ case PHILIPS2:
+ i2c_write(0x8A, 0x07, hue);
+ break;
+ case PHILIPS1:
+ i2c_write(0x42, 0x07, hue);
+ break;
+ }
+}
+
+static void pms_colour(short colour)
+{
+ switch(decoder)
+ {
+ case MOTOROLA:
+ i2c_write(0x8A, 0x00, colour);
+ break;
+ case PHILIPS1:
+ i2c_write(0x42, 012, colour);
+ break;
+ }
+}
+
+
+static void pms_contrast(short contrast)
+{
+ switch(decoder)
+ {
+ case MOTOROLA:
+ i2c_write(0x8A, 0x00, contrast);
+ break;
+ case PHILIPS1:
+ i2c_write(0x42, 0x13, contrast);
+ break;
+ }
+}
+
+static void pms_brightness(short brightness)
+{
+ switch(decoder)
+ {
+ case MOTOROLA:
+ i2c_write(0x8A, 0x00, brightness);
+ i2c_write(0x8A, 0x00, brightness);
+ i2c_write(0x8A, 0x00, brightness);
+ break;
+ case PHILIPS1:
+ i2c_write(0x42, 0x19, brightness);
+ break;
+ }
+}
+
+static void pms_hstart(short start)
+{
+ switch(decoder)
+ {
+ case PHILIPS1:
+ i2c_write(0x8A, 0x05, start);
+ i2c_write(0x8A, 0x18, start);
+ break;
+ case PHILIPS2:
+ i2c_write(0x42, 0x05, start);
+ i2c_write(0x42, 0x18, start);
+ break;
+ }
+}
+
+static void pms_format(short format)
+{
+ int target;
+ standard = format;
+
+ if(decoder==PHILIPS1)
+ target=0x42;
+ else if(decoder==PHILIPS2)
+ target=0x8A;
+ else
+ return;
+
+ switch(format)
+ {
+ case 0: /* Auto */
+ i2c_andor(target, 0x0D, 0xFE,0x00);
+ i2c_andor(target, 0x0F, 0x3F,0x80);
+ break;
+ case 1: /* NTSC */
+ i2c_andor(target, 0x0D, 0xFE, 0x00);
+ i2c_andor(target, 0x0F, 0x3F, 0x40);
+ break;
+ case 2: /* PAL */
+ i2c_andor(target, 0x0D, 0xFE, 0x00);
+ i2c_andor(target, 0x0F, 0x3F, 0x00);
+ break;
+ case 3: /* SECAM */
+ i2c_andor(target, 0x0D, 0xFE, 0x01);
+ i2c_andor(target, 0x0F, 0x3F, 0x00);
+ break;
+ }
+}
+
+/*
+ * Bandpass filters
+ */
+
+static void pms_bandpass(short pass)
+{
+ if(decoder==PHILIPS2)
+ i2c_andor(0x8A, 0x06, 0xCF, (pass&0x03)<<4);
+ else if(decoder==PHILIPS1)
+ i2c_andor(0x42, 0x06, 0xCF, (pass&0x03)<<4);
+}
+
+static void pms_antisnow(short snow)
+{
+ if(decoder==PHILIPS2)
+ i2c_andor(0x8A, 0x06, 0xF3, (snow&0x03)<<2);
+ else if(decoder==PHILIPS1)
+ i2c_andor(0x42, 0x06, 0xF3, (snow&0x03)<<2);
+}
+
+static void pms_sharpness(short sharp)
+{
+ if(decoder==PHILIPS2)
+ i2c_andor(0x8A, 0x06, 0xFC, sharp&0x03);
+ else if(decoder==PHILIPS1)
+ i2c_andor(0x42, 0x06, 0xFC, sharp&0x03);
+}
+
+static void pms_chromaagc(short agc)
+{
+ if(decoder==PHILIPS2)
+ i2c_andor(0x8A, 0x0C, 0x9F, (agc&0x03)<<5);
+ else if(decoder==PHILIPS1)
+ i2c_andor(0x42, 0x0C, 0x9F, (agc&0x03)<<5);
+}
+
+static void pms_vertnoise(short noise)
+{
+ if(decoder==PHILIPS2)
+ i2c_andor(0x8A, 0x10, 0xFC, noise&3);
+ else if(decoder==PHILIPS1)
+ i2c_andor(0x42, 0x10, 0xFC, noise&3);
+}
+
+static void pms_secamcross(short cross)
+{
+ if(decoder==PHILIPS2)
+ i2c_andor(0x8A, 0x0F, 0xDF, (cross&1)<<5);
+ else if(decoder==PHILIPS1)
+ i2c_andor(0x42, 0x0F, 0xDF, (cross&1)<<5);
+}
+
+static void pms_forcecolour(short colour)
+{
+ if(decoder==PHILIPS2)
+ i2c_andor(0x8A, 0x0C, 0x7F, (colour&1)<<7);
+ else if(decoder==PHILIPS1)
+ i2c_andor(0x42, 0x0C, 0x7, (colour&1)<<7);
+}
+
+static void pms_antigamma(short gamma)
+{
+ if(decoder==PHILIPS2)
+ i2c_andor(0xB8, 0x00, 0x7F, (gamma&1)<<7);
+ else if(decoder==PHILIPS1)
+ i2c_andor(0x42, 0x20, 0x7, (gamma&1)<<7);
+}
+
+static void pms_prefilter(short filter)
+{
+ if(decoder==PHILIPS2)
+ i2c_andor(0x8A, 0x06, 0xBF, (filter&1)<<6);
+ else if(decoder==PHILIPS1)
+ i2c_andor(0x42, 0x06, 0xBF, (filter&1)<<6);
+}
+
+static void pms_hfilter(short filter)
+{
+ if(decoder==PHILIPS2)
+ i2c_andor(0xB8, 0x04, 0x1F, (filter&7)<<5);
+ else if(decoder==PHILIPS1)
+ i2c_andor(0x42, 0x24, 0x1F, (filter&7)<<5);
+}
+
+static void pms_vfilter(short filter)
+{
+ if(decoder==PHILIPS2)
+ i2c_andor(0xB8, 0x08, 0x9F, (filter&3)<<5);
+ else if(decoder==PHILIPS1)
+ i2c_andor(0x42, 0x28, 0x9F, (filter&3)<<5);
+}
+
+static void pms_killcolour(short colour)
+{
+ if(decoder==PHILIPS2)
+ {
+ i2c_andor(0x8A, 0x08, 0x07, (colour&0x1F)<<3);
+ i2c_andor(0x8A, 0x09, 0x07, (colour&0x1F)<<3);
+ }
+ else if(decoder==PHILIPS1)
+ {
+ i2c_andor(0x42, 0x08, 0x07, (colour&0x1F)<<3);
+ i2c_andor(0x42, 0x09, 0x07, (colour&0x1F)<<3);
+ }
+}
+
+static void pms_swsense(short sense)
+{
+ if(decoder==PHILIPS2)
+ {
+ i2c_write(0x8A, 0x0A, sense);
+ i2c_write(0x8A, 0x0B, sense);
+ }
+ else if(decoder==PHILIPS1)
+ {
+ i2c_write(0x42, 0x08, sense);
+ i2c_write(0x42, 0x09, sense);
+ }
+}
+
+static void pms_chromagain(short chroma)
+{
+ if(decoder==PHILIPS2)
+ {
+ i2c_write(0x8A, 0x0A, chroma);
+ i2c_write(0x8A, 0x0B, chroma);
+ }
+ else if(decoder==PHILIPS1)
+ {
+ i2c_write(0x42, 0x08, chroma);
+ i2c_write(0x42, 0x09, chroma);
+ }
+}
+
+
+static void pms_spacialcompl(short data)
+{
+ mvv_write(0x3B, data);
+}
+
+static void pms_spacialcomph(short data)
+{
+ mvv_write(0x3A, data);
+}
+
+static void pms_framerate(short frr)
+{
+ int fps=(standard==1)?30:25;
+ if(frr==0)
+ return;
+ fps=fps/frr;
+ mvv_write(0x14,0x80|fps);
+ mvv_write(0x15,1);
+}
+
+static void pms_vert(u8 deciden, u8 decinum)
+{
+ mvv_write(0x1C, deciden); /* Denominator */
+ mvv_write(0x1D, decinum); /* Numerator */
+}
+
+/*
+ * Turn 16bit ratios into best small ratio the chipset can grok
+ */
+
+static void pms_vertdeci(unsigned short decinum, unsigned short deciden)
+{
+ /* Knock it down by /5 once */
+ if(decinum%5==0)
+ {
+ deciden/=5;
+ decinum/=5;
+ }
+ /*
+ * 3's
+ */
+ while(decinum%3==0 && deciden%3==0)
+ {
+ deciden/=3;
+ decinum/=3;
+ }
+ /*
+ * 2's
+ */
+ while(decinum%2==0 && deciden%2==0)
+ {
+ decinum/=2;
+ deciden/=2;
+ }
+ /*
+ * Fudgyify
+ */
+ while(deciden>32)
+ {
+ deciden/=2;
+ decinum=(decinum+1)/2;
+ }
+ if(deciden==32)
+ deciden--;
+ pms_vert(deciden,decinum);
+}
+
+static void pms_horzdeci(short decinum, short deciden)
+{
+ if(decinum<=512)
+ {
+ if(decinum%5==0)
+ {
+ decinum/=5;
+ deciden/=5;
+ }
+ }
+ else
+ {
+ decinum=512;
+ deciden=640; /* 768 would be ideal */
+ }
+
+ while(decinum%2==0 && deciden%2==0)
+ {
+ decinum/=2;
+ deciden/=2;
+ }
+ while(deciden>32)
+ {
+ deciden/=2;
+ decinum=(decinum+1)/2;
+ }
+ if(deciden==32)
+ deciden--;
+
+ mvv_write(0x24, 0x80|deciden);
+ mvv_write(0x25, deciden);
+}
+
+static void pms_resolution(short width, short height)
+{
+ int fg_height;
+
+ fg_height=height;
+ if(fg_height>280)
+ fg_height=280;
+
+ mvv_write(0x18, fg_height);
+ mvv_write(0x19, fg_height>>8);
+
+ if(standard==1)
+ {
+ mvv_write(0x1A, 0xFC);
+ mvv_write(0x1B, 0x00);
+ if(height>width)
+ pms_vertdeci(240,240);
+ else
+ pms_vertdeci(fg_height,240);
+ }
+ else
+ {
+ mvv_write(0x1A, 0x1A);
+ mvv_write(0x1B, 0x01);
+ if(fg_height>256)
+ pms_vertdeci(270,270);
+ else
+ pms_vertdeci(fg_height, 270);
+ }
+ mvv_write(0x12,0);
+ mvv_write(0x13, MVVMEMORYWIDTH);
+ mvv_write(0x42, 0x00);
+ mvv_write(0x43, 0x00);
+ mvv_write(0x44, MVVMEMORYWIDTH);
+
+ mvv_write(0x22, width+8);
+ mvv_write(0x23, (width+8)>> 8);
+
+ if(standard==1)
+ pms_horzdeci(width,640);
+ else
+ pms_horzdeci(width+8, 768);
+
+ mvv_write(0x30, mvv_read(0x30)&0xFE);
+ mvv_write(0x08, mvv_read(0x08)|0x01);
+ mvv_write(0x01, mvv_read(0x01)&0xFD);
+ mvv_write(0x32, 0x00);
+ mvv_write(0x33, MVVMEMORYWIDTH);
+}
+
+static void pms_vstart(short start)
+{
+ mvv_write(0x16, start);
+ mvv_write(0x17, (start>>8)&0x01);
+}
+
+/*
+ * Set Input
+ */
+
+static void pms_vcrinput(short input)
+{
+ if(decoder==PHILIPS2)
+ i2c_andor(0x8A,0x0D,0x7F,(input&1)<<7);
+ else if(decoder==PHILIPS1)
+ i2c_andor(0x42,0x0D,0x7F,(input&1)<<7);
+}
+
+
+static int pms_capture(struct pms_device *dev, char *buf, int rgb555, int count)
+{
+ char dump[16];
+ int y;
+ int ww= dev->width, wh= dev->height;
+ int dinc, zz;
+ int dw=2*ww, loops;
+ unsigned char r8;
+ int len=0;
+
+ short *dst=(short *)buf;
+ short *src=(short *)bus_to_virt((void *)mem_base);
+
+ if(wh>256)
+ {
+ dinc=2*ww;
+ zz=wh/2;
+ loops=2;
+ }
+ else
+ {
+ dinc=ww;
+ zz=wh;
+ loops=1;
+ }
+
+
+ r8=0x5;
+ if(rgb555)
+ r8|=0x20;
+
+field_cap:
+
+ mvv_write(0x08, r8); /* Capture mode, Enable DRAM, PC enable */
+
+ for(y=0;y<zz;y++)
+ {
+ int n;
+
+ len+=16;
+
+ /*
+ * Avoid overrunning the given buffer
+ */
+
+ n=((char *)dst)-buf; /* Bytes left in frame */
+ n=count-n;
+ if(n>dw) /* But only as many as needed */
+ n=dw;
+ memcpy(dump, src, 16); /* Junk */
+ if(n)
+ copy_to_user(dst, src, n); /* Data */
+ dst+=dinc;
+ *src=0; /* Synchronization */
+ }
+
+ if(--loops>0)
+ {
+ mvv_write(0x14, mvv_read(0x14)|0xC0); /* Other frame */
+ dst=(short *)buf+ww;
+ goto field_cap;
+ }
+
+ /*
+ * Set back to capture even frames
+ */
+
+ if(wh>256)
+ mvv_write(0x14, (mvv_read(0x14)|0x80)&~0x40);
+ return len;
+}
+
+
+/*
+ * Video4linux interfacing
+ */
+
+static int pms_open(struct video_device *dev, int flags)
+{
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static void pms_close(struct video_device *dev)
+{
+ MOD_DEC_USE_COUNT;
+}
+
+static int pms_init_done(struct video_device *dev)
+{
+ return 0;
+}
+
+static long pms_write(struct video_device *v, const char *buf, unsigned long count, int noblock)
+{
+ return -EINVAL;
+}
+
+static int pms_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
+{
+ struct pms_device *pd=(struct pms_device *)dev;
+
+ switch(cmd)
+ {
+ case VIDIOCGCAP:
+ {
+ struct video_capability b;
+ strcpy(b.name, "Mediavision PMS");
+ b.type = VID_TYPE_CAPTURE|VID_TYPE_SCALES;
+ b.channels = 4;
+ b.audios = 0;
+ b.maxwidth = 640;
+ b.maxheight = 480;
+ b.minwidth = 16;
+ b.minheight = 16;
+ if(copy_to_user(arg, &b,sizeof(b)))
+ return -EFAULT;
+ return 0;
+ }
+ case VIDIOCGCHAN:
+ {
+ struct video_channel v;
+ if(copy_from_user(&v, arg, sizeof(v)))
+ return -EFAULT;
+ if(v.channel<0 || v.channel>3)
+ return -EINVAL;
+ v.flags=0;
+ v.tuners=1;
+ /* Good question.. its composite or SVHS so.. */
+ v.type = VIDEO_TYPE_CAMERA;
+ if(copy_to_user(arg, &v, sizeof(v)))
+ return -EFAULT;
+ return 0;
+ }
+ case VIDIOCSCHAN:
+ {
+ int v;
+ if(copy_from_user(&v, arg,sizeof(v)))
+ return -EFAULT;
+ if(v<0 && v>3)
+ return -EINVAL;
+ pms_videosource(v&1);
+ pms_vcrinput(v>>1);
+ return 0;
+ }
+ case VIDIOCGTUNER:
+ {
+ struct video_tuner v;
+ if(copy_from_user(&v, arg, sizeof(v))!=0)
+ return -EFAULT;
+ if(v.tuner)
+ return -EINVAL;
+ strcpy(v.name, "Format");
+ v.rangelow=0;
+ v.rangehigh=0;
+ v.flags= VIDEO_TUNER_PAL|VIDEO_TUNER_NTSC|VIDEO_TUNER_SECAM;
+ switch(standard)
+ {
+ case 0:
+ v.mode = VIDEO_MODE_AUTO;
+ break;
+ case 1:
+ v.mode = VIDEO_MODE_NTSC;
+ break;
+ case 2:
+ v.mode = VIDEO_MODE_PAL;
+ break;
+ case 3:
+ v.mode = VIDEO_MODE_SECAM;
+ break;
+ }
+ if(copy_to_user(arg,&v,sizeof(v))!=0)
+ return -EFAULT;
+ return 0;
+ }
+ case VIDIOCSTUNER:
+ {
+ struct video_tuner v;
+ if(copy_from_user(&v, arg, sizeof(v))!=0)
+ return -EFAULT;
+ if(v.tuner)
+ return -EINVAL;
+ switch(v.mode)
+ {
+ case VIDEO_MODE_AUTO:
+ pms_framerate(25);
+ pms_secamcross(0);
+ pms_format(0);
+ break;
+ case VIDEO_MODE_NTSC:
+ pms_framerate(30);
+ pms_secamcross(0);
+ pms_format(1);
+ break;
+ case VIDEO_MODE_PAL:
+ pms_framerate(25);
+ pms_secamcross(0);
+ pms_format(2);
+ break;
+ case VIDEO_MODE_SECAM:
+ pms_framerate(25);
+ pms_secamcross(1);
+ pms_format(2);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+ }
+ case VIDIOCGPICT:
+ {
+ struct video_picture p=pd->picture;
+ if(copy_to_user(arg, &p, sizeof(p)))
+ return -EFAULT;
+ return 0;
+ }
+ case VIDIOCSPICT:
+ {
+ struct video_picture p;
+ if(copy_from_user(&p, arg, sizeof(p)))
+ return -EFAULT;
+ if(!((p.palette==VIDEO_PALETTE_RGB565 && p.depth==16)
+ ||(p.palette==VIDEO_PALETTE_RGB555 && p.depth==15)))
+ return -EINVAL;
+ pd->picture=p;
+
+ /*
+ * Now load the card.
+ */
+
+ pms_brightness(p.brightness);
+ pms_hue(p.hue);
+ pms_colour(p.colour);
+ pms_contrast(p.contrast);
+ return 0;
+ }
+ case VIDIOCSWIN:
+ {
+ struct video_window vw;
+ if(copy_from_user(&vw, arg,sizeof(vw)))
+ return -EFAULT;
+ if(vw.flags)
+ return -EINVAL;
+ if(vw.clipcount)
+ return -EINVAL;
+ if(vw.height<16||vw.height>480)
+ return -EINVAL;
+ if(vw.width<16||vw.width>640)
+ return -EINVAL;
+ pd->width=vw.width;
+ pd->height=vw.height;
+ pms_resolution(pd->width, pd->height);
+ /* Ok we figured out what to use from our wide choice */
+ return 0;
+ }
+ case VIDIOCGWIN:
+ {
+ struct video_window vw;
+ vw.x=0;
+ vw.y=0;
+ vw.width=pd->width;
+ vw.height=pd->height;
+ vw.chromakey=0;
+ vw.flags=0;
+ if(copy_to_user(arg, &vw, sizeof(vw)))
+ return -EFAULT;
+ return 0;
+ }
+ case VIDIOCCAPTURE:
+ return -EINVAL;
+ case VIDIOCGFBUF:
+ return -EINVAL;
+ case VIDIOCSFBUF:
+ return -EINVAL;
+ case VIDIOCKEY:
+ return 0;
+ case VIDIOCGFREQ:
+ return -EINVAL;
+ case VIDIOCSFREQ:
+ return -EINVAL;
+ case VIDIOCGAUDIO:
+ return -EINVAL;
+ case VIDIOCSAUDIO:
+ return -EINVAL;
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+static long pms_read(struct video_device *v, char *buf, unsigned long count, int noblock)
+{
+ struct pms_device *pd=(struct pms_device *)v;
+ int len;
+
+ /* FIXME: semaphore this */
+ len=pms_capture(pd, buf, (pd->picture.depth==16)?0:1,count);
+ return len;
+}
+
+
+struct video_device pms_template=
+{
+ "Mediavision PMS",
+ VID_TYPE_CAPTURE,
+ VID_HARDWARE_PMS,
+ pms_open,
+ pms_close,
+ pms_read,
+ pms_write,
+ pms_ioctl,
+ NULL,
+ pms_init_done,
+ NULL,
+ 0,
+ 0
+};
+
+struct pms_device pms_device;
+
+
+/*
+ * Probe for and initialise the Mediavision PMS
+ */
+
+static int init_mediavision(void)
+{
+ int id;
+ int idec, decst;
+ int i;
+
+ unsigned char i2c_defs[]={
+ 0x4C,0x30,0x00,0xE8,
+ 0xB6,0xE2,0x00,0x00,
+ 0xFF,0xFF,0x00,0x00,
+ 0x00,0x00,0x78,0x98,
+ 0x00,0x00,0x00,0x00,
+ 0x34,0x0A,0xF4,0xCE,
+ 0xE4
+ };
+
+ if(check_region(0x9A01,1))
+ {
+ printk(KERN_WARNING "mediavision: unable to detect: 0x9A01 in use.\n");
+ return -EBUSY;
+ }
+ if(check_region(io_port,3))
+ {
+ printk(KERN_WARNING "mediavision: I/O port %d in use.\n", io_port);
+ return -EBUSY;
+ }
+ outb(0xB8, 0x9A01); /* Unlock */
+ outb(io_port>>4, 0x9A01); /* Set IO port */
+
+
+ id=mvv_read(3);
+ decst=i2c_stat(0x43);
+
+ if(decst!=-1)
+ idec=2;
+ else if(i2c_stat(0xb9)!=-1)
+ idec=3;
+ else if(i2c_stat(0x8b)!=-1)
+ idec=1;
+ else
+ idec=0;
+
+ if(idec==0)
+ return -ENODEV;
+
+ /*
+ * Ok we have a PMS of some sort
+ */
+
+ request_region(io_port,3, "Mediavision PMS");
+ request_region(0x9A01, 1, "Mediavision PMS config");
+
+ mvv_write(0x04, mem_base>>12); /* Set the memory area */
+
+ /* Ok now load the defaults */
+
+ for(i=0;i<0x19;i++)
+ {
+ if(i2c_defs[i]==0xFF)
+ i2c_andor(0x8A, i, 0x07,0x00);
+ else
+ i2c_write(0x8A, i, i2c_defs[i]);
+ }
+
+ i2c_write(0xB8,0x00,0x12);
+ i2c_write(0xB8,0x04,0x00);
+ i2c_write(0xB8,0x07,0x00);
+ i2c_write(0xB8,0x08,0x00);
+ i2c_write(0xB8,0x09,0xFF);
+ i2c_write(0xB8,0x0A,0x00);
+ i2c_write(0xB8,0x0B,0x10);
+ i2c_write(0xB8,0x10,0x03);
+
+ mvv_write(0x01, 0x00);
+ mvv_write(0x05, 0xA0);
+ mvv_write(0x08, 0x25);
+ mvv_write(0x09, 0x00);
+ mvv_write(0x0A, 0x20|MVVMEMORYWIDTH);
+
+ mvv_write(0x10, 0x02);
+ mvv_write(0x1E, 0x0C);
+ mvv_write(0x1F, 0x03);
+ mvv_write(0x26, 0x06);
+
+ mvv_write(0x2B, 0x00);
+ mvv_write(0x2C, 0x20);
+ mvv_write(0x2D, 0x00);
+ mvv_write(0x2F, 0x70);
+ mvv_write(0x32, 0x00);
+ mvv_write(0x33, MVVMEMORYWIDTH);
+ mvv_write(0x34, 0x00);
+ mvv_write(0x35, 0x00);
+ mvv_write(0x3A, 0x80);
+ mvv_write(0x3B, 0x10);
+ mvv_write(0x20, 0x00);
+ mvv_write(0x21, 0x00);
+ mvv_write(0x30, 0x22);
+ return 0;
+}
+
+static void shutdown_mediavision(void)
+{
+ release_region(io_port,3);
+ release_region(0x9A01, 1);
+}
+
+/*
+ * Module stuff
+ */
+
+#ifdef MODULE
+
+int init_module(void)
+{
+ printk(KERN_INFO "Mediavision Pro Movie Studio driver 0.01\n");
+ if(init_mediavision())
+ {
+ printk(KERN_INFO "Board not found.\n");
+ return -ENODEV;
+ }
+ memcpy(&pms_device, &pms_template, sizeof(pms_template));
+ pms_device.height=240;
+ pms_device.width=320;
+ pms_resolution(320,240);
+ return video_register_device((struct video_device *)&pms_device);
+}
+
+void cleanup_module(void)
+{
+ shutdown_mediavision();
+ video_unregister_device((struct video_device *)&pms_device);
+}
+
+#endif
diff --git a/drivers/char/psaux.c b/drivers/char/psaux.c
index d297670a8..771af9a51 100644
--- a/drivers/char/psaux.c
+++ b/drivers/char/psaux.c
@@ -321,13 +321,13 @@ static int open_aux(struct inode * inode, struct file * file)
* Write to the aux device.
*/
-static long write_aux(struct inode * inode, struct file * file,
- const char * buffer, unsigned long count)
+static ssize_t write_aux(struct file * file, const char * buffer,
+ size_t count, loff_t *ppos)
{
- int retval = 0;
+ ssize_t retval = 0;
if (count) {
- int written = 0;
+ ssize_t written = 0;
aux_start_atomic();
do {
@@ -345,7 +345,7 @@ static long write_aux(struct inode * inode, struct file * file,
retval = -EIO;
if (written) {
retval = written;
- inode->i_mtime = CURRENT_TIME;
+ file->f_dentry->d_inode->i_mtime = CURRENT_TIME;
}
}
@@ -468,10 +468,10 @@ static int open_qp(struct inode * inode, struct file * file)
* Write to the 82C710 mouse device.
*/
-static long write_qp(struct inode * inode, struct file * file,
- const char * buffer, unsigned long count)
+static ssize_t write_qp(struct file * file, const char * buffer,
+ size_t count, loff_t *ppos)
{
- int i = count;
+ ssize_t i = count;
while (i--) {
char c;
@@ -480,7 +480,7 @@ static long write_qp(struct inode * inode, struct file * file,
get_user(c, buffer++);
outb_p(c, qp_data);
}
- inode->i_mtime = CURRENT_TIME;
+ file->f_dentry->d_inode->i_mtime = CURRENT_TIME;
return count;
}
@@ -546,11 +546,11 @@ __initfunc(static int probe_qp(void))
* Put bytes from input queue to buffer.
*/
-static long read_aux(struct inode * inode, struct file * file,
- char * buffer, unsigned long count)
+static ssize_t read_aux(struct file * file, char * buffer,
+ size_t count, loff_t *ppos)
{
struct wait_queue wait = { current, NULL };
- int i = count;
+ ssize_t i = count;
unsigned char c;
if (queue_empty()) {
@@ -559,7 +559,7 @@ static long read_aux(struct inode * inode, struct file * file,
add_wait_queue(&queue->proc_list, &wait);
repeat:
current->state = TASK_INTERRUPTIBLE;
- if (queue_empty() && !(current->signal & ~current->blocked)) {
+ if (queue_empty() && !signal_pending(current)) {
schedule();
goto repeat;
}
@@ -573,10 +573,10 @@ repeat:
}
aux_ready = !queue_empty();
if (count-i) {
- inode->i_atime = CURRENT_TIME;
+ file->f_dentry->d_inode->i_atime = CURRENT_TIME;
return count-i;
}
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
return -ERESTARTSYS;
return 0;
}
diff --git a/drivers/char/radio.c b/drivers/char/radio.c
new file mode 100644
index 000000000..2b79e9872
--- /dev/null
+++ b/drivers/char/radio.c
@@ -0,0 +1,234 @@
+/*
+ * Radio Card Device Driver for Linux
+ *
+ * (c) 1997 Matthew Kirkwood <weejock@ferret.lmh.ox.ac.uk>
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+
+#include <linux/miscdevice.h>
+
+#include <asm/uaccess.h>
+
+#include <linux/config.h>
+
+#include <linux/radio.h>
+#ifdef CONFIG_RADIO_RTRACK
+#include "rtrack.h"
+#endif
+#ifdef CONFIG_RADIO_WINRADIO
+#include "winradio.h"
+#endif
+
+int radio_open(struct inode *inode, struct file *file);
+int radio_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
+
+/* /dev/radio interface */
+static struct file_operations radio_fops = {
+ NULL, /* seek */
+ NULL, /* read */
+ NULL, /* write */
+ NULL, /* readdir */
+ NULL, /* select */
+ &radio_ioctl, /* ioctl */
+ NULL, /* mmap */
+ &radio_open, /* we're not allowed NULL, it seems... */
+ NULL /* release */
+};
+
+static struct miscdevice radio_miscdevice = {
+ RADIO_MINOR, /* minor device number */
+ "radio", /* title */
+ &radio_fops, /* file operations */
+ NULL, NULL /* previous and next (not our business) */
+};
+
+
+static struct radio_device *firstdevice, *lastdevice;
+static int numdevices;
+
+__initfunc(void radio_init(void))
+{
+ /* register the handler for the device number... */
+ if(misc_register(&radio_miscdevice)) {
+ printk("radio: couldn't register misc device\n");
+ return;
+ }
+
+ /* do some general initialisation stuff */
+ lastdevice = firstdevice = NULL;
+ numdevices = 0;
+
+#ifdef CONFIG_RADIO_RTRACK
+ radiotrack_init();
+#endif
+#ifdef CONFIG_RADIO_WINRADIO
+ printk("oooops. no winradio support yet... :-(\n");
+#endif
+/* etc.... */
+
+ printk("radio: registered %d devices\n", numdevices);
+}
+
+
+/* according to drivers/char/misc.c, the "open" call must not be NULL.
+ * I'm not sure if I approve, but I didn't write it, so...
+ */
+int radio_open(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+
+/* append a device to the linked list... */
+int radio_add_device(struct radio_device *newdev)
+{
+ if(firstdevice == NULL) {
+ firstdevice = newdev;
+ } else {
+ lastdevice->next = newdev;
+ }
+ lastdevice = newdev; numdevices++;
+ newdev->cap->dev_num=numdevices;
+ newdev->next = NULL; /* don't need, but... */
+ return(numdevices);
+}
+
+struct radio_device *getdev(int index)
+{
+struct radio_device *retval;
+
+ if(index > numdevices)
+ return NULL; /* let's have a bit less of that */
+
+ retval = firstdevice;
+ for(;index;index--)
+ retval = retval->next;
+
+ return retval;
+}
+
+
+/* interface routine */
+int radio_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+struct radio_device *dev;
+struct radio_ctl *ctl_arg = (struct radio_ctl*)arg;
+int nowrite;
+int val, ret;
+
+ if((void*)arg == NULL)
+ return -EFAULT; /* XXX - check errnos are OK */
+
+
+ switch(cmd) {
+ case RADIO_NUMDEVS:
+ return (put_user(numdevices,(int*)arg) ? -EFAULT : 0);
+
+
+ case RADIO_GETCAPS:
+ /* p'raps I should verify for read then write ?? */
+ if(verify_area(VERIFY_WRITE, (void*)arg, sizeof(struct radio_cap)))
+ return -EFAULT;
+ if((dev = getdev(((struct radio_cap*)arg)->dev_num)) == NULL)
+ return -EINVAL;
+ copy_to_user((void*)arg, dev->cap, sizeof(struct radio_cap));
+ return 0;
+
+
+ case RADIO_GETBNDCAP:
+ if(verify_area(VERIFY_WRITE, (void*)arg, sizeof(struct radio_band)))
+ return -EFAULT;
+
+ if((dev = getdev(((struct radio_band*)arg)->dev_num)) == NULL)
+ return -EINVAL;
+
+ val = ((struct radio_band*)arg)->index;
+ if(val >= dev->cap->num_bwidths)
+ return -EINVAL; /* XXX errno */
+
+ copy_to_user((void*)arg, (dev->bands)+(val*sizeof(struct radio_band)),
+ sizeof(struct radio_band));
+ return 0;
+ }
+
+
+/* now, we know that arg points to a struct radio_ctl */
+ /* get the requested device */
+ if(verify_area(VERIFY_READ, ctl_arg, sizeof(struct radio_ctl)))
+ return -EFAULT;
+
+ if((dev = getdev(ctl_arg->dev_num)) == NULL)
+ return -EINVAL;
+
+ nowrite = verify_area(VERIFY_WRITE, ctl_arg, sizeof(struct radio_ctl));
+
+ val = ctl_arg->value;
+
+ switch(cmd) {
+ case RADIO_SETVOL:
+ if((val < dev->cap->volmin) || (val > dev->cap->volmax))
+ return -EINVAL;
+ if((ret = (*(dev->setvol))(dev, val)))
+ return ret;
+ dev->curvol = val;
+ return 0;
+
+ case RADIO_GETVOL:
+ if(nowrite)
+ return -EFAULT;
+ ctl_arg->value = dev->curvol;
+ return 0;
+
+
+ case RADIO_SETBAND:
+ if(val >= dev->cap->num_bwidths)
+ return -EINVAL;
+ if((ret = (*(dev->setband))(dev, val)))
+ return ret;
+ dev->curband = val;
+ return 0;
+
+ case RADIO_GETBAND:
+ if(nowrite)
+ return -EFAULT;
+ ctl_arg->value = dev->curband;
+ return 0;
+
+
+ case RADIO_SETFREQ: {
+ struct radio_band *bp;
+
+ bp = (dev->bands) + ((dev->curband) * sizeof(struct radio_band));
+ if((val < bp->freqmin) || (val > bp->freqmax))
+ return -EINVAL;
+ if((ret = (*(dev->setfreq))(dev, val)))
+ return ret;
+ dev->curfreq = val;
+ return 0;
+ }
+
+ case RADIO_GETFREQ:
+ if(nowrite)
+ return -EFAULT;
+ ctl_arg->value = dev->curfreq;
+ return 0;
+
+
+ case RADIO_GETSIGSTR:
+ if(nowrite)
+ return -EFAULT;
+ ctl_arg->value = (*(dev->getsigstr))(dev);
+ return 0;
+
+
+ default:
+ return -ENOSYS;
+ }
+}
diff --git a/drivers/char/random.c b/drivers/char/random.c
index 527ac8609..6b8a2f12b 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -346,13 +346,13 @@ static struct timer_rand_state *blkdev_timer_state[MAX_BLKDEV];
static struct wait_queue *random_read_wait;
static struct wait_queue *random_write_wait;
-static long random_read(struct inode * inode, struct file * file,
- char * buf, unsigned long nbytes);
-static long random_read_unlimited(struct inode * inode, struct file * file,
- char * buf, unsigned long nbytes);
+static ssize_t random_read(struct file * file, char * buf,
+ size_t nbytes, loff_t *ppos);
+static ssize_t random_read_unlimited(struct file * file, char * buf,
+ size_t nbytes, loff_t *ppos);
static unsigned int random_poll(struct file *file, poll_table * wait);
-static long random_write(struct inode * inode, struct file * file,
- const char * buffer, unsigned long count);
+static ssize_t random_write(struct file * file, const char * buffer,
+ size_t count, loff_t *ppos);
static int random_ioctl(struct inode * inode, struct file * file,
unsigned int cmd, unsigned long arg);
@@ -983,10 +983,10 @@ static void MD5Transform(__u32 buf[4],
* bits of entropy are left in the pool, but it does not restrict the
* number of bytes that are actually obtained.
*/
-static int extract_entropy(struct random_bucket *r, char * buf,
- int nbytes, int to_user)
+static ssize_t extract_entropy(struct random_bucket *r, char * buf,
+ size_t nbytes, int to_user)
{
- int ret, i;
+ ssize_t ret, i;
__u32 tmp[HASH_BUFFER_SIZE];
char *cp,*dp;
@@ -1075,13 +1075,11 @@ void get_random_bytes(void *buf, int nbytes)
extract_entropy(&random_state, (char *) buf, nbytes, 0);
}
-static long
-random_read(struct inode * inode, struct file * file, char * buf, unsigned long nbytes)
+static ssize_t
+random_read(struct file * file, char * buf, size_t nbytes, loff_t *ppos)
{
struct wait_queue wait = { current, NULL };
- int n;
- int retval = 0;
- int count = 0;
+ ssize_t n, retval = 0, count = 0;
if (nbytes == 0)
return 0;
@@ -1098,7 +1096,7 @@ random_read(struct inode * inode, struct file * file, char * buf, unsigned long
retval = -EAGAIN;
break;
}
- if (current->signal & ~current->blocked) {
+ if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
@@ -1121,19 +1119,18 @@ random_read(struct inode * inode, struct file * file, char * buf, unsigned long
remove_wait_queue(&random_read_wait, &wait);
/*
- * If we gave the user some bytes and we have an inode pointer,
- * update the access time.
+ * If we gave the user some bytes, update the access time.
*/
- if (inode && count != 0) {
- UPDATE_ATIME(inode);
+ if (count != 0) {
+ UPDATE_ATIME(file->f_dentry->d_inode);
}
return (count ? count : retval);
}
-static long
-random_read_unlimited(struct inode * inode, struct file * file,
- char * buf, unsigned long nbytes)
+static ssize_t
+random_read_unlimited(struct file * file, char * buf,
+ size_t nbytes, loff_t *ppos)
{
return extract_entropy(&random_state, buf, nbytes, 1);
}
@@ -1153,14 +1150,14 @@ random_poll(struct file *file, poll_table * wait)
return mask;
}
-static long
-random_write(struct inode * inode, struct file * file,
- const char * buffer, unsigned long count)
+static ssize_t
+random_write(struct file * file, const char * buffer,
+ size_t count, loff_t *ppos)
{
- int i, bytes, ret = 0;
+ ssize_t i, bytes, ret = 0;
__u32 buf[16];
const char *p = buffer;
- int c = count;
+ ssize_t c = count;
while (c > 0) {
bytes = MIN(c, sizeof(buf));
@@ -1179,9 +1176,9 @@ random_write(struct inode * inode, struct file * file,
while (i--)
add_entropy_word(&random_state, buf[i]);
}
- if ((ret > 0) && inode) {
- inode->i_mtime = CURRENT_TIME;
- mark_inode_dirty(inode);
+ if (ret > 0) {
+ file->f_dentry->d_inode->i_mtime = CURRENT_TIME;
+ mark_inode_dirty(file->f_dentry->d_inode);
}
return ret;
}
@@ -1265,7 +1262,8 @@ random_ioctl(struct inode * inode, struct file * file,
retval = verify_area(VERIFY_READ, (void *) p, size);
if (retval)
return retval;
- retval = random_write(0, file, (const char *) p, size);
+ retval = random_write(file, (const char *) p,
+ size, &file->f_pos);
if (retval < 0)
return retval;
/*
diff --git a/drivers/char/riscom8.c b/drivers/char/riscom8.c
index 806caf370..ef6b347ab 100644
--- a/drivers/char/riscom8.c
+++ b/drivers/char/riscom8.c
@@ -1040,7 +1040,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
!(port->flags & ASYNC_CLOSING) &&
(do_clocal || CD))
break;
- if (current->signal & ~current->blocked) {
+ if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
diff --git a/drivers/char/rocket.c b/drivers/char/rocket.c
index 59d2666ab..ef9af462b 100644
--- a/drivers/char/rocket.c
+++ b/drivers/char/rocket.c
@@ -622,10 +622,12 @@ static void init_r_port(int board, int aiop, int chan)
rp_table[line] = info;
}
+#if (LINUX_VERSION_CODE < 131393) /* Linux 2.1.65 */
static int baud_table[] = {
0, 50, 75, 110, 134, 150, 200, 300,
600, 1200, 1800, 2400, 4800, 9600, 19200,
38400, 57600, 115200, 230400, 460800, 0 };
+#endif
/*
* This routine configures a rocketport port so according to its
@@ -671,6 +673,7 @@ static void configure_r_port(struct r_port *info)
}
/* baud rate */
+#if (LINUX_VERSION_CODE < 131393) /* Linux 2.1.65 */
i = cflag & CBAUD;
if (i & CBAUDEX) {
i &= ~CBAUDEX;
@@ -690,6 +693,11 @@ static void configure_r_port(struct r_port *info)
i += 4;
}
baud = baud_table[i] ? baud_table[i] : 9600;
+#else
+ baud = tty_get_baud_rate(info->tty);
+ if (!baud)
+ baud = 9600;
+#endif
info->cps = baud / bits;
sSetBaud(cp, (rp_baud_base/baud) - 1);
@@ -836,7 +844,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
(do_clocal || (sGetChanStatusLo(&info->channel) &
CD_ACT)))
break;
- if (current->signal & ~current->blocked) {
+ if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
@@ -1182,7 +1190,7 @@ static void rp_set_termios(struct tty_struct *tty, struct termios *old_termios)
/*
* Here are the routines used by rp_ioctl
*/
-
+#if (LINUX_VERSION_CODE < 131393) /* Linux 2.1.65 */
static void send_break( struct r_port * info, int duration)
{
current->state = TASK_INTERRUPTIBLE;
@@ -1193,6 +1201,24 @@ static void send_break( struct r_port * info, int duration)
sClrBreak(&info->channel);
sti();
}
+#else
+static void rp_break(struct tty_struct *tty, int break_state)
+{
+ struct r_port * info = (struct r_port *)tty->driver_data;
+ unsigned long flags;
+
+ if (rocket_paranoia_check(info, tty->device, "rp_break"))
+ return;
+
+ save_flags(flags); cli();
+ if (break_state == -1) {
+ sSendBreak(&info->channel);
+ } else {
+ sClrBreak(&info->channel);
+ }
+ restore_flags(flags);
+}
+#endif
static int get_modem_info(struct r_port * info, unsigned int *value)
{
@@ -1289,8 +1315,19 @@ static int set_config(struct r_port * info, struct rocket_config * new_info)
(new_serial.flags & ROCKET_FLAGS));
info->close_delay = new_serial.close_delay;
info->closing_wait = new_serial.closing_wait;
- configure_r_port(info);
+
+#if (LINUX_VERSION_CODE >= 131393) /* Linux 2.1.65 */
+ if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_HI)
+ info->tty->alt_speed = 57600;
+ if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_VHI)
+ info->tty->alt_speed = 115200;
+ if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_SHI)
+ info->tty->alt_speed = 230400;
+ if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_WARP)
+ info->tty->alt_speed = 460800;
+#endif
+ configure_r_port(info);
return 0;
}
@@ -1319,25 +1356,27 @@ static int get_ports(struct r_port * info, struct rocket_ports * retports)
static int rp_ioctl(struct tty_struct *tty, struct file * file,
unsigned int cmd, unsigned long arg)
{
- int tmp;
struct r_port * info = (struct r_port *)tty->driver_data;
- int retval;
+#if (LINUX_VERSION_CODE < 131393) /* Linux 2.1.65 */
+ int retval, tmp;
+#endif
if (cmd != RCKP_GET_PORTS &&
rocket_paranoia_check(info, tty->device, "rp_ioctl"))
return -ENODEV;
switch (cmd) {
+#if (LINUX_VERSION_CODE < 131393) /* Linux 2.1.65 */
case TCSBRK: /* SVID version: non-zero arg --> no break */
retval = tty_check_change(tty);
if (retval)
return retval;
tty_wait_until_sent(tty, 0);
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
return -EINTR;
if (!arg) {
send_break(info, HZ/4); /* 1/4 second */
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
return -EINTR;
}
return 0;
@@ -1346,10 +1385,10 @@ static int rp_ioctl(struct tty_struct *tty, struct file * file,
if (retval)
return retval;
tty_wait_until_sent(tty, 0);
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
return -EINTR;
send_break(info, arg ? arg*(HZ/10) : HZ/4);
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
return -EINTR;
return 0;
case TIOCGSOFTCAR:
@@ -1365,6 +1404,7 @@ static int rp_ioctl(struct tty_struct *tty, struct file * file,
((tty->termios->c_cflag & ~CLOCAL) |
(tmp ? CLOCAL : 0));
return 0;
+#endif
case TIOCMGET:
return get_modem_info(info, (unsigned int *) arg);
case TIOCMBIS:
@@ -1548,7 +1588,7 @@ static void rp_wait_until_sent(struct tty_struct *tty, int timeout)
current->counter = 0; /* make us low-priority */
current->timeout = jiffies + check_time;
schedule();
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
break;
}
current->state = TASK_RUNNING;
@@ -2093,6 +2133,9 @@ __initfunc(int rp_init(void))
rocket_driver.stop = rp_stop;
rocket_driver.start = rp_start;
rocket_driver.hangup = rp_hangup;
+#if (LINUX_VERSION_CODE >= 131393) /* Linux 2.1.65 */
+ rocket_driver.break_ctl = rp_break;
+#endif
#if (LINUX_VERSION_CODE >= 131343)
rocket_driver.send_xchar = rp_send_xchar;
rocket_driver.wait_until_sent = rp_wait_until_sent;
diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c
index b150dd7e1..26cfa4768 100644
--- a/drivers/char/rtc.c
+++ b/drivers/char/rtc.c
@@ -78,8 +78,8 @@ static struct timer_list rtc_irq_timer;
static long long rtc_llseek(struct file *file, loff_t offset, int origin);
-static long rtc_read(struct inode *inode, struct file *file,
- char *buf, unsigned long count);
+static ssize_t rtc_read(struct file *file, char *buf,
+ size_t count, loff_t *ppos);
static int rtc_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg);
@@ -146,11 +146,12 @@ static long long rtc_llseek(struct file *file, loff_t offset, int origin)
return -ESPIPE;
}
-static long rtc_read(struct inode *inode, struct file *file, char *buf,
- unsigned long count)
+static ssize_t rtc_read(struct file *file, char *buf,
+ size_t count, loff_t *ppos)
{
struct wait_queue wait = { current, NULL };
- int retval = 0;
+ unsigned long data;
+ ssize_t retval;
if (count < sizeof(unsigned long))
return -EINVAL;
@@ -159,30 +160,22 @@ static long rtc_read(struct inode *inode, struct file *file, char *buf,
current->state = TASK_INTERRUPTIBLE;
- while (rtc_irq_data == 0) {
+ while ((data = xchg(&rtc_irq_data, 0)) == 0) {
if (file->f_flags & O_NONBLOCK) {
retval = -EAGAIN;
- break;
+ goto out;
}
- if (current->signal & ~current->blocked) {
+ if (signal_pending(current)) {
retval = -ERESTARTSYS;
- break;
+ goto out;
}
schedule();
}
- if (retval == 0) {
- unsigned long data, flags;
- save_flags(flags);
- cli();
- data = rtc_irq_data;
- rtc_irq_data = 0;
- restore_flags(flags);
- retval = put_user(data, (unsigned long *)buf);
- if (!retval)
- retval = sizeof(unsigned long);
- }
-
+ retval = put_user(data, (unsigned long *)buf);
+ if (!retval)
+ retval = sizeof(unsigned long);
+out:
current->state = TASK_RUNNING;
remove_wait_queue(&rtc_wait, &wait);
diff --git a/drivers/char/rtrack.c b/drivers/char/rtrack.c
new file mode 100644
index 000000000..efe01046a
--- /dev/null
+++ b/drivers/char/rtrack.c
@@ -0,0 +1,213 @@
+/* radiotrack (radioreveal) driver for Linux radio support
+ * (c) 1997 M. Kirkwood
+ */
+/* TODO: Allow for more than one of these foolish entities :-) */
+
+/* Notes on the hardware (reverse engineered from other peoples'
+ * reverse engineering of AIMS' code :-)
+ *
+ * Frequency control is done digitally -- ie out(port,encodefreq(95.8));
+ *
+ * The signal strength query is unsurprisingly inaccurate. And it seems
+ * to indicate that (on my card, at least) the frequency setting isn't
+ * too great. (I have to tune up .025MHz from what the freq should be
+ * to get a report that the thing is tuned.)
+ *
+ * Volume control is (ugh) analogue:
+ * out(port, start_increasing_volume);
+ * wait(a_wee_while);
+ * out(port, stop_changing_the_volume);
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/radio.h>
+#include <linux/ioport.h>
+
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+#include "rtrack.h"
+
+/* Let's just be a bit careful here, shall we? */
+#if (CONFIG_RADIO_RTRACK_PORT != 0x20f) && (CONFIG_RADIO_RTRACK_PORT != 0x30f)
+#error Invalid port specified for RadioTrack
+#endif
+
+/* local prototypes */
+void outbits(int bits, int data, int port);
+void decvol(int port);
+void incvol(int port);
+void mute(int port);
+void unmute(int port);
+
+/* public structurey-type things */
+int rt_port = CONFIG_RADIO_RTRACK_PORT;
+
+struct radio_cap rt_cap = {
+ 0, /* device index (not dealt with here) */
+ RADIO_TYPE_RTRACK, /* type */
+ 1, /* number of bandwidths */
+ 0, 10 /* volmin, volmax */
+};
+
+/* we only get one band/protocol with radiotrack */
+struct radio_band rt_band = {
+ 0, /* device index */
+ 0, /* bandwidth "index" */
+ RADIO_PROTOCOL_FM,
+ RADIO_BAND_FM_STD,
+ RADIO_FM_FRTOINT(88.0), /* freq range */
+ RADIO_FM_FRTOINT(108.0),
+ 0,1 /* sig strength range */
+};
+
+/* p'raps these should become struct radio_ops and struct radio_status? */
+struct radio_device rt_dev = {
+ &rt_cap,
+ &rt_band,
+ &rt_setvol,
+ 0, /* curvol */
+ &rt_setband,
+ 0, /* curband */
+ &rt_setfreq,
+ 0, /* curfreq */
+ &rt_getsigstr,
+ NULL, /* next (to be filled in later) */
+ &rt_port /* misc */
+};
+
+
+void radiotrack_init()
+{
+int dev_num, i;
+
+/* XXX - probe here?? - XXX */
+
+/* try to grab the i/o port */
+ if(check_region(rt_port,2)) {
+ printk("rtrack.c: port 0x%x already used\n", rt_port);
+ return;
+ }
+
+ request_region(rt_port,2,"rtrack");
+
+ dev_num = radio_add_device(&rt_dev);
+/* initialise the card */
+ /* set the volume very low */
+ for(i=rt_cap.volmax; i>rt_cap.volmin; i--)
+ decvol(rt_port);
+ rt_dev.curvol = rt_cap.volmin;
+}
+
+
+int rt_setvol(struct radio_device *dev, int vol)
+{
+int port, i;
+
+ if(vol == dev->curvol)
+ return 0;
+
+ port = *(int*)(dev->misc);
+ if(vol == 0)
+ mute(port);
+
+ if(vol > dev->curvol)
+ for(i = dev->curvol; i < vol; i++)
+ incvol(port);
+ else
+ for(i = dev->curvol; i > vol; i--)
+ decvol(port);
+
+ if(dev->curvol == 0)
+ unmute(port);
+
+ return 0;
+}
+
+
+int rt_setband(struct radio_device *dev, int band)
+{
+/* we know in advance that we only have one band, and
+ * the wrapper checks the validity of all the inputs
+ */
+ return 0;
+}
+
+int rt_setfreq(struct radio_device *dev, int freq)
+{
+int myport = *(int*)(dev->misc);
+
+ outbits(16, RTRACK_ENCODE(freq), myport);
+ outbits(8, 0xa0, myport);
+/* XXX - get rid of this once setvol is implemented properly - XXX */
+/* these insist on turning the thing on. not sure I approve... */
+ udelay(1000);
+ outb(0, myport);
+ outb(0xc8, myport);
+
+ return 0;
+}
+
+int rt_getsigstr(struct radio_device *dev)
+{
+int res;
+int myport = *(int*)(dev->misc);
+
+ outb(0xf8, myport);
+ udelay(200000);
+ res = (int)inb(myport);
+ udelay(10000);
+ outb(0xe8, myport);
+ if(res == 0xfd)
+ return 1;
+ else
+ return 0;
+}
+
+
+/* local things */
+void outbits(int bits, int data, int port)
+{
+ while(bits--) {
+ if(data & 1) {
+ outw(5, port);
+ outw(5, port);
+ outw(7, port);
+ outw(7, port);
+ } else {
+ outw(1, port);
+ outw(1, port);
+ outw(3, port);
+ outw(3, port);
+ }
+ data>>=1;
+ }
+}
+
+void decvol(int port)
+{
+ outb(0x48, port);
+ udelay(100000);
+ outb(0xc8, port);
+}
+
+void incvol(int port)
+{
+ outb(0x88, port);
+ udelay(100000);
+ outb(0xc8, port);
+}
+
+void mute(int port)
+{
+ outb(0, port);
+ outb(0xc0, port);
+}
+
+void unmute(int port)
+{
+ outb(0, port);
+ outb(0xc8, port);
+}
diff --git a/drivers/char/rtrack.h b/drivers/char/rtrack.h
new file mode 100644
index 000000000..b5a9bc124
--- /dev/null
+++ b/drivers/char/rtrack.h
@@ -0,0 +1,25 @@
+/* RadioTrack (RadioReveal) include file.
+ * (c) 1997 M. Kirkwood
+ *
+ * Not in include/linux/ because there's no need for anyone
+ * to know about these details, I reckon.
+ */
+
+#ifndef __RTRACK_H
+#define __RTRACK_H
+
+#include <linux/radio.h>
+
+void radiotrack_init(void);
+int rt_setvol(struct radio_device *dev, int vol);
+int rt_setband(struct radio_device *dev, int vol);
+int rt_setfreq(struct radio_device *dev, int vol);
+int rt_getsigstr(struct radio_device *dev);
+
+/* frequency encoding stuff... */
+/* we have to careful not to introduce fp stuff here */
+#define RTRACK_ENCODE(x) (((((x)*2)/5)-(40*88))+0xf6c)
+#define RTRACK_DECODE(x) (((((x)-0xf6c)+(40*88))*5)/2)
+/* we shouldn't actually need the decode macro (or the excessive bracketing :-) */
+
+#endif /* __RTRACK_H */
diff --git a/drivers/char/serial.c b/drivers/char/serial.c
index d9a8d5cea..7d21673aa 100644
--- a/drivers/char/serial.c
+++ b/drivers/char/serial.c
@@ -24,6 +24,9 @@
* 1/97: Extended dumb serial ports are a config option now.
* Saves 4k. Michael A. Griffith <grif@acm.org>
*
+ * 8/97: Fix bug in rs_set_termios with RTS
+ * Stanislav V. Voronyi <stas@uanet.kharkov.ua>
+ *
* This module exports the following rs232 io functions:
*
* int rs_init(void);
@@ -392,13 +395,6 @@ static inline int serial_paranoia_check(struct async_struct *info,
return 0;
}
-/*
- * This is used to figure out the divisor speeds and the timeouts
- */
-static int baud_table[] = {
- 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
- 9600, 19200, 38400, 57600, 115200, 230400, 460800, 0 };
-
static inline unsigned int serial_in(struct async_struct *info, int offset)
{
#ifdef CONFIG_HUB6
@@ -634,7 +630,7 @@ static _INLINE_ void receive_chars(struct async_struct *info,
ignore_char:
*status = serial_inp(info, UART_LSR);
} while (*status & UART_LSR_DR);
- queue_task(&tty->flip.tqueue, &tq_timer);
+ tty_flip_buffer_push(tty);
}
static _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done)
@@ -717,10 +713,10 @@ static _INLINE_ void check_modem_status(struct async_struct *info)
else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) &&
(info->flags & ASYNC_CALLOUT_NOHUP))) {
#ifdef SERIAL_DEBUG_OPEN
- printk("scheduling hangup...");
+ printk("doing serial hangup...");
#endif
- queue_task(&info->tqueue_hangup,
- &tq_scheduler);
+ if (info->tty)
+ tty_hangup(info->tty);
}
}
if (info->flags & ASYNC_CTS_FLOW) {
@@ -1001,28 +997,6 @@ static void do_softint(void *private_)
}
/*
- * This routine is called from the scheduler tqueue when the interrupt
- * routine has signalled that a hangup has occurred. The path of
- * hangup processing is:
- *
- * serial interrupt routine -> (scheduler tqueue) ->
- * do_serial_hangup() -> tty->hangup() -> rs_hangup()
- *
- */
-static void do_serial_hangup(void *private_)
-{
- struct async_struct *info = (struct async_struct *) private_;
- struct tty_struct *tty;
-
- tty = info->tty;
- if (!tty)
- return;
-
- tty_hangup(tty);
-}
-
-
-/*
* This subroutine is called when the RS_TIMER goes off. It is used
* by the serial driver to handle ports that do not have an interrupt
* (irq=0). This doesn't work very well for 16450's, but gives barely
@@ -1155,7 +1129,6 @@ static int startup(struct async_struct * info)
unsigned short ICP;
#endif
-
page = get_free_page(GFP_KERNEL);
if (!page)
return -ENOMEM;
@@ -1458,9 +1431,9 @@ static void shutdown(struct async_struct * info)
static void change_speed(struct async_struct *info)
{
unsigned short port;
- int quot = 0, baud_base;
+ int quot = 0, baud_base, baud;
unsigned cflag, cval, fcr = 0;
- int i, bits;
+ int bits;
unsigned long flags;
if (!info->tty || !info->tty->termios)
@@ -1494,37 +1467,20 @@ static void change_speed(struct async_struct *info)
#endif
/* Determine divisor based on baud rate */
- i = cflag & CBAUD;
- if (i & CBAUDEX) {
- i &= ~CBAUDEX;
- if (i < 1 || i > 4)
- info->tty->termios->c_cflag &= ~CBAUDEX;
- else
- i += 15;
- }
- if (i == 15) {
- if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
- i += 1;
- if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
- i += 2;
- if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
- i += 3;
- if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
- i += 4;
- if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)
- quot = info->state->custom_divisor;
- }
+ baud = tty_get_baud_rate(info->tty);
baud_base = info->state->baud_base;
- if (!quot) {
- if (baud_table[i] == 134)
+ if (baud == 38400)
+ quot = info->state->custom_divisor;
+ else {
+ if (baud == 134)
/* Special case since 134 is really 134.5 */
quot = (2*baud_base / 269);
- else if (baud_table[i])
- quot = baud_base / baud_table[i];
- /* If the quotient is ever zero, default to 9600 bps */
- if (!quot)
- quot = baud_base / 9600;
+ else if (baud)
+ quot = baud_base / baud;
}
+ /* If the quotient is ever zero, default to 9600 bps */
+ if (!quot)
+ quot = baud_base / 9600;
info->quot = quot;
info->timeout = ((info->xmit_fifo_size*HZ*bits*quot) / baud_base);
info->timeout += HZ/50; /* Add .02 seconds of slop */
@@ -1936,8 +1892,17 @@ check_and_exit:
if (state->flags & ASYNC_INITIALIZED) {
if (((old_state.flags & ASYNC_SPD_MASK) !=
(state->flags & ASYNC_SPD_MASK)) ||
- (old_state.custom_divisor != state->custom_divisor))
+ (old_state.custom_divisor != state->custom_divisor)) {
+ if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+ info->tty->alt_speed = 57600;
+ if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+ info->tty->alt_speed = 115200;
+ if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
+ info->tty->alt_speed = 230400;
+ if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
+ info->tty->alt_speed = 460800;
change_speed(info);
+ }
} else
retval = startup(info);
return retval;
@@ -2068,51 +2033,27 @@ static int do_autoconfig(struct async_struct * info)
return 0;
}
-
-/*
- * This routine sends a break character out the serial port.
- */
-static void send_break( struct async_struct * info, int duration)
-{
- if (!info->port)
- return;
- current->state = TASK_INTERRUPTIBLE;
- current->timeout = jiffies + duration;
-#ifdef SERIAL_DEBUG_SEND_BREAK
- printk("rs_send_break(%d) jiff=%lu...", duration, jiffies);
-#endif
- cli();
- serial_out(info, UART_LCR, serial_inp(info, UART_LCR) | UART_LCR_SBC);
- schedule();
- serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC);
- sti();
-#ifdef SERIAL_DEBUG_SEND_BREAK
- printk("done jiffies=%lu\n", jiffies);
-#endif
-}
-
/*
- * This routine sets the break condition on the serial port.
+ * rs_break() --- routine which turns the break handling on or off
*/
-static void begin_break(struct async_struct * info)
+static void rs_break(struct tty_struct *tty, int break_state)
{
- if (!info->port)
+ struct async_struct * info = (struct async_struct *)tty->driver_data;
+ unsigned long flags;
+
+ if (serial_paranoia_check(info, tty->device, "rs_break"))
return;
- cli();
- serial_out(info, UART_LCR, serial_inp(info, UART_LCR) | UART_LCR_SBC);
- sti();
-}
-/*
- * This routine clears the break condition on the serial port.
- */
-static void end_break(struct async_struct * info)
-{
if (!info->port)
return;
- cli();
- serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC);
- sti();
+ save_flags(flags); cli();
+ if (break_state == -1)
+ serial_out(info, UART_LCR,
+ serial_inp(info, UART_LCR) | UART_LCR_SBC);
+ else
+ serial_out(info, UART_LCR,
+ serial_inp(info, UART_LCR) & ~UART_LCR_SBC);
+ restore_flags(flags);
}
/*
@@ -2279,7 +2220,6 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file,
{
int error;
struct async_struct * info = (struct async_struct *)tty->driver_data;
- int retval;
struct async_icount cprev, cnow; /* kernel counter temps */
struct serial_icounter_struct *p_cuser; /* user space */
@@ -2295,53 +2235,6 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file,
}
switch (cmd) {
- case TCSBRK: /* SVID version: non-zero arg --> no break */
- retval = tty_check_change(tty);
- if (retval)
- return retval;
- tty_wait_until_sent(tty, 0);
- if (current->signal & ~current->blocked)
- return -EINTR;
- if (!arg) {
- send_break(info, HZ/4); /* 1/4 second */
- if (current->signal & ~current->blocked)
- return -EINTR;
- }
- return 0;
- case TCSBRKP: /* support for POSIX tcsendbreak() */
- retval = tty_check_change(tty);
- if (retval)
- return retval;
- tty_wait_until_sent(tty, 0);
- if (current->signal & ~current->blocked)
- return -EINTR;
- send_break(info, arg ? arg*(HZ/10) : HZ/4);
- if (current->signal & ~current->blocked)
- return -EINTR;
- return 0;
- case TIOCSBRK:
- retval = tty_check_change(tty);
- if (retval)
- return retval;
- tty_wait_until_sent(tty, 0);
- begin_break(info);
- return 0;
- case TIOCCBRK:
- retval = tty_check_change(tty);
- if (retval)
- return retval;
- end_break(info);
- return 0;
- case TIOCGSOFTCAR:
- return put_user(C_CLOCAL(tty) ? 1 : 0, (int *) arg);
- case TIOCSSOFTCAR:
- error = get_user(arg, (unsigned int *) arg);
- if (error)
- return error;
- tty->termios->c_cflag =
- ((tty->termios->c_cflag & ~CLOCAL) |
- (arg ? CLOCAL : 0));
- return 0;
case TIOCMGET:
return get_modem_info(info, (unsigned int *) arg);
case TIOCMBIS:
@@ -2403,7 +2296,7 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file,
while (1) {
interruptible_sleep_on(&info->delta_msr_wait);
/* see if a signal did it */
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
return -ERESTARTSYS;
cli();
cnow = info->state->icount; /* atomic copy */
@@ -2472,8 +2365,8 @@ static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios)
if (!(old_termios->c_cflag & CBAUD) &&
(tty->termios->c_cflag & CBAUD)) {
info->MCR |= UART_MCR_DTR;
- if (!tty->hw_stopped ||
- !(tty->termios->c_cflag & CRTSCTS)) {
+ if (!(tty->termios->c_cflag & CRTSCTS) ||
+ !test_bit(TTY_THROTTLED, &tty->flags)) {
info->MCR |= UART_MCR_RTS;
}
cli();
@@ -2655,7 +2548,7 @@ static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
current->counter = 0; /* make us low-priority */
current->timeout = jiffies + char_time;
schedule();
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
break;
if (timeout && ((orig_jiffies + timeout) < jiffies))
break;
@@ -2710,10 +2603,8 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
if (info->flags & ASYNC_CLOSING)
interruptible_sleep_on(&info->close_wait);
#ifdef SERIAL_DO_RESTART
- if (info->flags & ASYNC_HUP_NOTIFY)
- return -EAGAIN;
- else
- return -ERESTARTSYS;
+ return ((info->flags & ASYNC_HUP_NOTIFY) ?
+ -EAGAIN : -ERESTARTSYS);
#else
return -EAGAIN;
#endif
@@ -2802,7 +2693,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
(do_clocal || (serial_in(info, UART_MSR) &
UART_MSR_DCD)))
break;
- if (current->signal & ~current->blocked) {
+ if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
@@ -2851,8 +2742,6 @@ static int get_async_struct(int line, struct async_struct **ret_info)
info->line = line;
info->tqueue.routine = do_softint;
info->tqueue.data = info;
- info->tqueue_hangup.routine = do_serial_hangup;
- info->tqueue_hangup.data = info;
info->state = sstate;
if (sstate->info) {
kfree_s(info, sizeof(struct async_struct));
@@ -2900,7 +2789,22 @@ static int rs_open(struct tty_struct *tty, struct file * filp)
else
tmp_buf = (unsigned char *) page;
}
-
+
+ /*
+ * If the port is the middle of closing, bail out now
+ */
+ if (tty_hung_up_p(filp) ||
+ (info->flags & ASYNC_CLOSING)) {
+ if (info->flags & ASYNC_CLOSING)
+ interruptible_sleep_on(&info->close_wait);
+#ifdef SERIAL_DO_RESTART
+ return ((info->flags & ASYNC_HUP_NOTIFY) ?
+ -EAGAIN : -ERESTARTSYS);
+#else
+ return -EAGAIN;
+#endif
+ }
+
/*
* Start up serial port
*/
@@ -3294,7 +3198,6 @@ static void autoconfig(struct serial_state * state)
scratch = serial_in(info, UART_IIR) >> 5;
if (scratch == 7) {
serial_outp(info, UART_LCR, 0);
- serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO);
scratch = serial_in(info, UART_IIR) >> 5;
if (scratch == 6)
state->type = PORT_16750;
@@ -3424,6 +3327,7 @@ __initfunc(int rs_init(void))
serial_driver.stop = rs_stop;
serial_driver.start = rs_start;
serial_driver.hangup = rs_hangup;
+ serial_driver.break_ctl = rs_break;
serial_driver.wait_until_sent = rs_wait_until_sent;
serial_driver.read_proc = rs_read_proc;
diff --git a/drivers/char/softdog.c b/drivers/char/softdog.c
index 2eda6ef10..be63f08b4 100644
--- a/drivers/char/softdog.c
+++ b/drivers/char/softdog.c
@@ -113,7 +113,7 @@ static void softdog_ping(void)
return;
}
-static long softdog_write(struct inode *inode, struct file *file, const char *data, unsigned long len)
+static ssize_t softdog_write(struct file *file, const char *data, size_t len, loff_t *ppos)
{
/*
* Refresh the timer.
diff --git a/drivers/char/stallion.c b/drivers/char/stallion.c
index 1ec03230b..8e378215f 100644
--- a/drivers/char/stallion.c
+++ b/drivers/char/stallion.c
@@ -889,7 +889,7 @@ static int stl_waitcarrier(stlport_t *portp, struct file *filp)
(doclocal || (portp->sigs & TIOCM_CD))) {
break;
}
- if (current->signal & ~current->blocked) {
+ if (signal_pending(current)) {
rc = -ERESTARTSYS;
break;
}
diff --git a/drivers/char/tpqic02.c b/drivers/char/tpqic02.c
index e3032bb85..df4dff23f 100644
--- a/drivers/char/tpqic02.c
+++ b/drivers/char/tpqic02.c
@@ -1750,11 +1750,10 @@ static long long qic02_tape_lseek(struct file * file, long long offset, int orig
* request would return the EOF flag for the previous file.
*/
-static long qic02_tape_read(struct inode * inode, struct file * filp,
- char * buf, unsigned long count)
+static ssize_t qic02_tape_read(struct file * filp, char * buf, size_t count, loff_t *ppos)
{
int err;
- kdev_t dev = inode->i_rdev;
+ kdev_t dev = filp->f_dentry->d_inode->i_rdev;
unsigned short flags = filp->f_flags;
unsigned long bytes_todo, bytes_done, total_bytes_done = 0;
int stat;
@@ -1925,7 +1924,7 @@ static long qic02_tape_read(struct inode * inode, struct file * filp,
{
status_bytes_rd = YES;
buf += bytes_done;
- filp->f_pos += bytes_done;
+ *ppos += bytes_done;
total_bytes_done += bytes_done;
count -= bytes_done;
}
@@ -1964,11 +1963,11 @@ static long qic02_tape_read(struct inode * inode, struct file * filp,
* tape device again. The driver will detect an exception status in (No Cartridge)
* and force a rewind. After that tar may continue writing.
*/
-static long qic02_tape_write(struct inode * inode, struct file * filp,
- const char * buf, unsigned long count)
+static ssize_t qic02_tape_write( struct file * filp, const char * buf,
+ size_t count, loff_t *ppos)
{
int err;
- kdev_t dev = inode->i_rdev;
+ kdev_t dev = filp->f_dentry->d_inode->i_rdev;
unsigned short flags = filp->f_flags;
unsigned long bytes_todo, bytes_done, total_bytes_done = 0;
@@ -2120,7 +2119,7 @@ static long qic02_tape_write(struct inode * inode, struct file * filp,
{
status_bytes_wr = YES;
buf += bytes_done;
- filp->f_pos += bytes_done;
+ *ppos += bytes_done;
total_bytes_done += bytes_done;
count -= bytes_done;
}
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
index 2148a06a6..a2f092b74 100644
--- a/drivers/char/tty_io.c
+++ b/drivers/char/tty_io.c
@@ -108,8 +108,8 @@ struct tty_struct * redirect = NULL;
static void initialize_tty_struct(struct tty_struct *tty);
-static long tty_read(struct inode *, struct file *, char *, unsigned long);
-static long tty_write(struct inode *, struct file *, const char *, unsigned long);
+static ssize_t tty_read(struct file *, char *, size_t, loff_t *);
+static ssize_t tty_write(struct file *, const char *, size_t, loff_t *);
static unsigned int tty_poll(struct file *, poll_table *);
static int tty_open(struct inode *, struct file *);
static int tty_release(struct inode *, struct file *);
@@ -304,15 +304,21 @@ int tty_check_change(struct tty_struct * tty)
return -ERESTARTSYS;
}
-static long hung_up_tty_read(struct inode * inode, struct file * file,
- char * buf, unsigned long count)
+static ssize_t hung_up_tty_read(struct file * file, char * buf,
+ size_t count, loff_t *ppos)
{
+ /* Can't seek (pread) on ttys. */
+ if (ppos != &file->f_pos)
+ return -ESPIPE;
return 0;
}
-static long hung_up_tty_write(struct inode * inode,
- struct file * file, const char * buf, unsigned long count)
+static ssize_t hung_up_tty_write(struct file * file, const char * buf,
+ size_t count, loff_t *ppos)
{
+ /* Can't seek (pwrite) on ttys. */
+ if (ppos != &file->f_pos)
+ return -ESPIPE;
return -EIO;
}
@@ -360,14 +366,18 @@ static struct file_operations hung_up_tty_fops = {
NULL /* hung_up_tty_fasync */
};
-void do_tty_hangup(struct tty_struct * tty, struct file_operations *fops)
+void do_tty_hangup(void *data)
{
-
+ struct tty_struct *tty = (struct tty_struct *) data;
struct file * filp;
struct task_struct *p;
+ unsigned long flags;
if (!tty)
return;
+
+ save_flags(flags); cli();
+
check_tty_count(tty, "do_tty_hangup");
for (filp = inuse_filps; filp; filp = filp->f_next) {
if (filp->private_data != tty)
@@ -381,7 +391,7 @@ void do_tty_hangup(struct tty_struct * tty, struct file_operations *fops)
if (filp->f_op != &tty_fops)
continue;
tty_fasync(filp, 0);
- filp->f_op = fops;
+ filp->f_op = &hung_up_tty_fops;
}
if (tty->ldisc.flush_buffer)
@@ -398,6 +408,8 @@ void do_tty_hangup(struct tty_struct * tty, struct file_operations *fops)
* Shutdown the current line discipline, and reset it to
* N_TTY.
*/
+ if (tty->driver.flags & TTY_DRIVER_RESET_TERMIOS)
+ *tty->termios = tty->driver.init_termios;
if (tty->ldisc.num != ldiscs[N_TTY].num) {
if (tty->ldisc.close)
(tty->ldisc.close)(tty);
@@ -429,10 +441,9 @@ void do_tty_hangup(struct tty_struct * tty, struct file_operations *fops)
tty->session = 0;
tty->pgrp = -1;
tty->ctrl_status = 0;
- if (tty->driver.flags & TTY_DRIVER_RESET_TERMIOS)
- *tty->termios = tty->driver.init_termios;
if (tty->driver.hangup)
(tty->driver.hangup)(tty);
+ restore_flags(flags);
}
void tty_hangup(struct tty_struct * tty)
@@ -440,7 +451,7 @@ void tty_hangup(struct tty_struct * tty)
#ifdef TTY_DEBUG_HANGUP
printk("%s hangup...\n", tty_name(tty));
#endif
- do_tty_hangup(tty, &hung_up_tty_fops);
+ queue_task(&tty->tq_hangup, &tq_timer);
}
void tty_vhangup(struct tty_struct * tty)
@@ -448,7 +459,7 @@ void tty_vhangup(struct tty_struct * tty)
#ifdef TTY_DEBUG_HANGUP
printk("%s vhangup...\n", tty_name(tty));
#endif
- do_tty_hangup(tty, &hung_up_tty_fops);
+ do_tty_hangup((void *) tty);
}
int tty_hung_up_p(struct file * filp)
@@ -543,13 +554,19 @@ void start_tty(struct tty_struct *tty)
wake_up_interruptible(&tty->write_wait);
}
-static long tty_read(struct inode * inode, struct file * file,
- char * buf, unsigned long count)
+static ssize_t tty_read(struct file * file, char * buf, size_t count,
+ loff_t *ppos)
{
int i;
struct tty_struct * tty;
+ struct inode *inode;
+
+ /* Can't seek (pread) on ttys. */
+ if (ppos != &file->f_pos)
+ return -ESPIPE;
tty = (struct tty_struct *)file->private_data;
+ inode = file->f_dentry->d_inode;
if (tty_paranoia_check(tty, inode->i_rdev, "tty_read"))
return -EIO;
if (!tty || (test_bit(TTY_IO_ERROR, &tty->flags)))
@@ -584,15 +601,14 @@ static long tty_read(struct inode * inode, struct file * file,
* Split writes up in sane blocksizes to avoid
* denial-of-service type attacks
*/
-static inline int do_tty_write(
- int (*write)(struct tty_struct *, struct file *, const unsigned char *, unsigned int),
- struct inode *inode,
+static inline ssize_t do_tty_write(
+ ssize_t (*write)(struct tty_struct *, struct file *, const unsigned char *, size_t),
struct tty_struct *tty,
struct file *file,
const unsigned char *buf,
- unsigned int count)
+ size_t count)
{
- int ret = 0, written = 0;
+ ssize_t ret = 0, written = 0;
for (;;) {
unsigned long size = PAGE_SIZE*2;
@@ -607,25 +623,31 @@ static inline int do_tty_write(
if (!count)
break;
ret = -ERESTARTSYS;
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
break;
if (need_resched)
schedule();
}
if (written) {
- inode->i_mtime = CURRENT_TIME;
+ file->f_dentry->d_inode->i_mtime = CURRENT_TIME;
ret = written;
}
return ret;
}
-static long tty_write(struct inode * inode, struct file * file,
- const char * buf, unsigned long count)
+static ssize_t tty_write(struct file * file, const char * buf, size_t count,
+ loff_t *ppos)
{
int is_console;
struct tty_struct * tty;
+ struct inode *inode;
+ /* Can't seek (pwrite) on ttys. */
+ if (ppos != &file->f_pos)
+ return -ESPIPE;
+
+ inode = file->f_dentry->d_inode;
is_console = (inode->i_rdev == CONSOLE_DEV);
if (is_console && redirect)
@@ -649,22 +671,23 @@ static long tty_write(struct inode * inode, struct file * file,
#endif
if (!tty->ldisc.write)
return -EIO;
- return do_tty_write(tty->ldisc.write,
- inode, tty, file,
- (const unsigned char *)buf,
- (unsigned int)count);
+ return do_tty_write(tty->ldisc.write, tty, file,
+ (const unsigned char *)buf, count);
}
/* Semaphore to protect creating and releasing a tty */
static struct semaphore tty_sem = MUTEX;
+
static void down_tty_sem(int index)
{
down(&tty_sem);
}
+
static void up_tty_sem(int index)
{
up(&tty_sem);
}
+
static void release_mem(struct tty_struct *tty, int idx);
/*
@@ -1117,27 +1140,9 @@ static void release_dev(struct file * filp)
}
/*
- * Make sure that the tty's task queue isn't activated. If it
- * is, take it out of the linked list. The tqueue isn't used by
- * pty's, so skip the test for them.
+ * Make sure that the tty's task queue isn't activated.
*/
- if (tty->driver.type != TTY_DRIVER_TYPE_PTY) {
- spin_lock_irq(&tqueue_lock);
- if (tty->flip.tqueue.sync) {
- struct tq_struct *tq, *prev;
-
- for (tq=tq_timer, prev=0; tq; prev=tq, tq=tq->next) {
- if (tq == &tty->flip.tqueue) {
- if (prev)
- prev->next = tq->next;
- else
- tq_timer = tq->next;
- break;
- }
- }
- }
- spin_unlock_irq(&tqueue_lock);
- }
+ run_task_queue(&tq_timer);
/*
* The release_mem function takes care of the details of clearing
@@ -1217,7 +1222,7 @@ retry_open:
release_dev(filp);
if (retval != -ERESTARTSYS)
return retval;
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
return retval;
schedule();
/*
@@ -1472,15 +1477,26 @@ static int tiocsetd(struct tty_struct *tty, int *arg)
{
int retval, ldisc;
- retval = tty_check_change(tty);
- if (retval)
- return retval;
retval = get_user(ldisc, arg);
if (retval)
return retval;
return tty_set_ldisc(tty, ldisc);
}
+static int send_break(struct tty_struct *tty, int duration)
+{
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + duration;
+
+ tty->driver.break_ctl(tty, -1);
+ if (!signal_pending(current))
+ schedule();
+ tty->driver.break_ctl(tty, 0);
+ if (signal_pending(current))
+ return -EINTR;
+ return 0;
+}
+
/*
* Split this up, as gcc can choke on it otherwise..
*/
@@ -1488,6 +1504,7 @@ static int tty_ioctl(struct inode * inode, struct file * file,
unsigned int cmd, unsigned long arg)
{
struct tty_struct *tty, *real_tty;
+ int retval;
tty = (struct tty_struct *)file->private_data;
if (tty_paranoia_check(tty, inode->i_rdev, "tty_ioctl"))
@@ -1498,6 +1515,46 @@ static int tty_ioctl(struct inode * inode, struct file * file,
tty->driver.subtype == PTY_TYPE_MASTER)
real_tty = tty->link;
+ /*
+ * Break handling by driver
+ */
+ if (!tty->driver.break_ctl) {
+ switch(cmd) {
+ case TIOCSBRK:
+ case TIOCCBRK:
+ return tty->driver.ioctl(tty, file, cmd, arg);
+
+ /* These two ioctl's always return success; even if */
+ /* the driver doesn't support them. */
+ case TCSBRK:
+ case TCSBRKP:
+ retval = tty->driver.ioctl(tty, file, cmd, arg);
+ if (retval == -ENOIOCTLCMD)
+ retval = 0;
+ return retval;
+ }
+ }
+
+ /*
+ * Factor out some common prep work
+ */
+ switch (cmd) {
+ case TIOCSETD:
+ case TIOCSBRK:
+ case TIOCCBRK:
+ case TCSBRK:
+ case TCSBRKP:
+ retval = tty_check_change(tty);
+ if (retval)
+ return retval;
+ if (cmd != TIOCCBRK) {
+ tty_wait_until_sent(tty, 0);
+ if (signal_pending(current))
+ return -EINTR;
+ }
+ break;
+ }
+
switch (cmd) {
case TIOCSTI:
return tiocsti(tty, (char *)arg);
@@ -1538,6 +1595,28 @@ static int tty_ioctl(struct inode * inode, struct file * file,
#endif
case TIOCTTYGSTRUCT:
return tiocttygstruct(tty, (struct tty_struct *) arg);
+
+ /*
+ * Break handling
+ */
+ case TIOCSBRK: /* Turn break on, unconditionally */
+ tty->driver.break_ctl(tty, -1);
+ return 0;
+
+ case TIOCCBRK: /* Turn break off, unconditionally */
+ tty->driver.break_ctl(tty, 0);
+ return 0;
+ case TCSBRK: /* SVID version: non-zero arg --> no break */
+ /*
+ * XXX is the above comment correct, or the
+ * code below correct? Is this ioctl used at
+ * all by anyone?
+ */
+ if (!arg)
+ return send_break(tty, HZ/4);
+ return 0;
+ case TCSBRKP: /* support for POSIX tcsendbreak() */
+ return send_break(tty, arg ? arg*(HZ/10) : HZ/4);
}
if (tty->driver.ioctl) {
int retval = (tty->driver.ioctl)(tty, file, cmd, arg);
@@ -1612,13 +1691,14 @@ static void flush_to_ldisc(void *private_)
unsigned char *cp;
char *fp;
int count;
+ unsigned long flags;
if (tty->flip.buf_num) {
cp = tty->flip.char_buf + TTY_FLIPBUF_SIZE;
fp = tty->flip.flag_buf + TTY_FLIPBUF_SIZE;
tty->flip.buf_num = 0;
- cli();
+ save_flags(flags); cli();
tty->flip.char_buf_ptr = tty->flip.char_buf;
tty->flip.flag_buf_ptr = tty->flip.flag_buf;
} else {
@@ -1626,22 +1706,57 @@ static void flush_to_ldisc(void *private_)
fp = tty->flip.flag_buf;
tty->flip.buf_num = 1;
- cli();
+ save_flags(flags); cli();
tty->flip.char_buf_ptr = tty->flip.char_buf + TTY_FLIPBUF_SIZE;
tty->flip.flag_buf_ptr = tty->flip.flag_buf + TTY_FLIPBUF_SIZE;
}
count = tty->flip.count;
tty->flip.count = 0;
- sti();
+ restore_flags(flags);
-#if 0
- if (count > tty->max_flip_cnt)
- tty->max_flip_cnt = count;
-#endif
tty->ldisc.receive_buf(tty, cp, fp, count);
}
/*
+ * Routine which returns the baud rate of the tty
+ */
+
+/*
+ * This is used to figure out the divisor speeds and the timeouts
+ */
+static int baud_table[] = {
+ 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
+ 9600, 19200, 38400, 57600, 115200, 230400, 460800, 0 };
+
+int tty_get_baud_rate(struct tty_struct *tty)
+{
+ unsigned int cflag, i;
+
+ cflag = tty->termios->c_cflag;
+
+ i = cflag & CBAUD;
+ if (i & CBAUDEX) {
+ i &= ~CBAUDEX;
+ if (i < 1 || i > 4)
+ tty->termios->c_cflag &= ~CBAUDEX;
+ else
+ i += 15;
+ }
+ if (i==15 && tty->alt_speed)
+ return(tty->alt_speed);
+
+ return baud_table[i];
+}
+
+void tty_flip_buffer_push(struct tty_struct *tty)
+{
+ if (tty->low_latency)
+ flush_to_ldisc((void *) tty);
+ else
+ queue_task(&tty->flip.tqueue, &tq_timer);
+}
+
+/*
* This subroutine initializes a tty structure.
*/
static void initialize_tty_struct(struct tty_struct *tty)
@@ -1655,6 +1770,8 @@ static void initialize_tty_struct(struct tty_struct *tty)
tty->flip.tqueue.routine = flush_to_ldisc;
tty->flip.tqueue.data = tty;
tty->flip.pty_sem = MUTEX;
+ tty->tq_hangup.routine = do_tty_hangup;
+ tty->tq_hangup.data = tty;
}
/*
diff --git a/drivers/char/tty_ioctl.c b/drivers/char/tty_ioctl.c
index 02f42ec87..21c09384e 100644
--- a/drivers/char/tty_ioctl.c
+++ b/drivers/char/tty_ioctl.c
@@ -60,7 +60,7 @@ void tty_wait_until_sent(struct tty_struct * tty, int timeout)
printk("waiting %s...(%d)\n", tty_name(tty), tty->driver.chars_in_buffer(tty));
#endif
current->state = TASK_INTERRUPTIBLE;
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
goto stop_waiting;
if (!tty->driver.chars_in_buffer(tty))
break;
@@ -170,7 +170,7 @@ static int set_termios(struct tty_struct * tty, unsigned long arg, int opt)
if (opt & TERMIOS_WAIT) {
tty_wait_until_sent(tty, 0);
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
return -EINTR;
}
@@ -509,18 +509,15 @@ int n_tty_ioctl(struct tty_struct * tty, struct file * file,
tty->packet = 0;
return 0;
}
- /* These two ioctl's always return success; even if */
- /* the driver doesn't support them. */
- case TCSBRK: case TCSBRKP:
- retval = tty_check_change(tty);
+ case TIOCGSOFTCAR:
+ return put_user(C_CLOCAL(tty) ? 1 : 0, (int *) arg);
+ case TIOCSSOFTCAR:
+ retval = get_user(arg, (unsigned int *) arg);
if (retval)
return retval;
- tty_wait_until_sent(tty, 0);
- if (current->signal & ~current->blocked)
- return -EINTR;
- if (!tty->driver.ioctl)
- return 0;
- tty->driver.ioctl(tty, file, cmd, arg);
+ tty->termios->c_cflag =
+ ((tty->termios->c_cflag & ~CLOCAL) |
+ (arg ? CLOCAL : 0));
return 0;
default:
return -ENOIOCTLCMD;
diff --git a/drivers/char/tuner.h b/drivers/char/tuner.h
new file mode 100644
index 000000000..5b3cf012f
--- /dev/null
+++ b/drivers/char/tuner.h
@@ -0,0 +1,59 @@
+/*
+ tuner.h - definition for different tuners
+
+ Copyright (C) 1997 Markus Schroeder (schroedm@uni-duesseldorf.de)
+ minor modifications by Ralph Metzler (rjkm@thp.uni-koeln.de)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _TUNER_H
+#define _TUNER_H
+
+#define TUNER_TEMIC_PAL 0 /* Miro Gpio Coding -1 */
+#define TUNER_PHILIPS_PAL_I 1
+#define TUNER_PHILIPS_NTSC 2
+#define TUNER_PHILIPS_SECAM 3
+#define TUNER_ABSENT 4
+#define TUNER_PHILIPS_PAL 5
+#define TUNER_TEMIC_NTSC 6
+#define TUNER_TEMIC_PAL_I 7
+
+#define NOTUNER 0
+#define PAL 1
+#define PAL_I 2
+#define NTSC 3
+#define SECAM 4
+
+#define NoTuner 0
+#define Philips 1
+#define TEMIC 2
+#define Sony 3
+
+struct tunertype {
+ char *name;
+ unchar Vendor;
+ unchar Type;
+
+ ushort thresh1; /* frequency Range for UHF,VHF-L, VHF_H */
+ ushort thresh2;
+ unchar VHF_L;
+ unchar VHF_H;
+ unchar UHF;
+ unchar config;
+ unchar I2C;
+};
+#endif
+
diff --git a/drivers/char/vc_screen.c b/drivers/char/vc_screen.c
index 9d2ecda3a..77fb908fe 100644
--- a/drivers/char/vc_screen.c
+++ b/drivers/char/vc_screen.c
@@ -103,12 +103,13 @@ static long long vcs_lseek(struct file *file, long long offset, int orig)
}
#define RETURN( x ) { enable_bh( CONSOLE_BH ); return x; }
-static long
-vcs_read(struct inode *inode, struct file *file, char *buf, unsigned long count)
+static ssize_t
+vcs_read(struct file *file, char *buf, size_t count, loff_t *ppos)
{
- int p = file->f_pos;
+ struct inode *inode = file->f_dentry->d_inode;
unsigned int currcons = MINOR(inode->i_rdev);
- int viewed, attr, size, read;
+ long p = *ppos;
+ long viewed, attr, size, read;
char *buf0;
unsigned short *org = NULL;
@@ -160,16 +161,17 @@ vcs_read(struct inode *inode, struct file *file, char *buf, unsigned long count)
put_user(func_scr_readw(org) & 0xff, buf++);
}
read = buf - buf0;
- file->f_pos += read;
+ *ppos += read;
RETURN( read );
}
-static long
-vcs_write(struct inode *inode, struct file *file, const char *buf, unsigned long count)
+static ssize_t
+vcs_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
{
- int p = file->f_pos;
+ struct inode *inode = file->f_dentry->d_inode;
unsigned int currcons = MINOR(inode->i_rdev);
- int viewed, attr, size, written;
+ long p = *ppos;
+ long viewed, attr, size, written;
const char *buf0;
unsigned short *org = NULL;
@@ -242,7 +244,7 @@ vcs_write(struct inode *inode, struct file *file, const char *buf, unsigned long
update_screen(currcons);
#endif
written = buf - buf0;
- file->f_pos += written;
+ *ppos += written;
RETURN( written );
}
diff --git a/drivers/char/videodev.c b/drivers/char/videodev.c
new file mode 100644
index 000000000..71e88f04c
--- /dev/null
+++ b/drivers/char/videodev.c
@@ -0,0 +1,264 @@
+/*
+ * Video capture interface for Linux
+ *
+ * A generic video device interface for the LINUX operating system
+ * using a set of device structures/vectors for low level operations.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Author: Alan Cox, <alan@cymru.net>
+ *
+ * Fixes:
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/videodev.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+
+#define VIDEO_NUM_DEVICES 256
+
+/*
+ * Active devices
+ */
+
+static struct video_device *video_device[VIDEO_NUM_DEVICES];
+
+/*
+ * Initialiser list
+ */
+
+struct video_init
+{
+ char *name;
+ int (*init)(struct video_init *);
+};
+
+extern int init_bttv_cards(struct video_init *);
+
+static struct video_init video_init_list[]={
+#ifdef CONFIG_VIDEO_BT848
+ {"bttv", init_bttv_cards},
+#endif
+#ifdef CONFIG_VIDEO_BWQCAM
+ {"bttv", init_bw_qcams},
+#endif
+#ifdef CONFIG_VIDEO_PMS
+ {"bttv", init_pms_cards},
+#endif
+ {"end", NULL}
+};
+
+
+/*
+ * Read will do some smarts later on. Buffer pin etc.
+ */
+
+static ssize_t video_read(struct file *file,
+ char *buf, size_t count, loff_t *ppos)
+{
+ int err;
+ struct video_device *vfl=video_device[MINOR(file->f_dentry->d_inode->i_rdev)];
+ return vfl->read(vfl, buf, count, file->f_flags&O_NONBLOCK);
+}
+
+/*
+ * Write for now does nothing. No reason it shouldnt do overlay setting
+ * for some boards I guess..
+ */
+
+static ssize_t video_write(struct file *file, const char *buf,
+ size_t count, loff_t *ppos)
+{
+ int err;
+ struct video_device *vfl=video_device[MINOR(file->f_dentry->d_inode->i_rdev)];
+ return vfl->write(vfl, buf, count, file->f_flags&O_NONBLOCK);
+}
+
+/*
+ * Open a video device.
+ */
+
+static int video_open(struct inode *inode, struct file *file)
+{
+ unsigned int minor = MINOR(inode->i_rdev);
+ int err;
+ struct video_device *vfl;
+
+ if(minor>=VIDEO_NUM_DEVICES)
+ return -ENODEV;
+
+ vfl=video_device[minor];
+ if(vfl==NULL)
+ return -ENODEV;
+ if(vfl->busy)
+ return -EBUSY;
+ vfl->busy=1; /* In case vfl->open sleeps */
+
+ if(vfl->open)
+ {
+ err=vfl->open(vfl,0); /* Tell the device it is open */
+ if(err)
+ {
+ vfl->busy=0;
+ return err;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Last close of a video for Linux device
+ */
+
+static int video_release(struct inode *inode, struct file *file)
+{
+ struct video_device *vfl=video_device[MINOR(inode->i_rdev)];
+ if(vfl->close)
+ vfl->close(vfl);
+ vfl->busy=0;
+ return 0;
+}
+
+/*
+ * Question: Should we be able to capture and then seek around the
+ * image ?
+ */
+
+static long long video_lseek(struct file * file,
+ long long offset, int origin)
+{
+ return -ESPIPE;
+}
+
+
+static int video_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct video_device *vfl=video_device[MINOR(inode->i_rdev)];
+ int err=vfl->ioctl(vfl, cmd, (void *)arg);
+
+ if(err!=-ENOIOCTLCMD)
+ return err;
+
+ switch(cmd)
+ {
+ default:
+ return -EINVAL;
+ }
+}
+
+/*
+ * We need to do MMAP support
+ */
+
+/*
+ * Video For Linux device drivers request registration here.
+ */
+
+int video_register_device(struct video_device *vfd)
+{
+ int i=0;
+ int base=0;
+ int err;
+
+ for(i=base;i<base+VIDEO_NUM_DEVICES;i++)
+ {
+ if(video_device[i]==NULL)
+ {
+ video_device[i]=vfd;
+ vfd->minor=i;
+ /* The init call may sleep so we book the slot out
+ then call */
+ MOD_INC_USE_COUNT;
+ err=vfd->initialize(vfd);
+ if(err<0)
+ {
+ video_device[i]=NULL;
+ MOD_DEC_USE_COUNT;
+ return err;
+ }
+ return 0;
+ }
+ }
+ return -ENFILE;
+}
+
+/*
+ * Unregister an unused video for linux device
+ */
+
+void video_unregister_device(struct video_device *vfd)
+{
+ if(video_device[vfd->minor]!=vfd)
+ panic("vfd: bad unregister");
+ video_device[vfd->minor]=NULL;
+ MOD_DEC_USE_COUNT;
+}
+
+
+static struct file_operations video_fops=
+{
+ video_lseek,
+ video_read,
+ video_write,
+ NULL, /* readdir */
+ NULL, /* poll */
+ video_ioctl,
+ NULL, /* mmap */
+ video_open,
+ video_release
+};
+
+/*
+ * Initialise video for linux
+ */
+
+int videodev_init(void)
+{
+ struct video_init *vfli = video_init_list;
+
+ printk(KERN_INFO "Linux video capture interface: v0.01 ALPHA\n");
+ if(register_chrdev(VIDEO_MAJOR,"video_capture", &video_fops))
+ {
+ printk("video_dev: unable to get major %d\n", VIDEO_MAJOR);
+ return -EIO;
+ }
+
+ /*
+ * Init kernel installed video drivers
+ */
+
+ while(vfli->init!=NULL)
+ {
+ vfli->init(vfli);
+ vfli++;
+ }
+ return 0;
+}
+
+
+int init_module(void)
+{
+ return videodev_init();
+}
+
+void cleanup_module(void)
+{
+ unregister_chrdev(VIDEO_MAJOR, "video_capture");
+}
+
+EXPORT_SYMBOL(video_register_device);
+EXPORT_SYMBOL(video_unregister_device);
diff --git a/drivers/char/vt.c b/drivers/char/vt.c
index 971426bf1..6ba0c687d 100644
--- a/drivers/char/vt.c
+++ b/drivers/char/vt.c
@@ -1094,7 +1094,7 @@ int vt_waitactive(int vt)
if (vt == fg_console)
break;
retval = -EINTR;
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
break;
schedule();
}
diff --git a/drivers/char/wdt.c b/drivers/char/wdt.c
index cbe763975..37f444e29 100644
--- a/drivers/char/wdt.c
+++ b/drivers/char/wdt.c
@@ -169,7 +169,7 @@ static void wdt_ping(void)
outb_p(0, WDT_DC);
}
-static long wdt_write(struct inode *inode, struct file *file, const char *buf, unsigned long count)
+static ssize_t wdt_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
{
if(count)
{
@@ -183,13 +183,13 @@ static long wdt_write(struct inode *inode, struct file *file, const char *buf, u
* Read reports the temperature in farenheit
*/
-static long wdt_read(struct inode *inode, struct file *file, char *buf, unsigned long count)
+static ssize_t wdt_read(struct file *file, char *buf, size_t count, loff_t *ptr)
{
unsigned short c=inb_p(WDT_RT);
unsigned char cp;
int err;
- switch(MINOR(inode->i_rdev))
+ switch(MINOR(file->f_dentry->d_inode->i_rdev))
{
case TEMP_MINOR:
err=verify_area(VERIFY_WRITE, buf, 1);