From 230e5ab6a084ed50470f101934782dbf54b0d06b Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Sat, 6 Dec 1997 23:51:34 +0000 Subject: Merge with Linux 2.1.67. --- drivers/char/ChangeLog | 44 + drivers/char/Config.in | 27 +- drivers/char/Makefile | 52 +- drivers/char/README.cycladesZ | 8 + drivers/char/acquirewdt.c | 4 +- drivers/char/apm_bios.c | 14 +- drivers/char/atixlmouse.c | 10 +- drivers/char/bt848.h | 319 ++++ drivers/char/bttv.c | 2005 +++++++++++++++++++++++ drivers/char/bttv.h | 162 ++ drivers/char/busmouse.c | 8 +- drivers/char/cyclades.c | 1409 +++++++++------- drivers/char/epca.c | 2 +- drivers/char/esp.c | 148 +- drivers/char/ftape/Config.in | 38 + drivers/char/ftape/Makefile | 110 +- drivers/char/ftape/README.PCI | 4 +- drivers/char/ftape/RELEASE-NOTES | 399 +++++ drivers/char/ftape/calibr.c | 183 --- drivers/char/ftape/calibr.h | 39 - drivers/char/ftape/compressor/Makefile | 48 + drivers/char/ftape/compressor/lzrw3.c | 750 +++++++++ drivers/char/ftape/compressor/lzrw3.h | 253 +++ drivers/char/ftape/compressor/zftape-compress.c | 1317 +++++++++++++++ drivers/char/ftape/compressor/zftape-compress.h | 83 + drivers/char/ftape/ecc.c | 880 ---------- drivers/char/ftape/ecc.h | 85 - drivers/char/ftape/fc-10.c | 160 -- drivers/char/ftape/fc-10.h | 42 - drivers/char/ftape/fdc-io.c | 1300 --------------- drivers/char/ftape/fdc-io.h | 182 -- drivers/char/ftape/fdc-isr.c | 813 --------- drivers/char/ftape/fdc-isr.h | 56 - drivers/char/ftape/ftape-bsm.c | 428 ----- drivers/char/ftape/ftape-bsm.h | 62 - drivers/char/ftape/ftape-ctl.c | 883 ---------- drivers/char/ftape/ftape-ctl.h | 94 -- drivers/char/ftape/ftape-eof.c | 554 ------- drivers/char/ftape/ftape-eof.h | 55 - drivers/char/ftape/ftape-io.c | 1050 ------------ drivers/char/ftape/ftape-io.h | 77 - drivers/char/ftape/ftape-read.c | 677 -------- drivers/char/ftape/ftape-read.h | 45 - drivers/char/ftape/ftape-rw.c | 953 ----------- drivers/char/ftape/ftape-rw.h | 173 -- drivers/char/ftape/ftape-write.c | 723 -------- drivers/char/ftape/ftape-write.h | 48 - drivers/char/ftape/kernel-interface.c | 358 ---- drivers/char/ftape/kernel-interface.h | 65 - drivers/char/ftape/lowlevel/.cvsignore | 1 + drivers/char/ftape/lowlevel/Makefile | 60 + drivers/char/ftape/lowlevel/fc-10.c | 175 ++ drivers/char/ftape/lowlevel/fc-10.h | 39 + drivers/char/ftape/lowlevel/fdc-io.c | 1439 ++++++++++++++++ drivers/char/ftape/lowlevel/fdc-io.h | 257 +++ drivers/char/ftape/lowlevel/fdc-isr.c | 1185 ++++++++++++++ drivers/char/ftape/lowlevel/fdc-isr.h | 55 + drivers/char/ftape/lowlevel/ftape-bsm.c | 490 ++++++ drivers/char/ftape/lowlevel/ftape-bsm.h | 67 + drivers/char/ftape/lowlevel/ftape-buffer.c | 147 ++ drivers/char/ftape/lowlevel/ftape-buffer.h | 32 + drivers/char/ftape/lowlevel/ftape-calibr.c | 289 ++++ drivers/char/ftape/lowlevel/ftape-calibr.h | 37 + drivers/char/ftape/lowlevel/ftape-ctl.c | 901 ++++++++++ drivers/char/ftape/lowlevel/ftape-ctl.h | 172 ++ drivers/char/ftape/lowlevel/ftape-ecc.c | 853 ++++++++++ drivers/char/ftape/lowlevel/ftape-ecc.h | 84 + drivers/char/ftape/lowlevel/ftape-format.c | 342 ++++ drivers/char/ftape/lowlevel/ftape-format.h | 37 + drivers/char/ftape/lowlevel/ftape-init.c | 188 +++ drivers/char/ftape/lowlevel/ftape-init.h | 59 + drivers/char/ftape/lowlevel/ftape-io.c | 1018 ++++++++++++ drivers/char/ftape/lowlevel/ftape-io.h | 94 ++ drivers/char/ftape/lowlevel/ftape-proc.c | 431 +++++ drivers/char/ftape/lowlevel/ftape-proc.h | 37 + drivers/char/ftape/lowlevel/ftape-read.c | 614 +++++++ drivers/char/ftape/lowlevel/ftape-read.h | 51 + drivers/char/ftape/lowlevel/ftape-rw.c | 1091 ++++++++++++ drivers/char/ftape/lowlevel/ftape-rw.h | 121 ++ drivers/char/ftape/lowlevel/ftape-setup.c | 105 ++ drivers/char/ftape/lowlevel/ftape-tracing.c | 118 ++ drivers/char/ftape/lowlevel/ftape-tracing.h | 180 ++ drivers/char/ftape/lowlevel/ftape-write.c | 337 ++++ drivers/char/ftape/lowlevel/ftape-write.h | 53 + drivers/char/ftape/lowlevel/ftape_syms.c | 103 ++ drivers/char/ftape/lowlevel/ftape_syms.h | 39 + drivers/char/ftape/qic117.h | 280 ---- drivers/char/ftape/tracing.c | 103 -- drivers/char/ftape/tracing.h | 99 -- drivers/char/ftape/vendors.h | 127 -- drivers/char/ftape/zftape/.cvsignore | 1 + drivers/char/ftape/zftape/Makefile | 54 + drivers/char/ftape/zftape/zftape-buffers.c | 160 ++ drivers/char/ftape/zftape/zftape-buffers.h | 56 + drivers/char/ftape/zftape/zftape-ctl.c | 1502 +++++++++++++++++ drivers/char/ftape/zftape/zftape-ctl.h | 60 + drivers/char/ftape/zftape/zftape-eof.c | 207 +++ drivers/char/ftape/zftape/zftape-eof.h | 52 + drivers/char/ftape/zftape/zftape-init.c | 512 ++++++ drivers/char/ftape/zftape/zftape-init.h | 85 + drivers/char/ftape/zftape/zftape-read.c | 390 +++++ drivers/char/ftape/zftape/zftape-read.h | 53 + drivers/char/ftape/zftape/zftape-rw.c | 377 +++++ drivers/char/ftape/zftape/zftape-rw.h | 102 ++ drivers/char/ftape/zftape/zftape-vtbl.c | 758 +++++++++ drivers/char/ftape/zftape/zftape-vtbl.h | 229 +++ drivers/char/ftape/zftape/zftape-write.c | 496 ++++++ drivers/char/ftape/zftape/zftape-write.h | 38 + drivers/char/ftape/zftape/zftape_syms.c | 60 + drivers/char/ftape/zftape/zftape_syms.h | 39 + drivers/char/istallion.c | 16 +- drivers/char/joystick.c | 1051 ++++++++---- drivers/char/lp.c | 15 +- drivers/char/lp_m68k.c | 4 +- drivers/char/mem.c | 132 +- drivers/char/misc.c | 6 +- drivers/char/msbusmouse.c | 9 +- drivers/char/n_tty.c | 27 +- drivers/char/nvram.c | 15 +- drivers/char/pc110pad.c | 5 +- drivers/char/pcwd.c | 5 +- drivers/char/pcxx.c | 2 +- drivers/char/pms.c | 1069 ++++++++++++ drivers/char/psaux.c | 30 +- drivers/char/radio.c | 234 +++ drivers/char/random.c | 60 +- drivers/char/riscom8.c | 2 +- drivers/char/rocket.c | 63 +- drivers/char/rtc.c | 35 +- drivers/char/rtrack.c | 213 +++ drivers/char/rtrack.h | 25 + drivers/char/serial.c | 230 +-- drivers/char/softdog.c | 2 +- drivers/char/stallion.c | 2 +- drivers/char/tpqic02.c | 15 +- drivers/char/tty_io.c | 235 ++- drivers/char/tty_ioctl.c | 21 +- drivers/char/tuner.h | 59 + drivers/char/vc_screen.c | 22 +- drivers/char/videodev.c | 264 +++ drivers/char/vt.c | 2 +- drivers/char/wdt.c | 6 +- 142 files changed, 26141 insertions(+), 12075 deletions(-) create mode 100644 drivers/char/README.cycladesZ create mode 100644 drivers/char/bt848.h create mode 100644 drivers/char/bttv.c create mode 100644 drivers/char/bttv.h create mode 100644 drivers/char/ftape/Config.in delete mode 100644 drivers/char/ftape/calibr.c delete mode 100644 drivers/char/ftape/calibr.h create mode 100644 drivers/char/ftape/compressor/Makefile create mode 100644 drivers/char/ftape/compressor/lzrw3.c create mode 100644 drivers/char/ftape/compressor/lzrw3.h create mode 100644 drivers/char/ftape/compressor/zftape-compress.c create mode 100644 drivers/char/ftape/compressor/zftape-compress.h delete mode 100644 drivers/char/ftape/ecc.c delete mode 100644 drivers/char/ftape/ecc.h delete mode 100644 drivers/char/ftape/fc-10.c delete mode 100644 drivers/char/ftape/fc-10.h delete mode 100644 drivers/char/ftape/fdc-io.c delete mode 100644 drivers/char/ftape/fdc-io.h delete mode 100644 drivers/char/ftape/fdc-isr.c delete mode 100644 drivers/char/ftape/fdc-isr.h delete mode 100644 drivers/char/ftape/ftape-bsm.c delete mode 100644 drivers/char/ftape/ftape-bsm.h delete mode 100644 drivers/char/ftape/ftape-ctl.c delete mode 100644 drivers/char/ftape/ftape-ctl.h delete mode 100644 drivers/char/ftape/ftape-eof.c delete mode 100644 drivers/char/ftape/ftape-eof.h delete mode 100644 drivers/char/ftape/ftape-io.c delete mode 100644 drivers/char/ftape/ftape-io.h delete mode 100644 drivers/char/ftape/ftape-read.c delete mode 100644 drivers/char/ftape/ftape-read.h delete mode 100644 drivers/char/ftape/ftape-rw.c delete mode 100644 drivers/char/ftape/ftape-rw.h delete mode 100644 drivers/char/ftape/ftape-write.c delete mode 100644 drivers/char/ftape/ftape-write.h delete mode 100644 drivers/char/ftape/kernel-interface.c delete mode 100644 drivers/char/ftape/kernel-interface.h create mode 100644 drivers/char/ftape/lowlevel/.cvsignore create mode 100644 drivers/char/ftape/lowlevel/Makefile create mode 100644 drivers/char/ftape/lowlevel/fc-10.c create mode 100644 drivers/char/ftape/lowlevel/fc-10.h create mode 100644 drivers/char/ftape/lowlevel/fdc-io.c create mode 100644 drivers/char/ftape/lowlevel/fdc-io.h create mode 100644 drivers/char/ftape/lowlevel/fdc-isr.c create mode 100644 drivers/char/ftape/lowlevel/fdc-isr.h create mode 100644 drivers/char/ftape/lowlevel/ftape-bsm.c create mode 100644 drivers/char/ftape/lowlevel/ftape-bsm.h create mode 100644 drivers/char/ftape/lowlevel/ftape-buffer.c create mode 100644 drivers/char/ftape/lowlevel/ftape-buffer.h create mode 100644 drivers/char/ftape/lowlevel/ftape-calibr.c create mode 100644 drivers/char/ftape/lowlevel/ftape-calibr.h create mode 100644 drivers/char/ftape/lowlevel/ftape-ctl.c create mode 100644 drivers/char/ftape/lowlevel/ftape-ctl.h create mode 100644 drivers/char/ftape/lowlevel/ftape-ecc.c create mode 100644 drivers/char/ftape/lowlevel/ftape-ecc.h create mode 100644 drivers/char/ftape/lowlevel/ftape-format.c create mode 100644 drivers/char/ftape/lowlevel/ftape-format.h create mode 100644 drivers/char/ftape/lowlevel/ftape-init.c create mode 100644 drivers/char/ftape/lowlevel/ftape-init.h create mode 100644 drivers/char/ftape/lowlevel/ftape-io.c create mode 100644 drivers/char/ftape/lowlevel/ftape-io.h create mode 100644 drivers/char/ftape/lowlevel/ftape-proc.c create mode 100644 drivers/char/ftape/lowlevel/ftape-proc.h create mode 100644 drivers/char/ftape/lowlevel/ftape-read.c create mode 100644 drivers/char/ftape/lowlevel/ftape-read.h create mode 100644 drivers/char/ftape/lowlevel/ftape-rw.c create mode 100644 drivers/char/ftape/lowlevel/ftape-rw.h create mode 100644 drivers/char/ftape/lowlevel/ftape-setup.c create mode 100644 drivers/char/ftape/lowlevel/ftape-tracing.c create mode 100644 drivers/char/ftape/lowlevel/ftape-tracing.h create mode 100644 drivers/char/ftape/lowlevel/ftape-write.c create mode 100644 drivers/char/ftape/lowlevel/ftape-write.h create mode 100644 drivers/char/ftape/lowlevel/ftape_syms.c create mode 100644 drivers/char/ftape/lowlevel/ftape_syms.h delete mode 100644 drivers/char/ftape/qic117.h delete mode 100644 drivers/char/ftape/tracing.c delete mode 100644 drivers/char/ftape/tracing.h delete mode 100644 drivers/char/ftape/vendors.h create mode 100644 drivers/char/ftape/zftape/.cvsignore create mode 100644 drivers/char/ftape/zftape/Makefile create mode 100644 drivers/char/ftape/zftape/zftape-buffers.c create mode 100644 drivers/char/ftape/zftape/zftape-buffers.h create mode 100644 drivers/char/ftape/zftape/zftape-ctl.c create mode 100644 drivers/char/ftape/zftape/zftape-ctl.h create mode 100644 drivers/char/ftape/zftape/zftape-eof.c create mode 100644 drivers/char/ftape/zftape/zftape-eof.h create mode 100644 drivers/char/ftape/zftape/zftape-init.c create mode 100644 drivers/char/ftape/zftape/zftape-init.h create mode 100644 drivers/char/ftape/zftape/zftape-read.c create mode 100644 drivers/char/ftape/zftape/zftape-read.h create mode 100644 drivers/char/ftape/zftape/zftape-rw.c create mode 100644 drivers/char/ftape/zftape/zftape-rw.h create mode 100644 drivers/char/ftape/zftape/zftape-vtbl.c create mode 100644 drivers/char/ftape/zftape/zftape-vtbl.h create mode 100644 drivers/char/ftape/zftape/zftape-write.c create mode 100644 drivers/char/ftape/zftape/zftape-write.h create mode 100644 drivers/char/ftape/zftape/zftape_syms.c create mode 100644 drivers/char/ftape/zftape/zftape_syms.h create mode 100644 drivers/char/pms.c create mode 100644 drivers/char/radio.c create mode 100644 drivers/char/rtrack.c create mode 100644 drivers/char/rtrack.h create mode 100644 drivers/char/tuner.h create mode 100644 drivers/char/videodev.c (limited to 'drivers/char') 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 + + * 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 + + * 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 * 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 #include -#include +#include #include #include #include @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#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; ivbip=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; inext) && (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> 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 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;ibus, 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 +#include + +#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 @@ -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<= 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<= 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<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]<first_line; info = &cy_port[i]; info->last_active = jiffies; - save_car = base_addr[CyCAR<tty == 0){ - j = (base_addr[CyRIVR<tty; - j = (base_addr[CyRIVR<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<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<flip.flag_buf_ptr++ = TTY_PARITY; *tty->flip.char_buf_ptr++ = - base_addr[CyRDSR<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<flip.char_buf_ptr++ = + cy_readb(base_addr+(CyRDSR<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<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<first_line; - save_car = base_addr[CyCAR<last_active = jiffies; if(info->tty == 0){ - base_addr[CySRER<x_char) { /* send special char */ outch = info->x_char; - base_addr[CyTDR<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<x_break*200/HZ; - base_addr[CyTDR<= 0x48 ) { + /* It is a CD1400 rev. J or later */ + cy_writeb((u_long)base_addr + (CyTDR<x_break*500/HZ); + } else { + cy_writeb((u_long)base_addr + (CyTDR<x_break*200/HZ); + } + /* finish break */ + cy_writeb((u_long)base_addr + (CyTDR<x_break = 0; } while (char_count-- > 0){ if (!info->xmit_cnt){ - base_addr[CySRER<xmit_buf == 0){ - base_addr[CySRER<tty->stopped || info->tty->hw_stopped){ - base_addr[CySRER<xmit_cnt--; info->xmit_tail = (info->xmit_tail + 1) - & (PAGE_SIZE - 1); - base_addr[CyTDR< 1){ info->xmit_cnt--; info->xmit_tail = (info->xmit_tail + 1) - & (PAGE_SIZE - 1); - base_addr[CyTDR<first_line]; info->last_active = jiffies; - save_car = base_addr[CyCAR<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<tty->hw_stopped = 1; - base_addr[CySRER<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, ¶m) == 1){ + while(cyz_fetch_msg(cinfo, &channel, &cmd, ¶m) == 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<default_timeout + cy_writeb((ulong)base_addr+(CyRTPR<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<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]<tty || (info->tty->termios->c_cflag & HUPCL)) { - base_addr[CyMSVR1<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<flags & ASYNC_CALLOUT_ACTIVE) && !(info->flags & ASYNC_CLOSING) && (C_CLOCAL(tty) - || (base_addr[CyMSVR1<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]<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<= 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<= 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]<tco; - base_addr[CyTBPR<tbpr; - base_addr[CyRCOR<rco; - base_addr[CyRBPR<rbpr; + cy_writeb((u_long)base_addr+(CyTCOR<tco); + cy_writeb((u_long)base_addr+(CyTBPR<tbpr); + cy_writeb((u_long)base_addr+(CyRCOR<rco); + cy_writeb((u_long)base_addr+(CyRBPR<rbpr); /* set line characteristics according configuration */ - base_addr[CySCHR1<tty); - base_addr[CySCHR2<tty); - base_addr[CyCOR1<cor1; - base_addr[CyCOR2<cor2; - base_addr[CyCOR3<cor3; - base_addr[CyCOR4<cor4; - base_addr[CyCOR5<cor5; + cy_writeb((u_long)base_addr+(CySCHR1<tty)); + cy_writeb((u_long)base_addr+(CySCHR2<tty)); + cy_writeb((u_long)base_addr+(CyCOR1<cor1); + cy_writeb((u_long)base_addr+(CyCOR2<cor2); + cy_writeb((u_long)base_addr+(CyCOR3<cor3); + cy_writeb((u_long)base_addr+(CyCOR4<cor4); + cy_writeb((u_long)base_addr+(CyCOR5<cor5); cyy_issue_cmd(base_addr, CyCOR_CHANGE|CyCOR1ch|CyCOR2ch|CyCOR3ch,index); - base_addr[CyCAR<default_timeout - ? info->default_timeout - : 0x02); /* 10ms rx timeout */ + cy_writeb((u_long)base_addr+(CyCAR<default_timeout + ? info->default_timeout + : 0x02)); /* 10ms rx timeout */ if (C_CLOCAL(info->tty)) { - base_addr[CySRER<0 modem transitions */ - base_addr[CyMCOR1<rflow) { + cy_writeb((u_long)base_addr+(CyMCOR1<1 modem transitions */ - base_addr[CyMCOR2<0 modem transitions */ - base_addr[CyMCOR1<rflow) { + cy_writeb((u_long)base_addr+(CyMCOR1<1 modem transitions */ - base_addr[CyMCOR2<rtsdtr_inv) { + cy_writeb((u_long)base_addr+(CyMSVR1<rtsdtr_inv) { + cy_writeb((u_long)base_addr+(CyMSVR1<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]<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<rtsdtr_inv) { + cy_writeb((u_long)base_addr+(CyMSVR2<rtsdtr_inv) { + cy_writeb((u_long)base_addr+(CyMSVR1<rtsdtr_inv) { + cy_writeb((u_long)base_addr+(CyMSVR2<rtsdtr_inv) { + cy_writeb((u_long)base_addr+(CyMSVR1<rtsdtr_inv) { + cy_writeb((u_long)base_addr+(CyMSVR2<rtsdtr_inv) { + cy_writeb((u_long)base_addr+(CyMSVR2<rtsdtr_inv) { + cy_writeb((u_long)base_addr+(CyMSVR1<rtsdtr_inv) { + cy_writeb((u_long)base_addr+(CyMSVR1<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<cor3; + cy_writeb((u_long)base_addr+(CyCOR3<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]<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]<rtsdtr_inv) { + cy_writeb((u_long)base_addr+(CyMSVR2<ldisc.chars_in_buffer(tty), info->line); #endif @@ -3820,8 +4069,12 @@ cy_unthrottle(struct tty_struct * tty) + (cy_chip_offset[chip]<rtsdtr_inv) { + cy_writeb((u_long)base_addr+(CyMSVR2<= 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<= 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<= 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<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 #include #include +#include #include #include #include @@ -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, ==== + +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 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 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 +has resigned from maintaining ftape, and I, +Claus-Justus Heine , +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 + +- 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 -#include -#include -#include -#include - -#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/calibr.h b/drivers/char/ftape/calibr.h deleted file mode 100644 index 75bebe75f..000000000 --- a/drivers/char/ftape/calibr.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef _CALIBRATE_H -#define _CALIBRATE_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/calibr.h,v $ - $Author: bas $ - * - $Revision: 1.20 $ - $Date: 1995/04/22 07:30:15 $ - $State: Beta $ - * - * This file contains a gp calibration routine for - * hardware dependent timeout functions. - */ - -#include - -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); - -#endif 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 +#include + +/******************************************************************************/ +/* */ +/* 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 +#include +#include +#include + +#include + +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,6) +#include +#else +#include +#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/ecc.c b/drivers/char/ftape/ecc.c deleted file mode 100644 index 19708ee68..000000000 --- a/drivers/char/ftape/ecc.c +++ /dev/null @@ -1,880 +0,0 @@ -/* 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. - * - * $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 $ - * - * This file contains the Reed-Solomon error correction code - * for the QIC-40/80 floppy-tape driver for Linux. - */ - -#include -#include - -#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). - * - * The Galois Field in which coefficient arithmetic is performed are - * the polynomials over Z_2 (i.e., 0 and 1) modulo the irreducible - * polynomial f(r), where f(r)=r^8 + r^7 + r^2 + r + 1. A polynomial - * is represented as a byte with the MSB as the coefficient of r^7 and - * the LSB as the coefficient of r^0. For example, the binary - * representation of f(x) is 0x187 (of course, this doesn't fit into 8 - * bits). In this field, the polynomial r is a primitive element. - * That is, r^i with i in 0,...,255 enumerates all elements in the - * field. - * - * The generator polynomial for the QIC-80 ECC is - * - * g(x) = x^3 + r^105*x^2 + r^105*x + 1 - * - * which can be factored into: - * - * g(x) = (x-r^-1)(x-r^0)(x-r^1) - * - * the byte representation of the coefficients are: - * - * r^105 = 0xc0 - * r^-1 = 0xc3 - * r^0 = 0x01 - * r^1 = 0x02 - * - * 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. - * - */ - -typedef unsigned char 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[] = -{ - 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, - 0x87, 0x89, 0x95, 0xad, 0xdd, 0x3d, 0x7a, 0xf4, - 0x6f, 0xde, 0x3b, 0x76, 0xec, 0x5f, 0xbe, 0xfb, - 0x71, 0xe2, 0x43, 0x86, 0x8b, 0x91, 0xa5, 0xcd, - 0x1d, 0x3a, 0x74, 0xe8, 0x57, 0xae, 0xdb, 0x31, - 0x62, 0xc4, 0x0f, 0x1e, 0x3c, 0x78, 0xf0, 0x67, - 0xce, 0x1b, 0x36, 0x6c, 0xd8, 0x37, 0x6e, 0xdc, - 0x3f, 0x7e, 0xfc, 0x7f, 0xfe, 0x7b, 0xf6, 0x6b, - 0xd6, 0x2b, 0x56, 0xac, 0xdf, 0x39, 0x72, 0xe4, - 0x4f, 0x9e, 0xbb, 0xf1, 0x65, 0xca, 0x13, 0x26, - 0x4c, 0x98, 0xb7, 0xe9, 0x55, 0xaa, 0xd3, 0x21, - 0x42, 0x84, 0x8f, 0x99, 0xb5, 0xed, 0x5d, 0xba, - 0xf3, 0x61, 0xc2, 0x03, 0x06, 0x0c, 0x18, 0x30, - 0x60, 0xc0, 0x07, 0x0e, 0x1c, 0x38, 0x70, 0xe0, - 0x47, 0x8e, 0x9b, 0xb1, 0xe5, 0x4d, 0x9a, 0xb3, - 0xe1, 0x45, 0x8a, 0x93, 0xa1, 0xc5, 0x0d, 0x1a, - 0x34, 0x68, 0xd0, 0x27, 0x4e, 0x9c, 0xbf, 0xf9, - 0x75, 0xea, 0x53, 0xa6, 0xcb, 0x11, 0x22, 0x44, - 0x88, 0x97, 0xa9, 0xd5, 0x2d, 0x5a, 0xb4, 0xef, - 0x59, 0xb2, 0xe3, 0x41, 0x82, 0x83, 0x81, 0x85, - 0x8d, 0x9d, 0xbd, 0xfd, 0x7d, 0xfa, 0x73, 0xe6, - 0x4b, 0x96, 0xab, 0xd1, 0x25, 0x4a, 0x94, 0xaf, - 0xd9, 0x35, 0x6a, 0xd4, 0x2f, 0x5e, 0xbc, 0xff, - 0x79, 0xf2, 0x63, 0xc6, 0x0b, 0x16, 0x2c, 0x58, - 0xb0, 0xe7, 0x49, 0x92, 0xa3, 0xc1, 0x05, 0x0a, - 0x14, 0x28, 0x50, 0xa0, 0xc7, 0x09, 0x12, 0x24, - 0x48, 0x90, 0xa7, 0xc9, 0x15, 0x2a, 0x54, 0xa8, - 0xd7, 0x29, 0x52, 0xa4, 0xcf, 0x19, 0x32, 0x64, - 0xc8, 0x17, 0x2e, 0x5c, 0xb8, 0xf7, 0x69, 0xd2, - 0x23, 0x46, 0x8c, 0x9f, 0xb9, 0xf5, 0x6d, 0xda, - 0x33, 0x66, 0xcc, 0x1f, 0x3e, 0x7c, 0xf8, 0x77, - 0xee, 0x5b, 0xb6, 0xeb, 0x51, 0xa2, 0xc3, 0x01 -}; - -/* - * 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] = -{ - 0xff, 0x00, 0x01, 0x63, 0x02, 0xc6, 0x64, 0x6a, - 0x03, 0xcd, 0xc7, 0xbc, 0x65, 0x7e, 0x6b, 0x2a, - 0x04, 0x8d, 0xce, 0x4e, 0xc8, 0xd4, 0xbd, 0xe1, - 0x66, 0xdd, 0x7f, 0x31, 0x6c, 0x20, 0x2b, 0xf3, - 0x05, 0x57, 0x8e, 0xe8, 0xcf, 0xac, 0x4f, 0x83, - 0xc9, 0xd9, 0xd5, 0x41, 0xbe, 0x94, 0xe2, 0xb4, - 0x67, 0x27, 0xde, 0xf0, 0x80, 0xb1, 0x32, 0x35, - 0x6d, 0x45, 0x21, 0x12, 0x2c, 0x0d, 0xf4, 0x38, - 0x06, 0x9b, 0x58, 0x1a, 0x8f, 0x79, 0xe9, 0x70, - 0xd0, 0xc2, 0xad, 0xa8, 0x50, 0x75, 0x84, 0x48, - 0xca, 0xfc, 0xda, 0x8a, 0xd6, 0x54, 0x42, 0x24, - 0xbf, 0x98, 0x95, 0xf9, 0xe3, 0x5e, 0xb5, 0x15, - 0x68, 0x61, 0x28, 0xba, 0xdf, 0x4c, 0xf1, 0x2f, - 0x81, 0xe6, 0xb2, 0x3f, 0x33, 0xee, 0x36, 0x10, - 0x6e, 0x18, 0x46, 0xa6, 0x22, 0x88, 0x13, 0xf7, - 0x2d, 0xb8, 0x0e, 0x3d, 0xf5, 0xa4, 0x39, 0x3b, - 0x07, 0x9e, 0x9c, 0x9d, 0x59, 0x9f, 0x1b, 0x08, - 0x90, 0x09, 0x7a, 0x1c, 0xea, 0xa0, 0x71, 0x5a, - 0xd1, 0x1d, 0xc3, 0x7b, 0xae, 0x0a, 0xa9, 0x91, - 0x51, 0x5b, 0x76, 0x72, 0x85, 0xa1, 0x49, 0xeb, - 0xcb, 0x7c, 0xfd, 0xc4, 0xdb, 0x1e, 0x8b, 0xd2, - 0xd7, 0x92, 0x55, 0xaa, 0x43, 0x0b, 0x25, 0xaf, - 0xc0, 0x73, 0x99, 0x77, 0x96, 0x5c, 0xfa, 0x52, - 0xe4, 0xec, 0x5f, 0x4a, 0xb6, 0xa2, 0x16, 0x86, - 0x69, 0xc5, 0x62, 0xfe, 0x29, 0x7d, 0xbb, 0xcc, - 0xe0, 0xd3, 0x4d, 0x8c, 0xf2, 0x1f, 0x30, 0xdc, - 0x82, 0xab, 0xe7, 0x56, 0xb3, 0x93, 0x40, 0xd8, - 0x34, 0xb0, 0xef, 0x26, 0x37, 0x0c, 0x11, 0x44, - 0x6f, 0x78, 0x19, 0x9a, 0x47, 0x74, 0xa7, 0xc1, - 0x23, 0x53, 0x89, 0xfb, 0x14, 0x5d, 0xf8, 0x97, - 0x2e, 0x4b, 0xb9, 0x60, 0x0f, 0xed, 0x3e, 0xe5, - 0xf6, 0x87, 0xa5, 0x17, 0x3a, 0xa3, 0x3c, 0xb7 -}; - -/* - * This is a multiplication table for the factor - * 0xc0 (i.e., r^105 (modulo f(r)). - * gfmul_c0[f] returns r^105 * f(r) (modulo f(r)). - */ -static const unsigned char gfmul_c0[256] = -{ - 0x00, 0xc0, 0x07, 0xc7, 0x0e, 0xce, 0x09, 0xc9, - 0x1c, 0xdc, 0x1b, 0xdb, 0x12, 0xd2, 0x15, 0xd5, - 0x38, 0xf8, 0x3f, 0xff, 0x36, 0xf6, 0x31, 0xf1, - 0x24, 0xe4, 0x23, 0xe3, 0x2a, 0xea, 0x2d, 0xed, - 0x70, 0xb0, 0x77, 0xb7, 0x7e, 0xbe, 0x79, 0xb9, - 0x6c, 0xac, 0x6b, 0xab, 0x62, 0xa2, 0x65, 0xa5, - 0x48, 0x88, 0x4f, 0x8f, 0x46, 0x86, 0x41, 0x81, - 0x54, 0x94, 0x53, 0x93, 0x5a, 0x9a, 0x5d, 0x9d, - 0xe0, 0x20, 0xe7, 0x27, 0xee, 0x2e, 0xe9, 0x29, - 0xfc, 0x3c, 0xfb, 0x3b, 0xf2, 0x32, 0xf5, 0x35, - 0xd8, 0x18, 0xdf, 0x1f, 0xd6, 0x16, 0xd1, 0x11, - 0xc4, 0x04, 0xc3, 0x03, 0xca, 0x0a, 0xcd, 0x0d, - 0x90, 0x50, 0x97, 0x57, 0x9e, 0x5e, 0x99, 0x59, - 0x8c, 0x4c, 0x8b, 0x4b, 0x82, 0x42, 0x85, 0x45, - 0xa8, 0x68, 0xaf, 0x6f, 0xa6, 0x66, 0xa1, 0x61, - 0xb4, 0x74, 0xb3, 0x73, 0xba, 0x7a, 0xbd, 0x7d, - 0x47, 0x87, 0x40, 0x80, 0x49, 0x89, 0x4e, 0x8e, - 0x5b, 0x9b, 0x5c, 0x9c, 0x55, 0x95, 0x52, 0x92, - 0x7f, 0xbf, 0x78, 0xb8, 0x71, 0xb1, 0x76, 0xb6, - 0x63, 0xa3, 0x64, 0xa4, 0x6d, 0xad, 0x6a, 0xaa, - 0x37, 0xf7, 0x30, 0xf0, 0x39, 0xf9, 0x3e, 0xfe, - 0x2b, 0xeb, 0x2c, 0xec, 0x25, 0xe5, 0x22, 0xe2, - 0x0f, 0xcf, 0x08, 0xc8, 0x01, 0xc1, 0x06, 0xc6, - 0x13, 0xd3, 0x14, 0xd4, 0x1d, 0xdd, 0x1a, 0xda, - 0xa7, 0x67, 0xa0, 0x60, 0xa9, 0x69, 0xae, 0x6e, - 0xbb, 0x7b, 0xbc, 0x7c, 0xb5, 0x75, 0xb2, 0x72, - 0x9f, 0x5f, 0x98, 0x58, 0x91, 0x51, 0x96, 0x56, - 0x83, 0x43, 0x84, 0x44, 0x8d, 0x4d, 0x8a, 0x4a, - 0xd7, 0x17, 0xd0, 0x10, 0xd9, 0x19, 0xde, 0x1e, - 0xcb, 0x0b, 0xcc, 0x0c, 0xc5, 0x05, 0xc2, 0x02, - 0xef, 0x2f, 0xe8, 0x28, 0xe1, 0x21, 0xe6, 0x26, - 0xf3, 0x33, 0xf4, 0x34, 0xfd, 0x3d, 0xfa, 0x3a -}; - - -/* - * Returns V modulo 255 provided V is in the range -255,-254,...,509. - */ -static inline unsigned char mod255(int v) -{ - if (v > 0) { - if (v < 255) { - return v; - } else { - return v - 255; - } - } else { - return v + 255; - } -} - - -/* - * 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) -{ - return a ^ b; -} - - -/* - * Add two vectors of numbers in the field. Each byte in A and B get - * added individually. - */ -static inline unsigned long gfadd_long(unsigned long a, unsigned long b) -{ - return a ^ b; -} - - -/* - * Multiply two numbers in the field: - */ -static inline unsigned char gfmul(unsigned char a, unsigned char b) -{ - if (a && b) { - return gfpow[mod255(gflog[a] + gflog[b])]; - } else { - return 0; - } -} - - -/* - * 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) -{ - if (a) { - return gfpow[mod255(gflog[a] + b)]; - } else { - return 0; - } -} - - -/* - * 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; - - 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) - } 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 - } else { - TRACEx1(1, "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)). - */ -static inline unsigned char gfdiv(unsigned char a, unsigned char b) -{ - TRACE_FUN(8, "gfdiv"); - if (!b) { - TRACE(-1, "Error: division by zero"); - return 0xff; - } 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 - * 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., L00 CRC failures)"); - TRACE_EXIT; - return 0; - } - log_det = 255 - gflog[det]; - - /* - * 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[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); - - TRACE_EXIT; - return 1; -} - - -static inline int gfinv2(unsigned char l0, unsigned char l1, Matrix Ainv) -{ - TRACE_FUN(8, "gfinv2"); - unsigned char det; - unsigned char 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; - } - log_det = 255 - gflog[det]; - - /* - * 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. - */ -static inline void gfmat_mul(int n, Matrix A, unsigned char *s, unsigned char *b) -{ - int i, j; - unsigned char dot_prod; - - for (i = 0; i < n; ++i) { - dot_prod = 0; - for (j = 0; j < n; ++j) { - dot_prod = gfadd(dot_prod, gfmul(A[i][j], s[j])); - } - b[i] = dot_prod; - } -} - - - -/* - * 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. - * - * 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 - * 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: - * - * x^k * p(x) + m(x) = 0 (modulo g(x)) - * - * 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] - */ -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)); - 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: - * - * 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}) - * p2_i = (m_{i-1} - p0_{i-1}) - * - * With the initial condition: p0_0 = p1_0 = p2_0 = 0. - */ - t1 = gfadd_long(*data, p0); - /* - * 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) - } 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 - } else { - TRACEx1(1, "Error: long is of size %d", (int) sizeof(long)); - } - p0 = gfadd_long(t2, p1); - p1 = gfadd_long(t2, p2); - p2 = t1; - data += SECTOR_SIZE / sizeof(long); - } - *p = p0; - p += stride; - *p = p1; - p += stride; - *p = p2; - TRACE_EXIT; -} - - -/* - * 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]. - * - * S[i] is the value of the "message" polynomial m(x) evaluated at the - * i-th root of the generator polynomial g(x). - * - * As g(x)=(x-r^-1)(x-1)(x-r^1) we evaluate the message polynomial at - * x=r^-1 to get S[0], at x=r^0=1 to get S[1], and at x=r to get S[2]. - * This could be done directly and efficiently via the Horner scheme. - * However, it would require multiplication tables for the factors - * r^-1 (0xc3) and r (0x02). The following scheme does not require - * any multiplication tables beyond what's needed for set_parity() - * anyway and is slightly faster if there are no errors and slightly - * 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: - * - * x^k * p(x) = m(x) (modulo g(x)). - * - * That is, to evaluate m(r^m), where r^m is a root of g(x), we can - * simply evaluate (r^m)^k*p(r^m). Also, notice that p is 0 if and - * only if s is zero. That is, if all parity bytes are 0, we know - * there is no error in the data and consequently there is no need to - * compute s(x) at all! In all other cases, we compute s(x) from p(x) - * by evaluating (r^m)^k*p(r^m) for m=-1, m=0, and m=1. The p(x) - * polynomial is evaluated via the Horner scheme. - */ -static int compute_syndromes(unsigned long *data, int nblocks, unsigned long *s) -{ - unsigned long p[3]; - - 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. - */ - 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); - return 0; - } else { - return 1; - } -} - - -/* - * 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, - int nbad, int *bad_loc, Matrix Ainv, - unsigned char *s, - BAD_SECTOR * 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; - - 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 */ - 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; - } - error_mag[0] = s[1]; - break; - case 1: - t1 = gfadd(gfmul_exp(s[1], bad_loc[0]), s[2]); - t2 = gfadd(gfmul_exp(s[0], bad_loc[0]), s[1]); - if (t1 == 0 && t2 == 0) { - /* one erasure, no error: */ - 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; - } 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 (!gfinv2(bad_loc[0], bad_loc[1], Ainv)) { - /* inversion failed---must have more than one error */ - TRACE_EXIT; - return -1; - } - } - /* - * FALL THROUGH TO ERROR MAGNITUDE COMPUTATION: - */ - case 2: - case 3: - /* compute error magnitudes: */ - gfmat_mul(nbad, Ainv, s, error_mag); - break; - - default: - TRACE(1, "Internal Error: number of CRC errors > 3"); - TRACE_EXIT; - return -1; - } - - /* - * 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]; - c2 = s[2]; - for (i = 0; i < nbad; ++i) { - e = error_mag[i]; - if (e) { - /* correct the byte at offset L by magnitude E: */ - l = bad_loc[i]; - dp = &data[l * SECTOR_SIZE]; - *dp = gfadd(*dp, e); - *correction_map |= 1 << l; - ++ncorrected; - - log_error_mag = gflog[e]; - c0 = gfadd(c0, gfpow[mod255(log_error_mag - l)]); - c1 = gfadd(c1, e); - c2 = gfadd(c2, gfpow[mod255(log_error_mag + l)]); - } - } - if (c0 || c1 || c2) { - TRACE(1, "ECC self-check failed, too many errors"); - TRACE_EXIT; - return -1; - } - TRACE_EXIT; - return ncorrected; -} - - -#if defined(ECC_SANITY_CHECK) || defined(ECC_PARANOID) - -/* - * Perform a sanity check on the computed parity bytes: - */ -static int sanity_check(unsigned long *data, int nblocks) -{ - TRACE_FUN(8, "sanity_check"); - unsigned long s[3]; - - if (!compute_syndromes(data, nblocks, s)) { - TRACE(-1, "Internal Error: syndrome self-check failed"); - TRACE_EXIT; - return 0; - } - TRACE_EXIT; - return 1; -} - -#endif /* defined(ECC_SANITY_CHECK) || defined(ECC_PARANOID) */ - - - -/* - * Compute the parity for an entire segment of data. - */ -int ecc_set_segment_parity(struct memory_segment *mseg) -{ - int i; - unsigned char *parity_bytes; - - parity_bytes = &mseg->data[(mseg->blocks - 3) * SECTOR_SIZE]; - for (i = 0; i < SECTOR_SIZE; i += sizeof(long)) { - set_parity((unsigned long *) &mseg->data[i], mseg->blocks - 3, - (unsigned long *) &parity_bytes[i], - SECTOR_SIZE / sizeof(long)); -#ifdef ECC_PARANOID - if (!sanity_check((unsigned long *) &mseg->data[i], mseg->blocks)) { - return -1; - } -#endif /* ECC_PARANOID */ - } - return 0; -} - - -/* - * 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) -{ - 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]; - Matrix Ainv; - - mseg->corrected = 0; - - /* find first column that has non-zero syndromes: */ - for (col = 0; col < 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) { - /* all syndromes are ok, therefore nothing to correct */ - TRACE_EXIT; - return ECC_OK; - } - /* count the number of CRC errors if there were any: */ - if (mseg->read_bad) { - for (i = 0; i < mseg->blocks; i++) { - 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; - } /* if */ - erasure_loc[nerasures++] = i; - } - } - } - /* - * 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; - } - break; - case 3: - if (!gfinv3(erasure_loc[0], erasure_loc[1], erasure_loc[2], Ainv)) { - TRACE_EXIT; - return ECC_FAILED; - } - break; - default: - /* this is not an error condition... */ - break; - } - - do { - for (i = 0; i < sizeof(long); ++i) { - s[0] = ss[0]; - 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], - 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, - &mseg->corrected); -#else -#error "Huh? Neither __BIG_ENDIAN nor __LITTLE_ENDIAN?" -#endif - if (result < 0) { - TRACE_EXIT; - return ECC_FAILED; - } - ncorrected += result; - } - ss[0] >>= 8; - ss[1] >>= 8; - ss[2] >>= 8; - } - -#ifdef ECC_SANITY_CHECK - if (!sanity_check((unsigned long *) &mseg->data[col], mseg->blocks)) { - TRACE_EXIT; - return 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)) { - /* something is wrong---have to fix things */ - break; - } - } - } while (col < SECTOR_SIZE); - if (ncorrected && nerasures == 0) { - TRACE(2, "block contained error not caught by CRC"); - } - TRACEi((ncorrected > 0) ? 4 : 8, "number of corrections:", ncorrected); - TRACE_EXIT; - return ncorrected ? ECC_CORRECTED : ECC_OK; -} - -/*** end of ecc.c ***/ diff --git a/drivers/char/ftape/ecc.h b/drivers/char/ftape/ecc.h deleted file mode 100644 index 890543ceb..000000000 --- a/drivers/char/ftape/ecc.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * 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. - * - * - * $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 $ - * - * 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; -#define BAD_CLEAR(entry) ((entry)=0) -#define BAD_SET(entry,sector) ((entry)|=(1<<(sector))) -#define BAD_CHECK(entry,sector) ((entry)&(1<<(sector))) - -/* - * Return values for ecc_correct_data: - */ -enum { - ECC_OK, /* Data was correct. */ - ECC_CORRECTED, /* Correctable error in data. */ - ECC_FAILED, /* Could not correct data. */ -}; - -/* - * Representation of an in memory segment. MARKED_BAD lists the - * sectors that were marked bad during formatting. If the N-th sector - * in a segment is marked bad, bit 1< -#include - -#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[] = { - 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[] = { - 0x180, 0x210, 0x2A0, 0x300, 0x330, 0x340, 0x370 -}; - -int fc10_enable(void) -{ - int i; - byte cardConfig = 0x00; - byte x; - - /* Clear state machine ??? - */ - for (i = 0; i < NR_ITEMS(inbs_magic); i++) { - inb(FDC_BASE + inbs_magic[i]); - } - outb(0x0, FDC_BASE); - - x = inb(FDC_BASE); - if (x == 0x13 || x == 0x93) { - for (i = 1; i < 8; i++) { - if (inb(FDC_BASE + i) != x) { - return 0; - } - } - } else { - return 0; - } - - outb(0x8, FDC_BASE); - - for (i = 0; i < 8; i++) { - if (inb(FDC_BASE + i) != 0x0) { - return 0; - } - } - outb(0x10, FDC_BASE); - - for (i = 0; i < 8; i++) { - if (inb(FDC_BASE + i) != 0xff) { - return 0; - } - } - - /* Okay, we found a FC-10 card ! ??? - */ - outb(0x0, fdc.ccr); - - /* Clear state machine again ??? - */ - for (i = 0; i < NR_ITEMS(inbs_magic); i++) { - inb(FDC_BASE + inbs_magic[i]); - } - /* Send io port */ - for (i = 0; i < NR_ITEMS(fc10_ports); i++) - if (FDC_BASE == fc10_ports[i]) - cardConfig = i + 1; - if (cardConfig == 0) - return 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; - 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] */ - - /* 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); - - /* 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; -} - -#endif /* CMS_FC10_CONTROLLER */ diff --git a/drivers/char/ftape/fc-10.h b/drivers/char/ftape/fc-10.h deleted file mode 100644 index f38d4685e..000000000 --- a/drivers/char/ftape/fc-10.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef _FC_10_H -#define _FC_10_H - -/* - * Copyright (C) 1994 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/fc-10.h,v $ - $Author: bas $ - * - $Revision: 1.3 $ - $Date: 1995/01/08 14:16:21 $ - $State: Beta $ - * - * This file contains definitions for the FC-10 code - * of the QIC-40/80 floppy-tape driver for Linux. - */ - -/* - * fc-10.c defined global vars. - */ - -/* - * fc-10.c defined global functions. - */ -extern int fc10_enable(void); - -#endif 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 -#include -#include -#include -#include -#include -#include -#include - -#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(®[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 - -#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 -#include -#include - -#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/fdc-isr.h b/drivers/char/ftape/fdc-isr.h deleted file mode 100644 index 123d4168f..000000000 --- a/drivers/char/ftape/fdc-isr.h +++ /dev/null @@ -1,56 +0,0 @@ -#ifndef _FDC_ISR_H -#define _FDC_ISR_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-isr.h,v $ - $Author: bas $ - * - $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. - */ - -/* - * 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; - -/* - * fdc-io.c defined public functions - */ -extern void fdc_isr(void); - -/* - * A kernel hook that steals one interrupt from the floppy - * driver (Should be fixed when the new fdc driver gets ready) - * See the linux kernel source files: - * drivers/block/floppy.c & drivers/block/blk.h - * for the details. - */ -extern void (*do_floppy) (void); - -#endif 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 -#include - -#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-bsm.h b/drivers/char/ftape/ftape-bsm.h deleted file mode 100644 index e84363d45..000000000 --- a/drivers/char/ftape/ftape-bsm.h +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef _FTAPE_BSM_H -#define _FTAPE_BSM_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/ftape-bsm.h,v $ - $Author: bas $ - * - $Revision: 1.5 $ - $Date: 1995/04/22 07:30:15 $ - $State: Beta $ - * - * This file contains definitions for the bad sector map handling - * routines for the QIC-117 floppy-tape driver for Linux. - */ - -#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) - -/* - * ftape-io.c defined global vars. - */ -extern bad_sector_map_changed; - -/* - * ftape-io.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 ftape_init_bsm(void); - -#endif 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 -#include -#include -#include - -#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 "); - 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 "); - 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 "); - 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 -#include - -#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 -#include -#include - -#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-eof.h b/drivers/char/ftape/ftape-eof.h deleted file mode 100644 index 58df1f019..000000000 --- a/drivers/char/ftape/ftape-eof.h +++ /dev/null @@ -1,55 +0,0 @@ - - -#ifndef _FTAPE_EOF_H -#define _FTAPE_EOF_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/ftape-eof.h,v $ - $Author: bas $ - * - $Revision: 1.12 $ - $Date: 1995/04/22 07:30:15 $ - $State: Beta $ - * - * Definitions and declarations for the end of file markers - * for the QIC-40/80 floppy-tape driver for Linux. - */ - -/* ftape-eof.c defined global vars. - */ -extern int failed_sector_log_changed; -extern int eof_mark; - -/* 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); - -#endif 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 -#include -#include -#include -#include -#include -#include -#include - -#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 -#include -#include -#include -#include - -#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-read.h b/drivers/char/ftape/ftape-read.h deleted file mode 100644 index aeca35950..000000000 --- a/drivers/char/ftape/ftape-read.h +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef _FTAPE_READ_H -#define _FTAPE_READ_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/ftape-read.h,v $ - $Author: bas $ - * - $Revision: 1.13 $ - $Date: 1995/05/10 16:09:36 $ - $State: Beta $ - * - * 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); -extern void ftape_zap_read_buffers(void); - -#endif /* _FTAPE_READ_H */ 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 -#include -#include - -#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 -#include -#include -#include -#include - -#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/ftape-write.h b/drivers/char/ftape/ftape-write.h deleted file mode 100644 index 2cbf07668..000000000 --- a/drivers/char/ftape/ftape-write.h +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef _FTAPE_WRITE_H -#define _FTAPE_WRITE_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/ftape-write.h,v $ - $Author: bas $ - * - $Revision: 1.13 $ - $Date: 1995/04/22 07:30:15 $ - $State: Beta $ - * - * 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); -extern void ftape_zap_write_buffers(void); - -#endif /* _FTAPE_WRITE_H */ 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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/kernel-interface.h b/drivers/char/ftape/kernel-interface.h deleted file mode 100644 index 9843390c7..000000000 --- a/drivers/char/ftape/kernel-interface.h +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef _KERNEL_INTERFACE_H -#define _KERNEL_INTERFACE_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/kernel-interface.h,v $ - $Author: bas $ - * - $Revision: 1.24 $ - $Date: 1995/04/30 13:15:14 $ - $State: Beta $ - * - * ----Description---- - * - */ - -#include -#include - -#define _S(nr) (1<<((nr)-1)) -#define _NEVER_BLOCK (_S(SIGKILL)|_S(SIGSTOP)) -#define _DONT_BLOCK (_NEVER_BLOCK|_S(SIGINT)) -#define _DO_BLOCK (_S(SIGPIPE)) -#define _BLOCK_ALL (0xffffffffL) - - -#ifndef QIC117_TAPE_MAJOR -#define QIC117_TAPE_MAJOR 27 -#endif - -#define FTAPE_NO_REWIND 4 /* mask for minor nr */ - -/* kernel-interface.c defined global variables. - */ -extern byte *tape_buffer[]; -extern char kernel_version[]; - -/* kernel-interface.c defined global functions. - */ -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 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/lowlevel/fc-10.c b/drivers/char/ftape/lowlevel/fc-10.c new file mode 100644 index 000000000..cbfd71f49 --- /dev/null +++ b/drivers/char/ftape/lowlevel/fc-10.c @@ -0,0 +1,175 @@ +/* + * + + Copyright (C) 1993,1994 Jon Tombs. + + 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. + + The entire guts of this program was written by dosemu, modified to + record reads and writes to the ports in the 0x180-0x188 address space, + while running the CMS program TAPE.EXE V2.0.5 supplied with the drive. + + Modified to use an array of addresses and generally cleaned up (made + much shorter) 4 June 94, dosemu isn't that good at writing short code it + would seem :-). Made independent of 0x180, but I doubt it will work + at any other address. + + Modified for distribution with ftape source. 21 June 94, SJL. + + Modifications on 20 October 95, by Daniel Cohen (catman@wpi.edu): + Modified to support different DMA, IRQ, and IO Ports. Borland's + Turbo Debugger in virtual 8086 mode (TD386.EXE with hardware breakpoints + provided by the TDH386.SYS Device Driver) was used on the CMS program + TAPE V4.0.5. I set breakpoints on I/O to ports 0x180-0x187. Note that + CMS's program will not successfully configure the tape drive if you set + breakpoints on IO Reads, but you can set them on IO Writes without problems. + Known problems: + - You can not use DMA Channels 5 or 7. + + Modification on 29 January 96, by Daniel Cohen (catman@wpi.edu): + Modified to only accept IRQs 3 - 7, or 9. Since we can only send a 3 bit + number representing the IRQ to the card, special handling is required when + IRQ 9 is selected. IRQ 2 and 9 are the same, and we should request IRQ 9 + from the kernel while telling the card to use IRQ 2. Thanks to Greg + 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 +#include +#include "../lowlevel/ftape-tracing.h" +#include "../lowlevel/fdc-io.h" +#include "../lowlevel/fc-10.h" + +__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 +}; + +__u16 fc10_ports[] = { + 0x180, 0x210, 0x2A0, 0x300, 0x330, 0x340, 0x370 +}; + +int fc10_enable(void) +{ + int i; + __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(ft_fdc_base + inbs_magic[i]); + } + outb(0x0, ft_fdc_base); + + x = inb(ft_fdc_base); + if (x == 0x13 || x == 0x93) { + for (i = 1; i < 8; i++) { + if (inb(ft_fdc_base + i) != x) { + TRACE_EXIT 0; + } + } + } else { + TRACE_EXIT 0; + } + + outb(0x8, ft_fdc_base); + + for (i = 0; i < 8; i++) { + if (inb(ft_fdc_base + i) != 0x0) { + TRACE_EXIT 0; + } + } + outb(0x10, ft_fdc_base); + + for (i = 0; i < 8; i++) { + if (inb(ft_fdc_base + i) != 0xff) { + TRACE_EXIT 0; + } + } + + /* Okay, we found a FC-10 card ! ??? + */ + outb(0x0, fdc.ccr); + + /* Clear state machine again ??? + */ + for (i = 0; i < NR_ITEMS(inbs_magic); i++) { + inb(ft_fdc_base + inbs_magic[i]); + } + /* Send io port */ + for (i = 0; i < NR_ITEMS(fc10_ports); i++) + if (ft_fdc_base == fc10_ports[i]) + cardConfig = i + 1; + 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 (ft_fdc_irq != 9) + cardConfig |= ft_fdc_irq << 3; + else + cardConfig |= 2 << 3; + + /* and finally DMA Channel */ + 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.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(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; +} diff --git a/drivers/char/ftape/lowlevel/fc-10.h b/drivers/char/ftape/lowlevel/fc-10.h new file mode 100644 index 000000000..da7b88bca --- /dev/null +++ b/drivers/char/ftape/lowlevel/fc-10.h @@ -0,0 +1,39 @@ +#ifndef _FC_10_H +#define _FC_10_H + +/* + * 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 + 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/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. + */ + +/* + * fc-10.c defined global vars. + */ + +/* + * fc-10.c defined global functions. + */ +extern int fc10_enable(void); + +#endif 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#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(®[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 + +#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 +#include + +#define volatile /* */ + +#include +#include +#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/lowlevel/fdc-isr.h b/drivers/char/ftape/lowlevel/fdc-isr.h new file mode 100644 index 000000000..065aa9789 --- /dev/null +++ b/drivers/char/ftape/lowlevel/fdc-isr.h @@ -0,0 +1,55 @@ +#ifndef _FDC_ISR_H +#define _FDC_ISR_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-isr.h,v $ + * $Revision: 1.2 $ + * $Date: 1997/10/05 19:18:07 $ + * + * 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 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 + */ +extern void fdc_isr(void); + +/* + * A kernel hook that steals one interrupt from the floppy + * driver (Should be fixed when the new fdc driver gets ready) + * See the linux kernel source files: + * drivers/block/floppy.c & drivers/block/blk.h + * for the details. + */ +extern void (*do_floppy) (void); + +#endif 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 + +#include +#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/lowlevel/ftape-bsm.h b/drivers/char/ftape/lowlevel/ftape-bsm.h new file mode 100644 index 000000000..0dc7805da --- /dev/null +++ b/drivers/char/ftape/lowlevel/ftape-bsm.h @@ -0,0 +1,67 @@ +#ifndef _FTAPE_BSM_H +#define _FTAPE_BSM_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-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 +#include + +#define EMPTY_SEGMENT (0xffffffff) +#define FAKE_SEGMENT (0xfffffffe) + +/* 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-bsm.c defined global vars. + */ + +/* + * ftape-bsm.c defined global functions. + */ +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 +#include +#include +#include +#include +#include + +#include +#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 +#include +#include +#include +#include +#if defined(__alpha__) +# include +#elif defined(__i386__) +# include +#endif +#include +#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/lowlevel/ftape-calibr.h b/drivers/char/ftape/lowlevel/ftape-calibr.h new file mode 100644 index 000000000..0c7e75246 --- /dev/null +++ b/drivers/char/ftape/lowlevel/ftape-calibr.h @@ -0,0 +1,37 @@ +#ifndef _FTAPE_CALIBR_H +#define _FTAPE_CALIBR_H + +/* + * 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.h,v $ + * $Revision: 1.1 $ + * $Date: 1997/09/19 09:05:26 $ + * + * This file contains a gp calibration routine for + * hardware dependent timeout functions. + */ + +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 /* _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 +#include +#include +#include +#include + +#include +#include +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,6) +#include +#else +#include +#endif +#include + +#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 +#include +#include + +#include "../lowlevel/ftape-rw.h" +#include + +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/lowlevel/ftape-ecc.c b/drivers/char/ftape/lowlevel/ftape-ecc.c new file mode 100644 index 000000000..e5632f674 --- /dev/null +++ b/drivers/char/ftape/lowlevel/ftape-ecc.c @@ -0,0 +1,853 @@ +/* + * + * 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. + + * + * $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 + +#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 + * polynomial f(r), where f(r)=r^8 + r^7 + r^2 + r + 1. A polynomial + * is represented as a byte with the MSB as the coefficient of r^7 and + * the LSB as the coefficient of r^0. For example, the binary + * representation of f(x) is 0x187 (of course, this doesn't fit into 8 + * bits). In this field, the polynomial r is a primitive element. + * That is, r^i with i in 0,...,255 enumerates all elements in the + * field. + * + * The generator polynomial for the QIC-80 ECC is + * + * g(x) = x^3 + r^105*x^2 + r^105*x + 1 + * + * which can be factored into: + * + * g(x) = (x-r^-1)(x-r^0)(x-r^1) + * + * the byte representation of the coefficients are: + * + * r^105 = 0xc0 + * r^-1 = 0xc3 + * r^0 = 0x01 + * r^1 = 0x02 + * + * 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. + * + */ + +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 __u8 gfpow[] = +{ + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, + 0x87, 0x89, 0x95, 0xad, 0xdd, 0x3d, 0x7a, 0xf4, + 0x6f, 0xde, 0x3b, 0x76, 0xec, 0x5f, 0xbe, 0xfb, + 0x71, 0xe2, 0x43, 0x86, 0x8b, 0x91, 0xa5, 0xcd, + 0x1d, 0x3a, 0x74, 0xe8, 0x57, 0xae, 0xdb, 0x31, + 0x62, 0xc4, 0x0f, 0x1e, 0x3c, 0x78, 0xf0, 0x67, + 0xce, 0x1b, 0x36, 0x6c, 0xd8, 0x37, 0x6e, 0xdc, + 0x3f, 0x7e, 0xfc, 0x7f, 0xfe, 0x7b, 0xf6, 0x6b, + 0xd6, 0x2b, 0x56, 0xac, 0xdf, 0x39, 0x72, 0xe4, + 0x4f, 0x9e, 0xbb, 0xf1, 0x65, 0xca, 0x13, 0x26, + 0x4c, 0x98, 0xb7, 0xe9, 0x55, 0xaa, 0xd3, 0x21, + 0x42, 0x84, 0x8f, 0x99, 0xb5, 0xed, 0x5d, 0xba, + 0xf3, 0x61, 0xc2, 0x03, 0x06, 0x0c, 0x18, 0x30, + 0x60, 0xc0, 0x07, 0x0e, 0x1c, 0x38, 0x70, 0xe0, + 0x47, 0x8e, 0x9b, 0xb1, 0xe5, 0x4d, 0x9a, 0xb3, + 0xe1, 0x45, 0x8a, 0x93, 0xa1, 0xc5, 0x0d, 0x1a, + 0x34, 0x68, 0xd0, 0x27, 0x4e, 0x9c, 0xbf, 0xf9, + 0x75, 0xea, 0x53, 0xa6, 0xcb, 0x11, 0x22, 0x44, + 0x88, 0x97, 0xa9, 0xd5, 0x2d, 0x5a, 0xb4, 0xef, + 0x59, 0xb2, 0xe3, 0x41, 0x82, 0x83, 0x81, 0x85, + 0x8d, 0x9d, 0xbd, 0xfd, 0x7d, 0xfa, 0x73, 0xe6, + 0x4b, 0x96, 0xab, 0xd1, 0x25, 0x4a, 0x94, 0xaf, + 0xd9, 0x35, 0x6a, 0xd4, 0x2f, 0x5e, 0xbc, 0xff, + 0x79, 0xf2, 0x63, 0xc6, 0x0b, 0x16, 0x2c, 0x58, + 0xb0, 0xe7, 0x49, 0x92, 0xa3, 0xc1, 0x05, 0x0a, + 0x14, 0x28, 0x50, 0xa0, 0xc7, 0x09, 0x12, 0x24, + 0x48, 0x90, 0xa7, 0xc9, 0x15, 0x2a, 0x54, 0xa8, + 0xd7, 0x29, 0x52, 0xa4, 0xcf, 0x19, 0x32, 0x64, + 0xc8, 0x17, 0x2e, 0x5c, 0xb8, 0xf7, 0x69, 0xd2, + 0x23, 0x46, 0x8c, 0x9f, 0xb9, 0xf5, 0x6d, 0xda, + 0x33, 0x66, 0xcc, 0x1f, 0x3e, 0x7c, 0xf8, 0x77, + 0xee, 0x5b, 0xb6, 0xeb, 0x51, 0xa2, 0xc3, 0x01 +}; + +/* + * 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 __u8 gflog[256] = +{ + 0xff, 0x00, 0x01, 0x63, 0x02, 0xc6, 0x64, 0x6a, + 0x03, 0xcd, 0xc7, 0xbc, 0x65, 0x7e, 0x6b, 0x2a, + 0x04, 0x8d, 0xce, 0x4e, 0xc8, 0xd4, 0xbd, 0xe1, + 0x66, 0xdd, 0x7f, 0x31, 0x6c, 0x20, 0x2b, 0xf3, + 0x05, 0x57, 0x8e, 0xe8, 0xcf, 0xac, 0x4f, 0x83, + 0xc9, 0xd9, 0xd5, 0x41, 0xbe, 0x94, 0xe2, 0xb4, + 0x67, 0x27, 0xde, 0xf0, 0x80, 0xb1, 0x32, 0x35, + 0x6d, 0x45, 0x21, 0x12, 0x2c, 0x0d, 0xf4, 0x38, + 0x06, 0x9b, 0x58, 0x1a, 0x8f, 0x79, 0xe9, 0x70, + 0xd0, 0xc2, 0xad, 0xa8, 0x50, 0x75, 0x84, 0x48, + 0xca, 0xfc, 0xda, 0x8a, 0xd6, 0x54, 0x42, 0x24, + 0xbf, 0x98, 0x95, 0xf9, 0xe3, 0x5e, 0xb5, 0x15, + 0x68, 0x61, 0x28, 0xba, 0xdf, 0x4c, 0xf1, 0x2f, + 0x81, 0xe6, 0xb2, 0x3f, 0x33, 0xee, 0x36, 0x10, + 0x6e, 0x18, 0x46, 0xa6, 0x22, 0x88, 0x13, 0xf7, + 0x2d, 0xb8, 0x0e, 0x3d, 0xf5, 0xa4, 0x39, 0x3b, + 0x07, 0x9e, 0x9c, 0x9d, 0x59, 0x9f, 0x1b, 0x08, + 0x90, 0x09, 0x7a, 0x1c, 0xea, 0xa0, 0x71, 0x5a, + 0xd1, 0x1d, 0xc3, 0x7b, 0xae, 0x0a, 0xa9, 0x91, + 0x51, 0x5b, 0x76, 0x72, 0x85, 0xa1, 0x49, 0xeb, + 0xcb, 0x7c, 0xfd, 0xc4, 0xdb, 0x1e, 0x8b, 0xd2, + 0xd7, 0x92, 0x55, 0xaa, 0x43, 0x0b, 0x25, 0xaf, + 0xc0, 0x73, 0x99, 0x77, 0x96, 0x5c, 0xfa, 0x52, + 0xe4, 0xec, 0x5f, 0x4a, 0xb6, 0xa2, 0x16, 0x86, + 0x69, 0xc5, 0x62, 0xfe, 0x29, 0x7d, 0xbb, 0xcc, + 0xe0, 0xd3, 0x4d, 0x8c, 0xf2, 0x1f, 0x30, 0xdc, + 0x82, 0xab, 0xe7, 0x56, 0xb3, 0x93, 0x40, 0xd8, + 0x34, 0xb0, 0xef, 0x26, 0x37, 0x0c, 0x11, 0x44, + 0x6f, 0x78, 0x19, 0x9a, 0x47, 0x74, 0xa7, 0xc1, + 0x23, 0x53, 0x89, 0xfb, 0x14, 0x5d, 0xf8, 0x97, + 0x2e, 0x4b, 0xb9, 0x60, 0x0f, 0xed, 0x3e, 0xe5, + 0xf6, 0x87, 0xa5, 0x17, 0x3a, 0xa3, 0x3c, 0xb7 +}; + +/* 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 __u8 gfmul_c0[256] = +{ + 0x00, 0xc0, 0x07, 0xc7, 0x0e, 0xce, 0x09, 0xc9, + 0x1c, 0xdc, 0x1b, 0xdb, 0x12, 0xd2, 0x15, 0xd5, + 0x38, 0xf8, 0x3f, 0xff, 0x36, 0xf6, 0x31, 0xf1, + 0x24, 0xe4, 0x23, 0xe3, 0x2a, 0xea, 0x2d, 0xed, + 0x70, 0xb0, 0x77, 0xb7, 0x7e, 0xbe, 0x79, 0xb9, + 0x6c, 0xac, 0x6b, 0xab, 0x62, 0xa2, 0x65, 0xa5, + 0x48, 0x88, 0x4f, 0x8f, 0x46, 0x86, 0x41, 0x81, + 0x54, 0x94, 0x53, 0x93, 0x5a, 0x9a, 0x5d, 0x9d, + 0xe0, 0x20, 0xe7, 0x27, 0xee, 0x2e, 0xe9, 0x29, + 0xfc, 0x3c, 0xfb, 0x3b, 0xf2, 0x32, 0xf5, 0x35, + 0xd8, 0x18, 0xdf, 0x1f, 0xd6, 0x16, 0xd1, 0x11, + 0xc4, 0x04, 0xc3, 0x03, 0xca, 0x0a, 0xcd, 0x0d, + 0x90, 0x50, 0x97, 0x57, 0x9e, 0x5e, 0x99, 0x59, + 0x8c, 0x4c, 0x8b, 0x4b, 0x82, 0x42, 0x85, 0x45, + 0xa8, 0x68, 0xaf, 0x6f, 0xa6, 0x66, 0xa1, 0x61, + 0xb4, 0x74, 0xb3, 0x73, 0xba, 0x7a, 0xbd, 0x7d, + 0x47, 0x87, 0x40, 0x80, 0x49, 0x89, 0x4e, 0x8e, + 0x5b, 0x9b, 0x5c, 0x9c, 0x55, 0x95, 0x52, 0x92, + 0x7f, 0xbf, 0x78, 0xb8, 0x71, 0xb1, 0x76, 0xb6, + 0x63, 0xa3, 0x64, 0xa4, 0x6d, 0xad, 0x6a, 0xaa, + 0x37, 0xf7, 0x30, 0xf0, 0x39, 0xf9, 0x3e, 0xfe, + 0x2b, 0xeb, 0x2c, 0xec, 0x25, 0xe5, 0x22, 0xe2, + 0x0f, 0xcf, 0x08, 0xc8, 0x01, 0xc1, 0x06, 0xc6, + 0x13, 0xd3, 0x14, 0xd4, 0x1d, 0xdd, 0x1a, 0xda, + 0xa7, 0x67, 0xa0, 0x60, 0xa9, 0x69, 0xae, 0x6e, + 0xbb, 0x7b, 0xbc, 0x7c, 0xb5, 0x75, 0xb2, 0x72, + 0x9f, 0x5f, 0x98, 0x58, 0x91, 0x51, 0x96, 0x56, + 0x83, 0x43, 0x84, 0x44, 0x8d, 0x4d, 0x8a, 0x4a, + 0xd7, 0x17, 0xd0, 0x10, 0xd9, 0x19, 0xde, 0x1e, + 0xcb, 0x0b, 0xcc, 0x0c, 0xc5, 0x05, 0xc2, 0x02, + 0xef, 0x2f, 0xe8, 0x28, 0xe1, 0x21, 0xe6, 0x26, + 0xf3, 0x33, 0xf4, 0x34, 0xfd, 0x3d, 0xfa, 0x3a +}; + + +/* Returns V modulo 255 provided V is in the range -255,-254,...,509. + */ +static inline __u8 mod255(int v) +{ + if (v > 0) { + if (v < 255) { + return v; + } else { + return v - 255; + } + } else { + return v + 255; + } +} + + +/* 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 __u8 gfadd(__u8 a, __u8 b) +{ + return a ^ b; +} + + +/* 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) +{ + return a ^ b; +} + + +/* Multiply two numbers in the field: + */ +static inline __u8 gfmul(__u8 a, __u8 b) +{ + if (a && b) { + return gfpow[mod255(gflog[a] + gflog[b])]; + } else { + return 0; + } +} + + +/* Just like gfmul, except we have already looked up the log of the + * second number. + */ +static inline __u8 gfmul_exp(__u8 a, int b) +{ + if (a) { + return gfpow[mod255(gflog[a] + b)]; + } else { + return 0; + } +} + + +/* 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) +{ + __u8 t; + + if (sizeof(long) == 4) { + 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) { + 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 { + TRACE_FUN(ft_t_any); + TRACE_ABORT(-1, ft_t_err, "Error: size of long is %d bytes", + (int)sizeof(long)); + } +} + + +/* Divide two numbers in the field. Returns a/b (modulo f(x)). + */ +static inline __u8 gfdiv(__u8 a, __u8 b) +{ + if (!b) { + 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])]; + } +} + + +/* 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., L00 CRC failures)"); + } + log_det = 255 - gflog[det]; + + /* 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[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); + + return 1; +} + + +static inline int gfinv2(__u8 l0, __u8 l1, Matrix Ainv) +{ + __u8 det; + __u8 t1, t2; + int log_det; + + t1 = gfpow[255 - l0]; + t2 = gfpow[255 - l1]; + det = gfadd(t1, t2); + if (!det) { + 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: + */ + 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); + + 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. + */ +static inline void gfmat_mul(int n, Matrix A, + __u8 *s, __u8 *b) +{ + int i, j; + __u8 dot_prod; + + for (i = 0; i < n; ++i) { + dot_prod = 0; + for (j = 0; j < n; ++j) { + dot_prod = gfadd(dot_prod, gfmul(A[i][j], s[j])); + } + b[i] = dot_prod; + } +} + + + +/* 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. + * + * 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 + * 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: + * + * x^k * p(x) + m(x) = 0 (modulo g(x)) + * + * 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] + */ +static inline void set_parity(unsigned long *data, + int nblocks, + unsigned long *p, + int stride) +{ + unsigned long p0, p1, p2, t1, t2, *end; + + 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: + * + * 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}) + * p2_i = (m_{i-1} - p0_{i-1}) + * + * With the initial condition: p0_0 = p1_0 = p2_0 = 0. + */ + t1 = gfadd_long(*data, p0); + /* + * Multiply each byte in t1 by 0xc0: + */ + if (sizeof(long) == 4) { + 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= (((__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 { + 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 += FT_SECTOR_SIZE / sizeof(long); + } + *p = p0; + p += stride; + *p = p1; + p += stride; + *p = p2; + return; +} + + +/* 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]. + * + * S[i] is the value of the "message" polynomial m(x) evaluated at the + * i-th root of the generator polynomial g(x). + * + * As g(x)=(x-r^-1)(x-1)(x-r^1) we evaluate the message polynomial at + * x=r^-1 to get S[0], at x=r^0=1 to get S[1], and at x=r to get S[2]. + * This could be done directly and efficiently via the Horner scheme. + * However, it would require multiplication tables for the factors + * r^-1 (0xc3) and r (0x02). The following scheme does not require + * any multiplication tables beyond what's needed for set_parity() + * anyway and is slightly faster if there are no errors and slightly + * 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: + * + * x^k * p(x) = m(x) (modulo g(x)). + * + * That is, to evaluate m(r^m), where r^m is a root of g(x), we can + * simply evaluate (r^m)^k*p(r^m). Also, notice that p is 0 if and + * only if s is zero. That is, if all parity bytes are 0, we know + * there is no error in the data and consequently there is no need to + * compute s(x) at all! In all other cases, we compute s(x) from p(x) + * by evaluating (r^m)^k*p(r^m) for m=-1, m=0, and m=1. The p(x) + * polynomial is evaluated via the Horner scheme. + */ +static int compute_syndromes(unsigned long *data, int nblocks, unsigned long *s) +{ + unsigned long p[3]; + + 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. + */ + 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); + return 0; + } else { + return 1; + } +} + + +/* 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(__u8 *data, int nblocks, + int nbad, int *bad_loc, Matrix Ainv, + __u8 *s, + SectorMap * correction_map) +{ + int ncorrected = 0; + int i; + __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_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(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; + case 1: + t1 = gfadd(gfmul_exp(s[1], bad_loc[0]), s[2]); + t2 = gfadd(gfmul_exp(s[0], bad_loc[0]), s[1]); + if (t1 == 0 && t2 == 0) { + /* one erasure, no error: */ + Ainv[0][0] = gfpow[bad_loc[0]]; + } else if (t1 == 0 || t2 == 0) { + /* one erasure and more than one error: */ + 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(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 -1; + } + } + /* FALL THROUGH TO ERROR MAGNITUDE COMPUTATION: + */ + case 2: + case 3: + /* compute error magnitudes: */ + gfmat_mul(nbad, Ainv, s, error_mag); + break; + + default: + 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). + */ + c0 = s[0]; + c1 = s[1]; + c2 = s[2]; + for (i = 0; i < nbad; ++i) { + e = error_mag[i]; + if (e) { + /* correct the byte at offset L by magnitude E: */ + l = bad_loc[i]; + dp = &data[l * FT_SECTOR_SIZE]; + *dp = gfadd(*dp, e); + *correction_map |= 1 << l; + ++ncorrected; + + log_error_mag = gflog[e]; + c0 = gfadd(c0, gfpow[mod255(log_error_mag - l)]); + c1 = gfadd(c1, e); + c2 = gfadd(c2, gfpow[mod255(log_error_mag + l)]); + } + } + if (c0 || c1 || c2) { + TRACE_ABORT(-1, ft_t_err, + "ECC self-check failed, too many errors"); + } + TRACE_EXIT ncorrected; +} + + +#if defined(ECC_SANITY_CHECK) || defined(ECC_PARANOID) + +/* Perform a sanity check on the computed parity bytes: + */ +static int sanity_check(unsigned long *data, int nblocks) +{ + TRACE_FUN(ft_t_any); + unsigned long s[3]; + + if (!compute_syndromes(data, nblocks, s)) { + TRACE_ABORT(0, ft_bug, + "Internal Error: syndrome self-check failed"); + } + TRACE_EXIT 1; +} + +#endif /* defined(ECC_SANITY_CHECK) || defined(ECC_PARANOID) */ + +/* Compute the parity for an entire segment of data. + */ +int ftape_ecc_set_segment_parity(struct memory_segment *mseg) +{ + int i; + __u8 *parity_bytes; + + 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], + FT_SECTOR_SIZE / sizeof(long)); +#ifdef ECC_PARANOID + if (!sanity_check((unsigned long *) &mseg->data[i], + mseg->blocks)) { + return -1; + } +#endif /* ECC_PARANOID */ + } + return 0; +} + + +/* Checks and corrects (if possible) the segment MSEG. Returns one of + * ECC_OK, ECC_CORRECTED, and ECC_FAILED. + */ +int ftape_ecc_correct_data(struct memory_segment *mseg) +{ + int col, i, result; + int ncorrected = 0; + int nerasures = 0; /* # of erasures (CRC errors) */ + int erasure_loc[3]; /* erasure locations */ + unsigned long ss[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 < 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 >= FT_SECTOR_SIZE) { + /* all syndromes are ok, therefore nothing to correct */ + TRACE_EXIT ECC_OK; + } + /* count the number of CRC errors if there were any: */ + if (mseg->read_bad) { + for (i = 0; i < mseg->blocks; i++) { + if (BAD_CHECK(mseg->read_bad, i)) { + if (nerasures >= 3) { + /* this is too much for ECC */ + 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: + */ + switch (nerasures) { + case 2: + if (!gfinv2(erasure_loc[0], erasure_loc[1], Ainv)) { + TRACE_EXIT ECC_FAILED; + } + break; + case 3: + if (!gfinv3(erasure_loc[0], erasure_loc[1], + erasure_loc[2], Ainv)) { + TRACE_EXIT ECC_FAILED; + } + break; + default: + /* this is not an error condition... */ + break; + } + + do { + for (i = 0; i < sizeof(long); ++i) { + s[0] = ss[0]; + 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], + 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); +#endif + if (result < 0) { + TRACE_EXIT ECC_FAILED; + } + ncorrected += result; + } + ss[0] >>= 8; + ss[1] >>= 8; + ss[2] >>= 8; + } + +#ifdef ECC_SANITY_CHECK + 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)) < FT_SECTOR_SIZE) { + if (!compute_syndromes((unsigned long *) + &mseg->data[col], mseg->blocks, ss)) { + /* something is wrong---have to fix things */ + break; + } + } + } while (col < FT_SECTOR_SIZE); + if (ncorrected && nerasures == 0) { + TRACE(ft_t_warn, "block contained error not caught by CRC"); + } + TRACE((ncorrected > 0) ? ft_t_noise : ft_t_any, "number of corrections: %d", ncorrected); + TRACE_EXIT ncorrected ? ECC_CORRECTED : ECC_OK; +} diff --git a/drivers/char/ftape/lowlevel/ftape-ecc.h b/drivers/char/ftape/lowlevel/ftape-ecc.h new file mode 100644 index 000000000..4829146fe --- /dev/null +++ b/drivers/char/ftape/lowlevel/ftape-ecc.h @@ -0,0 +1,84 @@ +#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. + + * + * $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. + */ + +#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))) + +/* + * Return values for ecc_correct_data: + */ +enum { + ECC_OK, /* Data was correct. */ + ECC_CORRECTED, /* Correctable error in data. */ + ECC_FAILED, /* Could not correct data. */ +}; + +/* + * Representation of an in memory segment. MARKED_BAD lists the + * sectors that were marked bad during formatting. If the N-th sector + * in a segment is marked bad, bit 1< +#include + +#include +#include +#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 +#include +#include +#include +#include +#include +#include +#include + +#include +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,16) +#include +#else +#define __initdata +#define __initfunc(__arg) __arg +#endif +#include +#ifdef CONFIG_ZFTAPE +#include +#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/lowlevel/ftape-init.h b/drivers/char/ftape/lowlevel/ftape-init.h new file mode 100644 index 000000000..eae86b3dc --- /dev/null +++ b/drivers/char/ftape/lowlevel/ftape-init.h @@ -0,0 +1,59 @@ +#ifndef _FTAPE_INIT_H +#define _FTAPE_INIT_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-init.h,v $ + * $Revision: 1.2 $ + * $Date: 1997/10/05 19:18:16 $ + * + * This file contains the definitions for the interface to + * the Linux kernel for floppy tape driver ftape. + * + */ + +#include +#include + +#define _S(nr) (1<<((nr)-1)) +#define _NEVER_BLOCK (_S(SIGKILL)|_S(SIGSTOP)) +#define _DONT_BLOCK (_NEVER_BLOCK|_S(SIGINT)) +#define _DO_BLOCK (_S(SIGPIPE)) +#define _BLOCK_ALL (0xffffffffL) + + +#ifndef QIC117_TAPE_MAJOR +#define QIC117_TAPE_MAJOR 27 +#endif + +/* ftape-init.c defined global variables. + */ +#if defined(MODULE) && LINUX_VERSION_CODE <= KERNEL_VER(1,2,13) +extern char kernel_version[]; +#endif + +/* ftape-init.c defined global functions not defined in ftape.h + */ +#ifdef MODULE +asmlinkage extern int init_module (void); +asmlinkage extern void cleanup_module(void); +#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 +#include +#include +#include +#include +#include +#include + +#include +#include +#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 +#include + +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 + +#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 + +#include +#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 +#else +#define __initdata +#define __initfunc(__arg) __arg +#endif +#include + + +#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 /* 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 + * entry for each WAN device + */ + +/* + * Generic /proc/net/ftape/ 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 + +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 +#include +#include +#include + +#include +#include +#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/lowlevel/ftape-read.h b/drivers/char/ftape/lowlevel/ftape-read.h new file mode 100644 index 000000000..069f99f2a --- /dev/null +++ b/drivers/char/ftape/lowlevel/ftape-read.h @@ -0,0 +1,51 @@ +#ifndef _FTAPE_READ_H +#define _FTAPE_READ_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-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 functions. + */ +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 +#include + +#include +#include +#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 + +#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 +#include +#include +#include +#include + +#include +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,16) +#include +#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 +#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 +#include + +/* + * 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 +#include +#include +#include + +#include +#include +#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/lowlevel/ftape-write.h b/drivers/char/ftape/lowlevel/ftape-write.h new file mode 100644 index 000000000..0e7f898b7 --- /dev/null +++ b/drivers/char/ftape/lowlevel/ftape-write.h @@ -0,0 +1,53 @@ +#ifndef _FTAPE_WRITE_H +#define _FTAPE_WRITE_H + +/* + * 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 + 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.h,v $ + $Author: claus $ + * + $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 functions. + */ +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 +#define __NO_VERSION__ +#include + +#include +#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 +#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 +}; +#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 +/* 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 - -#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 - -#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 +#include +#include +#include + +#include + +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,0) +#include +#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 +#include +#include +#define __NO_VERSION__ +#include +#ifdef CONFIG_KERNELD +#include +#endif +#include + +#include + +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,6) +#include +#else +#include +#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 +#include +#include + +#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 +#include + +#include + +#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/zftape/zftape-eof.h b/drivers/char/ftape/zftape/zftape-eof.h new file mode 100644 index 000000000..26568c26c --- /dev/null +++ b/drivers/char/ftape/zftape/zftape-eof.h @@ -0,0 +1,52 @@ +#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 + 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.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 +#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 zft_nr_eof_marks; +extern eof_mark_union *zft_eof_map; + +/* ftape-eof.c defined global functions. + */ +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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_KERNELD +#include +#endif +#include +#include + +#include +#if LINUX_VERSION_CODE >=KERNEL_VER(2,1,16) +#include +#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 + +#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 +#include +#include +#ifdef CONFIG_KERNELD +#include +#endif + +#include + +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,6) +#include +#else +#include +#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 +#include +#include + +#include +#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 +#include +#include +#include + +#include +#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 + +#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 +#include +#include +#ifdef CONFIG_KERNELD +#include +#endif + +#include + +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,6) +#include +#else +#include +#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 +#define __NO_VERSION__ +#include + +#include + +#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 +#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 +}; +#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 +/* 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 - 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 - 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 +#include #include -#include +#include +#include #include +#include +#include +#include +#include #include -#include +#include + #include +#include #include +#include -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 "); +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<x, &js_axis[i].value, sizeof(int)); + + i++; + while (~jsd[minor].exist & (1<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 + * Reverted interrupt to polling at runtime if more than one device is parport + * registered and joined the interrupt and polling code. + * by Andrea Arcangeli */ /* 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 - PS/2 emulation * 0.2 1997-06-03 Robin O'Leary - tap gesture * 0.3 1997-06-27 Alan Cox - 2.1 commit + * 0.4 1997-11-09 Alan Cox - Single Unix VFS API changes */ #include @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#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;i255) + 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;i32) + { + 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;ydw) /* 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 + * + */ + +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include +#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 +#include +#include + +#include +#include +#include + +#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 + +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 * + * 8/97: Fix bug in rs_set_termios with RTS + * Stanislav V. Voronyi + * * 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) { @@ -1000,28 +996,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 @@ -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,21 +1706,56 @@ 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. */ @@ -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, + * + * Fixes: + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +#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;iminor=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); -- cgit v1.2.3