diff options
Diffstat (limited to 'drivers/char/ftape/lowlevel')
35 files changed, 10785 insertions, 0 deletions
diff --git a/drivers/char/ftape/lowlevel/Makefile b/drivers/char/ftape/lowlevel/Makefile new file mode 100644 index 00000000000..febab07ba42 --- /dev/null +++ b/drivers/char/ftape/lowlevel/Makefile @@ -0,0 +1,43 @@ +# +# 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. +# + +obj-$(CONFIG_FTAPE) += ftape.o + +ftape-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 ftape_syms.o + +ifeq ($(CONFIG_FTAPE),y) +ftape-objs += ftape-setup.o +endif + +ifndef CONFIG_FT_NO_TRACE_AT_ALL +ftape-objs += ftape-tracing.o +endif + +ifeq ($(CONFIG_FT_PROC_FS),y) +ftape-objs += ftape-proc.o +endif diff --git a/drivers/char/ftape/lowlevel/fc-10.c b/drivers/char/ftape/lowlevel/fc-10.c new file mode 100644 index 00000000000..9bc1cddade7 --- /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 <asm/io.h> +#include <linux/ftape.h> +#include "../lowlevel/ftape-tracing.h" +#include "../lowlevel/fdc-io.h" +#include "../lowlevel/fc-10.h" + +static __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 +}; + +static __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 00000000000..da7b88bca88 --- /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 00000000000..1704a2a5704 --- /dev/null +++ b/drivers/char/ftape/lowlevel/fdc-io.c @@ -0,0 +1,1352 @@ +/* + * Copyright (C) 1993-1996 Bas Laarhoven, + * (C) 1996-1997 Claus-Justus Heine. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + * + * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/fdc-io.c,v $ + * $Revision: 1.7.4.2 $ + * $Date: 1997/11/16 14:48:17 $ + * + * This file contains the low-level floppy disk interface code + * for the QIC-40/80/3010/3020 floppy-tape driver "ftape" for + * Linux. + */ + +#include <linux/config.h> /* for CONFIG_FT_* */ +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <asm/system.h> +#include <asm/io.h> +#include <asm/dma.h> +#include <asm/irq.h> + +#include <linux/ftape.h> +#include <linux/qic117.h> +#include "../lowlevel/ftape-tracing.h" +#include "../lowlevel/fdc-io.h" +#include "../lowlevel/fdc-isr.h" +#include "../lowlevel/ftape-io.h" +#include "../lowlevel/ftape-rw.h" +#include "../lowlevel/ftape-ctl.h" +#include "../lowlevel/ftape-calibr.h" +#include "../lowlevel/fc-10.h" + +/* Global vars. + */ +static int ftape_motor; +volatile int ftape_current_cylinder = -1; +volatile fdc_mode_enum fdc_mode = fdc_idle; +fdc_config_info fdc; +DECLARE_WAIT_QUEUE_HEAD(ftape_wait_intr); + +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 spinlock_t fdc_io_lock; +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; /* 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; /* has fifo && lock set ? */ +static __u8 fdc_precomp; /* default precomp. value (nsec) */ +static __u8 fdc_prec_code; /* fdc precomp. select code */ + +static char ftape_id[] = "ftape"; /* used by request irq and free irq */ + +static int fdc_set_seek_rate(int seek_rate); + +void fdc_catch_stray_interrupts(int count) +{ + unsigned long flags; + + spin_lock_irqsave(&fdc_io_lock, flags); + if (count == 0) { + ft_expected_stray_interrupts = 0; + } else { + ft_expected_stray_interrupts += count; + } + spin_unlock_irqrestore(&fdc_io_lock, 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 ! + */ +static 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 ! */ +} + +static 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); +} + +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; + unsigned int time; +#endif + TRACE_FUN(ft_t_any); + + fdc_usec_wait(FT_RQM_DELAY); /* wait for valid RQM status */ + spin_lock_irqsave(&fdc_io_lock, flags); + if (!in_interrupt()) + /* 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 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 */ + spin_lock_irqsave(&fdc_io_lock, flags); + } + fdc_status = inb(fdc.msr); + if ((fdc_status & FDC_DATA_READY_MASK) != FDC_DATA_IN_READY) { + spin_unlock_irqrestore(&fdc_io_lock, 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 (!in_interrupt()) { + /* shouldn't be cleared if called from isr + */ + ft_interrupt_seen = 0; + } + 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 + spin_unlock_irqrestore(&fdc_io_lock, 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); + + spin_lock_irqsave(&fdc_io_lock, flags); + 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)) { + spin_unlock_irqrestore(&fdc_io_lock, 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; + } + } + spin_unlock_irqrestore(&fdc_io_lock, 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. + */ +static 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) +{ + DECLARE_WAITQUEUE(wait,current); + sigset_t old_sigmask; + static int resetting; + long timeout; + + TRACE_FUN(ft_t_fdc_dma); + + if (waitqueue_active(&ftape_wait_intr)) { + TRACE_ABORT(-EIO, ft_t_err, "error: nested call"); + } + /* timeout time will be up to USPT microseconds too long ! */ + timeout = (1000 * time + FT_USPT - 1) / FT_USPT; + + spin_lock_irq(¤t->sighand->siglock); + old_sigmask = current->blocked; + sigfillset(¤t->blocked); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&ftape_wait_intr, &wait); + while (!ft_interrupt_seen && timeout) { + set_current_state(TASK_INTERRUPTIBLE); + timeout = schedule_timeout(timeout); + } + + spin_lock_irq(¤t->sighand->siglock); + current->blocked = old_sigmask; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + 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 */ + 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 (signal_pending(current)) ? -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. + */ +static 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; + } |