📄 fdc-io.c
字号:
/* Yo, Emacs! we're -*- Linux-C -*- * * Copyright (C) 1993-1995 Bas Laarhoven. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * * This file contains the low-level floppy disk interface code * for the QIC-40/80 tape streamer device driver. */#include <linux/errno.h>#include <linux/sched.h>#include <linux/ioport.h>#include <linux/ftape.h>#include <asm/system.h>#include <asm/io.h>#include <asm/dma.h>#include <asm/irq.h>#include "tracing.h"#include "fdc-io.h"#include "fdc-isr.h"#include "ftape-io.h"#include "ftape-rw.h"#include "calibr.h"#include "fc-10.h"#include "qic117.h"/* Global vars. */int ftape_unit = -1;int ftape_motor = 0;int current_cylinder = -1;fdc_mode_enum fdc_mode = fdc_idle;fdc_config_info fdc = {0};/* Local vars. */static int fdc_calibr_count;static int fdc_calibr_time;static int fdc_confused = 0;static int fdc_status;volatile byte fdc_head; /* FDC head */volatile byte fdc_cyl; /* FDC track */volatile byte fdc_sect; /* FDC sector */static int fdc_data_rate = 0; /* default rate = 500 Kbps */static int fdc_seek_rate = 14; /* default rate = 2 msec @ 500 Kbps */static void (*do_ftape) (void);static int fdc_fifo_state; /* original fifo setting - fifo enabled */static int fdc_fifo_thr; /* original fifo setting - threshold */static int fdc_lock_state; /* original lock setting - locked */static int fdc_fifo_locked = 0; /* has fifo && lock set ? */static byte fdc_precomp = 0; /* sets fdc to default precomp. value */static byte fdc_drv_spec[4]; /* drive specification bytes for i82078 */static int perpend_mode; /* true if fdc is in perpendicular mode */static char ftape_id[] = "ftape"; /* used by request irq and free irq */void fdc_catch_stray_interrupts(unsigned count){ unsigned long flags; save_flags(flags); cli(); if (count == 0) { expected_stray_interrupts = 0; } else { expected_stray_interrupts += count; } restore_flags(flags);}/* Wait during a timeout period for a given FDC status. * If usecs == 0 then just test status, else wait at least for usecs. * Returns -ETIME on timeout. Function must be calibrated first ! */int fdc_wait(int usecs, byte mask, byte state){ int count_1 = (fdc_calibr_count * usecs - 1) / fdc_calibr_time; do { fdc_status = inb_p(fdc.msr); if ((fdc_status & mask) == state) { return 0; } } while (count_1-- >= 0); return -ETIME;}int fdc_ready_wait(int usecs){ return fdc_wait(usecs, FDC_DATA_READY, FDC_DATA_READY);}static void fdc_usec_wait(int usecs){ fdc_wait(usecs, 0, 1); /* will always timeout ! */}int fdc_ready_out_wait(int usecs){ fdc_usec_wait(RQM_DELAY); /* wait for valid RQM status */ return fdc_wait(usecs, FDC_DATA_OUT_READY, FDC_DATA_OUT_READY);}int fdc_ready_in_wait(int usecs){ fdc_usec_wait(RQM_DELAY); /* wait for valid RQM status */ return fdc_wait(usecs, FDC_DATA_OUT_READY, FDC_DATA_IN_READY);}int fdc_wait_calibrate(void){ return calibrate("fdc_wait", fdc_usec_wait, &fdc_calibr_count, &fdc_calibr_time);}/* Wait for a (short) while for the FDC to become ready * and transfer the next command byte. * Return -ETIME on timeout on getting ready (depends on hardware!). */int fdc_write(byte data){ fdc_usec_wait(RQM_DELAY); /* wait for valid RQM status */ if (fdc_wait(150, FDC_DATA_READY_MASK, FDC_DATA_IN_READY) < 0) { return -ETIME; } else { outb(data, fdc.fifo); return 0; }}/* Wait for a (short) while for the FDC to become ready * and transfer the next result byte. * Return -ETIME if timeout on getting ready (depends on hardware!). */int fdc_read(byte * data){ fdc_usec_wait(RQM_DELAY); /* wait for valid RQM status */ if (fdc_wait(150, FDC_DATA_READY_MASK, FDC_DATA_OUT_READY) < 0) { return -ETIME; } else { *data = inb(fdc.fifo); return 0; }}/* Output a cmd_len long command string to the FDC. * The FDC should be ready to receive a new command or * an error (EBUSY) will occur. */int fdc_command(byte * cmd_data, int cmd_len){ TRACE_FUN(8, "fdc_command"); int result = 0; unsigned long flags; int count = cmd_len; fdc_usec_wait(RQM_DELAY); /* wait for valid RQM status */ save_flags(flags); cli(); fdc_status = inb(fdc.msr); if ((fdc_status & FDC_DATA_READY_MASK) == FDC_DATA_IN_READY) { int retry = 0; fdc_mode = *cmd_data; /* used by isr */ interrupt_seen = 0; while (count) { result = fdc_write(*cmd_data); if (result < 0) { TRACEx3(6, "fdc_mode = %02x, status = %02x at index %d", (int) fdc_mode, (int) fdc_status, cmd_len - count); if (++retry <= 3) { TRACE(2, "fdc_write timeout, retry"); } else { TRACE(1, "fdc_write timeout, fatal"); fdc_confused = 1; /* recover ??? */ break; } } else { --count; ++cmd_data; } } } else { TRACE(1, "fdc not ready"); result = -EBUSY; } restore_flags(flags); TRACE_EXIT; return result;}/* Input a res_len long result string from the FDC. * The FDC should be ready to send the result or an error * (EBUSY) will occur. */int fdc_result(byte * res_data, int res_len){ TRACE_FUN(8, "fdc_result"); int result = 0; unsigned long flags; int count = res_len; save_flags(flags); cli(); fdc_status = inb(fdc.msr); if ((fdc_status & FDC_DATA_READY_MASK) == FDC_DATA_OUT_READY) { int retry = 0; while (count) { if (!(fdc_status & FDC_BUSY)) { TRACE(1, "premature end of result phase"); } result = fdc_read(res_data); if (result < 0) { TRACEx3(6, "fdc_mode = %02x, status = %02x at index %d", (int) fdc_mode, (int) fdc_status, res_len - count); if (++retry <= 3) { TRACE(2, "fdc_read timeout, retry"); } else { TRACE(1, "fdc_read timeout, fatal"); fdc_confused = 1; /* recover ??? */ break; } } else { --count; ++res_data; } } } else { TRACE(1, "fdc not ready"); result = -EBUSY; } restore_flags(flags); fdc_usec_wait(RQM_DELAY); /* allow FDC to negate BSY */ TRACE_EXIT; return result;}/* Handle command and result phases for * commands without data phase. */int fdc_issue_command(byte * out_data, int out_count, byte * in_data, int in_count){ TRACE_FUN(8, "fdc_issue_command"); int result; int t0, t1; if (out_count > 0) { result = fdc_command(out_data, out_count); if (result < 0) { TRACE(1, "fdc_command failed"); TRACE_EXIT; return result; } } /* will take 24 - 30 usec for fdc_sense_drive_status and * fdc_sense_interrupt_status commands. * 35 fails sometimes (5/9/93 SJL) * On a loaded system it incidentally takes longer than * this for the fdc to get ready ! ?????? WHY ?????? * So until we know what's going on use a very long timeout. */ t0 = timestamp(); result = fdc_ready_out_wait(500 /* usec */ ); t1 = timestamp(); if (result < 0) { TRACEi(1, "fdc_ready_out_wait failed after:", timediff(t0, t1)); TRACE_EXIT; return result; } if (in_count > 0) { result = fdc_result(in_data, in_count); if (result < 0) { TRACE(1, "result phase aborted"); TRACE_EXIT; return result; } } TRACE_EXIT; return 0;}/* Wait for FDC interrupt with timeout. * Signals are blocked so the wait will not be aborted. * Note: interrupts must be enabled ! (23/05/93 SJL) */int fdc_interrupt_wait(int time){ TRACE_FUN(8, "fdc_interrupt_wait"); struct wait_queue wait = {current, NULL}; int result = -ETIME; int need_cleanup = 0; int current_blocked = current->blocked; static int resetting = 0; if (waitqueue_active(&wait_intr)) { TRACE(1, "error: nested call"); return -EIO; /* return error... */ } if (interrupt_seen == 0) { /* timeout time will be between 0 and MSPT milliseconds too long ! */ current->timeout = jiffies + 1 + (time + MSPT - 1) / MSPT; current->state = TASK_INTERRUPTIBLE; current->blocked = _BLOCK_ALL; add_wait_queue(&wait_intr, &wait); do { schedule(); /* sets TASK_RUNNING on timeout */ } while (!interrupt_seen && current->state != TASK_RUNNING); current->blocked = current_blocked; /* restore */ remove_wait_queue(&wait_intr, &wait); if (interrupt_seen) { current->timeout = 0; /* interrupt hasn't cleared this */ result = 0; } else {#if 1/*** remove me when sure this doesn't happen ***/ if (current->timeout > 0) { TRACE(-1, "*** BUG: unexpected schedule exit ***"); if (current->signal & ~current->blocked) { TRACE(4, "caused by signal ?"); } }#endif if (current->signal & ~current->blocked) { result = -EINTR; } else { result = -ETIME; } need_cleanup = 1; /* missing interrupt, reset fdc. */ } } else { result = 0; } /* In first instance, next statement seems unnecessary since * it will be cleared in fdc_command. However, a small part of * the software seems to rely on this being cleared here * (ftape_close might fail) so stick to it until things get fixed ! */ interrupt_seen = 0; /* clear for next call */ if (need_cleanup & !resetting) { resetting = 1; /* break infinite recursion if reset fails */ TRACE(8, "cleanup reset"); fdc_reset(); resetting = 0; } TRACE_EXIT; return result;}/* Start/stop drive motor. Enable DMA mode. */void fdc_motor(int motor){ TRACE_FUN(8, "fdc_motor"); int unit = FTAPE_UNIT; int data = unit | FDC_RESET_NOT | FDC_DMA_MODE; ftape_motor = motor; if (ftape_motor) { data |= FDC_MOTOR_0 << unit; TRACEx1(4, "turning motor %d on", unit); } else { TRACEx1(4, "turning motor %d off", unit); }#ifdef MACH2 outb_p(data, fdc.dor2);#else outb_p(data, fdc.dor);#endif ftape_sleep(10 * MILLISECOND); TRACE_EXIT;}static void fdc_update_dsr(void){ TRACE_FUN(8, "fdc_update_dsr"); TRACEx2(5, "rate = %d, precomp = %d", fdc_data_rate, fdc_precomp); if (fdc.type >= i82077) { outb_p((fdc_data_rate & 0x03) | fdc_precomp, fdc.dsr); } else { outb_p(fdc_data_rate, fdc.ccr); } TRACE_EXIT;}void fdc_set_write_precomp(int precomp){ /* write precompensation can be set in multiples of 41.67 nsec. * round the parameter to the nearest multiple and convert it * into a fdc setting. Note that 0 means default to the fdc, * 7 is used instead of that. */ fdc_precomp = ((precomp + 21) / 42) << 2; if (fdc_precomp == 0) { fdc_precomp = 7 << 2; } fdc_update_dsr();}/* Read back the Drive Specification regs on a 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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -