📄 ftape-io.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. $Source: /usr/local/cvsroot/AplioTRIO/linux/drivers/char/ftape/ftape-io.c,v $ $Author: vadim $ * $Revision: 1.1.1.1 $ $Date: 1999/11/15 13:41:57 $ $State: Exp $ * * This file contains the general control functions * for the QIC-40/80 floppy-tape driver for Linux. */#include <linux/errno.h>#include <linux/sched.h>#include <linux/mm.h>#include <linux/ftape.h>#include <asm/segment.h>#include <asm/system.h>#include <linux/ioctl.h>#include <linux/mtio.h>#include "tracing.h"#include "fdc-io.h"#include "qic117.h"#include "ftape-io.h"#include "ftape-ctl.h"#include "ftape-rw.h"#include "ftape-write.h"#include "ftape-read.h"#include "ftape-eof.h"#include "kernel-interface.h"#include "calibr.h"/* Global vars. *//* NOTE: sectors start numbering at 1, all others at 0 ! */timeout_table timeout;vendor_struct drive_type;int qic_std;int tape_len;volatile int current_command;const struct qic117_command_table qic117_cmds[] = QIC117_COMMANDS;int might_be_off_track;/* Local vars. */static int command_parameter = 0;/* command-restrictions is a table according * to the QIC-117 specs specifying the state * the drive status should be in at command execution. */static const ftape_error ftape_errors[] = QIC117_ERRORS;static int ftape_udelay_count;static int ftape_udelay_time;static const struct { char *text; int fdc_code; byte drive_code; int precomp;} rates[4] = {#if defined(FDC_82078SL) { "2 M", -1 /* unsupported */ , QIC_CONFIG_RATE_2000, 0 },#else { "2 M", fdc_data_rate_2000, QIC_CONFIG_RATE_2000, 0 },#endif { "1 M", fdc_data_rate_1000, QIC_CONFIG_RATE_1000, 42 }, { "500 K", fdc_data_rate_500, QIC_CONFIG_RATE_500, 125 }, { "250 K", fdc_data_rate_250, QIC_CONFIG_RATE_250, 250 },};typedef enum { prehistoric, pre_qic117c, post_qic117b, post_qic117d} qic_model;void udelay(int usecs){ volatile int count = (1 + (usecs * ftape_udelay_count - 1) / ftape_udelay_time); volatile int i; while (count-- > 0) { for (i = 0; i < 20; ++i); }}int udelay_calibrate(void){ return calibrate("udelay", udelay, &ftape_udelay_count, &ftape_udelay_time);}/* Delay (msec) routine. */void ftape_sleep(unsigned int time){ TRACE_FUN(8, "ftape_sleep"); unsigned long flags; int ticks = 1 + (time + MSPT - 1) / MSPT; /* error in range [0..1] MSPT */ if (time < MSPT) { /* Time too small for scheduler, do a busy wait ! */ udelay(1000 * time); } else { TRACEx2(8, "%d msec, %d ticks", time, ticks); current->timeout = jiffies + ticks; current->state = TASK_INTERRUPTIBLE; save_flags(flags); sti(); do { while (current->state != TASK_RUNNING) { schedule(); } if (current->signal & ~current->blocked) { TRACE(1, "awoken by non-blocked signal :-("); break; /* exit on signal */ } } while (current->timeout > 0); restore_flags(flags); } TRACE_EXIT;}/* forward */ int ftape_report_raw_drive_status(int *status);/* Issue a tape command: * Generate command # of step pulses. */int ftape_command(int command){ TRACE_FUN(8, "ftape_command"); int result = 0; int track; int old_tracing = tracing; static int level = 0; int status = -1; if (++level > 5) { /* This is a bug we'll want to know about. */ TRACEx1(1, "bug - recursion for command: %d", command); result = -EIO; } else if (command_parameter) { /* Don't check restrictions for parameters. */ TRACEx1(5, "called with parameter = %d", command - 2); } else if (command <= 0 || command > NR_ITEMS(qic117_cmds)) { /* This is a bug we'll want to know about too. */ TRACEx1(-1, "bug - bad command: %d", command); result = -EIO; } else { /* disable logging and restriction check for some commands, * check all other commands that have a prescribed starting status. */ if (command == QIC_REPORT_DRIVE_STATUS) { TRACE(8, "report drive status called"); tracing = 0; } else if (command == QIC_REPORT_NEXT_BIT) { tracing = 0; } else { TRACEx1(5, "%s", qic117_cmds[command].name); /* A new motion command during an uninterruptible (motion) * command requires a ready status before the new command * can be issued. Otherwise a new motion command needs to * be checked against required status. */ if (qic117_cmds[command].cmd_type == motion && qic117_cmds[current_command].non_intr) { ftape_report_raw_drive_status(&status); if ((status & QIC_STATUS_READY) == 0) { TRACEx2(4, "motion cmd (%d) during non-intr cmd (%d)", command, current_command); TRACE(4, "waiting until drive gets ready"); ftape_ready_wait(timeout.seek, &status); } } if (qic117_cmds[command].mask != 0) { byte difference; /* Some commands do require a certain status: */ if (status == -1) { /* not yet set */ ftape_report_raw_drive_status(&status); } difference = ((status ^ qic117_cmds[command].state) & qic117_cmds[command].mask); /* Wait until the drive gets ready. This may last forever * if the drive never gets ready... */ while ((difference & QIC_STATUS_READY) != 0) { TRACEx1(4, "command %d issued while not ready", command); TRACE(4, "waiting until drive gets ready"); ftape_ready_wait(timeout.seek, &status); difference = ((status ^ qic117_cmds[command].state) & qic117_cmds[command].mask); /* Bail out on signal ! */ if (current->signal & _DONT_BLOCK) { result = -EINTR; break; } } while (result == 0 && (difference & QIC_STATUS_ERROR) != 0) { int err; int cmd; TRACEx1(4, "command %d issued while error pending", command); TRACE(4, "clearing error status"); ftape_report_error(&err, &cmd, 1); ftape_report_raw_drive_status(&status); difference = ((status ^ qic117_cmds[command].state) & qic117_cmds[command].mask); /* Bail out on fatal signal ! */ if (current->signal & _DONT_BLOCK) { result = -EINTR; break; } } if (result == 0 && difference) { /* Any remaining difference can't be solved here. */ if (difference & (QIC_STATUS_CARTRIDGE_PRESENT | QIC_STATUS_NEW_CARTRIDGE | QIC_STATUS_REFERENCED)) { TRACE(1, "Fatal: tape removed or reinserted !"); ftape_failure = 1; } else { TRACEx2(1, "wrong state: 0x%02x should be: 0x%02x", status & qic117_cmds[command].mask, qic117_cmds[command].state); } result = -EIO; } if (~status & QIC_STATUS_READY & qic117_cmds[command].mask) { TRACE(1, "Bad: still busy!"); result = -EBUSY; } } } } tracing = old_tracing; /* Now all conditions are met or result is < 0. */ if (result >= 0) { /* Always wait for a command_timeout period to separate * individuals commands and/or parameters. */ ftape_sleep(3 * MILLISECOND); /* Keep cylinder nr within range, step towards home if possible. */ if (current_cylinder >= command) { track = current_cylinder - command; } else { track = current_cylinder + command; } result = fdc_seek(track); /* position is no longer valid after any of these commands * have completed. */ if (qic117_cmds[command].cmd_type == motion && command != QIC_LOGICAL_FORWARD && command != QIC_STOP_TAPE) { location.known = 0; } command_parameter = 0; /* always turned off for next command */ current_command = command; } --level; TRACE_EXIT; return result;}/* Send a tape command parameter: * Generates command # of step pulses. * Skips tape-status call ! */int ftape_parameter(int command){ command_parameter = 1; return ftape_command(command + 2);}/* Wait for the drive to get ready. * timeout time in milli-seconds * Returned status is valid if result != -EIO */int ftape_ready_wait(int timeout, int *status){ TRACE_FUN(8, "ftape_ready_wait"); int result; unsigned long t0; const int poll_delay = 100 * MILLISECOND; for (;;) { t0 = jiffies; result = ftape_report_raw_drive_status(status); if (result < 0) { TRACE(1, "ftape_report_raw_drive_status failed"); result = -EIO; break; } if (*status & QIC_STATUS_READY) { result = 0; break; } if (timeout >= 0) { /* this will fail when jiffies wraps around about * once every year :-) */ timeout -= ((jiffies - t0) * SECOND) / HZ; if (timeout <= 0) { TRACE(1, "timeout"); result = -ETIME; break; } ftape_sleep(poll_delay); timeout -= poll_delay; } else { ftape_sleep(poll_delay); } if (current->signal & _NEVER_BLOCK) { TRACE(1, "interrupted by fatal signal"); result = -EINTR; break; /* exit on signal */ } } TRACE_EXIT; return result;}/* Issue command and wait up to timeout seconds for drive ready */int ftape_command_wait(int command, int timeout, int *status){ TRACE_FUN(8, "ftape_command_wait"); int result; /* Drive should be ready, issue command */ result = ftape_command(command); if (result >= 0) { result = ftape_ready_wait(timeout, status); } TRACE_EXIT; return result;}int ftape_parameter_wait(int command, int timeout, int *status){ TRACE_FUN(8, "ftape_parameter_wait"); int result; /* Drive should be ready, issue command */ result = ftape_parameter(command); if (result >= 0) { result = ftape_ready_wait(timeout, status); } TRACE_EXIT; return result;}/*-------------------------------------------------------------------------- * Report operations *//* Query the drive about its status. The command is sent and result_length bits of status are returned (2 extra bits are read for start and stop). */static int ftape_report_operation(int *status, int command, int result_length){ TRACE_FUN(8, "ftape_report_operation"); int i, st3; int result; unsigned int t0, t1, dt; result = ftape_command(command); if (result < 0) { TRACE(1, "ftape_command failed"); TRACE_EXIT; return result; } t0 = timestamp(); dt = 0; i = 0; do { ++i; ftape_sleep(3 * MILLISECOND); /* see remark below */ result = fdc_sense_drive_status(&st3); if (result < 0) { TRACE(1, "fdc_sense_drive_status failed"); TRACE_EXIT; return result; } /* Calculate time difference every iteration because timer may * wrap around (but only one !) and timediff will account for this. * Note that the sleep above must be < 1/HZ or we'll lose ticks ! */ t1 = timestamp(); dt += timediff(t0, t1); t0 = t1; /* Ack should be asserted within Ttimout + Tack = 6 msec. * Looks like some drives fail to do this so extend this * period to 300 msec. */ } while (!(st3 & ST3_TRACK_0) && dt < 300000); if (st3 & ST3_TRACK_0) { /* dt may be larger than expected because of other tasks * scheduled while we were sleeping. */ if (i > 1 && dt > 6000) { TRACEx2(1, "Acknowledge after %u msec. (%i iter)", dt / 1000, i); } } else { TRACEx2(1, "No acknowledge after %u msec. (%i iter)", dt / 1000, i); TRACE(1, "timeout on Acknowledge"); TRACE_EXIT; return -EIO; } *status = 0; for (i = 0; i < result_length + 1; i++) { result = ftape_command(QIC_REPORT_NEXT_BIT); if (result < 0) { TRACE(1, "report next bit failed"); TRACE_EXIT; return result; }#if 1 /* fdc_seek does interrupt wait, so why should we ? * (it will only fail causing fdc to be reset...) * It's only purpose may be the delay, we'll have to find out! */#else fdc_interrupt_wait(25 * MILLISECOND); /* fails only if hw fails */#endif result = fdc_sense_drive_status(&st3); if (result < 0) { TRACE(1, "fdc_sense_drive_status (2) failed"); TRACE_EXIT; return result; } if (i < result_length) { *status |= ((st3 & ST3_TRACK_0) ? 1 : 0) << i; } else { if ((st3 & ST3_TRACK_0) == 0) { TRACE(1, "missing status stop bit"); TRACE_EXIT; return -EIO; } } } /* this command will put track zero and index back into normal state */ result = ftape_command(QIC_REPORT_NEXT_BIT); TRACE_EXIT; return 0;}/* Report the current drive status. */int ftape_report_raw_drive_status(int *status){ TRACE_FUN(8, "ftape_report_raw_drive_status"); int result; int count = 0; do { result = ftape_report_operation(status, QIC_REPORT_DRIVE_STATUS, 8); } while (result < 0 && ++count <= 3); if (result < 0) { TRACE(1, "report_operation failed"); result = -EIO; } else if (*status & QIC_STATUS_READY) { current_command = 0; /* completed */ } TRACE_EXIT; return result;}int ftape_report_drive_status(int *status){ TRACE_FUN(8, "ftape_report_drive_status"); int result; result = ftape_report_raw_drive_status(status); if (result < 0) { TRACE(1, "ftape_report_raw_drive_status failed"); TRACE_EXIT; return result; } if (*status & QIC_STATUS_NEW_CARTRIDGE || !(*status & QIC_STATUS_CARTRIDGE_PRESENT)) { ftape_failure = 1; /* will inhibit further operations */ TRACE_EXIT; return -EIO; } if (*status & QIC_STATUS_READY && *status & QIC_STATUS_ERROR) { /* Let caller handle all errors */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -