📄 fdc-io.c
字号:
/* * 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) timeout = schedule_timeout_interruptible(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) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -