📄 fdc-isr.c
字号:
/* * 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: /usr/local/cvsroot/AplioTRIO/linux/drivers/char/ftape/fdc-isr.c,v $ $Author: vadim $ * $Revision: 1.1.1.1 $ $Date: 1999/11/15 13:41:57 $ $State: Exp $ * * This file contains the interrupt service routine and associated * code for the QIC-40/80 tape streamer device driver. */#include <linux/ftape.h>#include <asm/io.h>#include <asm/dma.h>#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 voiddecode_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; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -