📄 fdc-isr.c
字号:
/* * Copyright (C) 1994-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-isr.c,v $ * $Revision: 1.9 $ * $Date: 1997/10/17 23:01:53 $ * * This file contains the interrupt service routine and * associated code for the QIC-40/80/3010/3020 floppy-tape driver * "ftape" for Linux. */#include <asm/io.h>#include <asm/dma.h>#define volatile /* */#include <linux/ftape.h>#include <linux/qic117.h>#include "../lowlevel/ftape-tracing.h"#include "../lowlevel/fdc-isr.h"#include "../lowlevel/fdc-io.h"#include "../lowlevel/ftape-ctl.h"#include "../lowlevel/ftape-rw.h"#include "../lowlevel/ftape-io.h"#include "../lowlevel/ftape-calibr.h"#include "../lowlevel/ftape-bsm.h"/* Global vars. */volatile int ft_expected_stray_interrupts;volatile int ft_interrupt_seen;volatile int ft_seek_completed;volatile int ft_hide_interrupt;/* 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 stop_read_ahead;static void print_error_cause(int cause){ TRACE_FUN(ft_t_any); switch (cause) { case no_data_error: TRACE(ft_t_noise, "no data error"); break; case id_am_error: TRACE(ft_t_noise, "id am error"); break; case id_crc_error: TRACE(ft_t_noise, "id crc error"); break; case data_am_error: TRACE(ft_t_noise, "data am error"); break; case data_crc_error: TRACE(ft_t_noise, "data crc error"); break; case overrun_error: TRACE(ft_t_noise, "overrun error"); break; default:; } TRACE_EXIT;}static char *fdc_mode_txt(fdc_mode_enum mode){ switch (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"; case fdc_formatting: return "fdc_formatting"; case fdc_verifying: return "fdc_verifying"; default: return "unknown"; }}static inline error_cause decode_irq_cause(fdc_mode_enum mode, __u8 st[]){ error_cause cause = no_error; TRACE_FUN(ft_t_any); /* Valid st[], decode cause of interrupt. */ switch (st[0] & ST0_INT_MASK) { case FDC_INT_NORMAL: TRACE(ft_t_fdc_dma,"normal completion: %s",fdc_mode_txt(mode)); break; case FDC_INT_ABNORMAL: TRACE(ft_t_flow, "abnormal completion %s", fdc_mode_txt(mode)); TRACE(ft_t_fdc_dma, "ST0: 0x%02x, ST1: 0x%02x, ST2: 0x%02x", st[0], st[1], st[2]); TRACE(ft_t_fdc_dma, "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: TRACE(ft_t_flow, "invalid completion %s", fdc_mode_txt(mode)); break; case FDC_INT_READYCH: if (st[0] & ST0_SEEK_END) { TRACE(ft_t_flow, "drive poll completed"); } else { TRACE(ft_t_flow, "ready change %s",fdc_mode_txt(mode)); } break; default: break; } TRACE_EXIT cause;}static void update_history(error_cause cause){ switch (cause) { case id_am_error: ft_history.id_am_errors++; break; case id_crc_error: ft_history.id_crc_errors++; break; case data_am_error: ft_history.data_am_errors++; break; case data_crc_error: ft_history.data_crc_errors++; break; case overrun_error: ft_history.overrun_errors++; break; case no_data_error: ft_history.no_data_errors++; break; default:; }}static void skip_bad_sector(buffer_struct * buff){ TRACE_FUN(ft_t_any); /* Mark sector as soft error and skip it */ if (buff->remaining > 0) { ++buff->sector_offset; ++buff->data_offset; --buff->remaining; buff->ptr += FT_SECTOR_SIZE; buff->bad_sector_map >>= 1; } else { /* Hey, what is this????????????? C code: if we shift * more than 31 bits, we get no shift. That's bad!!!!!! */ ++buff->sector_offset; /* hack for error maps */ TRACE(ft_t_warn, "skipping last sector in segment"); } TRACE_EXIT;}static void update_error_maps(buffer_struct * buff, unsigned int error_offset){ int hard = 0; TRACE_FUN(ft_t_any); if (buff->retry < FT_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; } TRACE(ft_t_noise, "sector %d : %s error\n" KERN_INFO "hard map: 0x%08lx\n" KERN_INFO "soft map: 0x%08lx", FT_SECTOR(error_offset), hard ? "hard" : "soft", (long) buff->hard_error_map, (long) buff->soft_error_map); TRACE_EXIT;}static void print_progress(buffer_struct *buff, error_cause cause){ TRACE_FUN(ft_t_any); switch (cause) { case no_error: TRACE(ft_t_flow,"%d Sector(s) transferred", buff->sector_count); break; case no_data_error: TRACE(ft_t_flow, "Sector %d not found", FT_SECTOR(buff->sector_offset)); break; case overrun_error: /* got an overrun error on the first byte, must be a * hardware problem */ TRACE(ft_t_bug, "Unexpected error: failing DMA or FDC controller ?"); break; case data_crc_error: TRACE(ft_t_flow, "Error in sector %d", FT_SECTOR(buff->sector_offset - 1)); break; case id_crc_error: case id_am_error: case data_am_error: TRACE(ft_t_flow, "Error in sector %d", FT_SECTOR(buff->sector_offset)); break; default: TRACE(ft_t_flow, "Unexpected error at sector %d", FT_SECTOR(buff->sector_offset)); break; } 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 *//* the arg `sector' is returned by the fdc and tells us at which sector we * are positioned at (relative to starting sector of segment) */static void determine_verify_progress(buffer_struct *buff, error_cause cause, __u8 sector){ TRACE_FUN(ft_t_any); if (cause == no_error && sector == 1) { buff->sector_offset = FT_SECTORS_PER_SEGMENT; buff->remaining = 0; if (TRACE_LEVEL >= ft_t_flow) { print_progress(buff, cause); } } else { buff->sector_offset = sector - buff->sect; buff->remaining = FT_SECTORS_PER_SEGMENT - buff->sector_offset; TRACE(ft_t_noise, "%ssector offset: 0x%04x", (cause == no_error) ? "unexpected " : "", buff->sector_offset); switch (cause) { case overrun_error: break;#if 0 case no_data_error: buff->retry = FT_SOFT_RETRIES; if (buff->hard_error_map && buff->sector_offset > 1 && (buff->hard_error_map & (1 << (buff->sector_offset-2)))) { buff->retry --; } break;#endif default: buff->retry = FT_SOFT_RETRIES; break; } if (TRACE_LEVEL >= ft_t_flow) { print_progress(buff, cause); } /* Sector_offset points to the problem area Now adjust * sector_offset so it always points one past he failing * sector. I.e. skip the bad sector. */ ++buff->sector_offset; --buff->remaining; update_error_maps(buff, buff->sector_offset - 1); } TRACE_EXIT;}static void determine_progress(buffer_struct *buff, error_cause cause, __u8 sector){ unsigned int dma_residue; TRACE_FUN(ft_t_any); /* 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); if (cause != no_error || dma_residue != 0) { TRACE(ft_t_noise, "%sDMA residue: 0x%04x", (cause == no_error) ? "unexpected " : "", dma_residue); /* adjust to actual value: */ if (dma_residue == 0) { /* this happens sometimes with overrun errors. * I don't know whether we could ignore the * overrun error. Play save. */ buff->sector_count --; } else { buff->sector_count -= ((dma_residue + (FT_SECTOR_SIZE - 1)) / FT_SECTOR_SIZE); } } /* 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 * FT_SECTOR_SIZE); buff->remaining -= buff->sector_count; buff->bad_sector_map >>= buff->sector_count; } if (TRACE_LEVEL >= ft_t_flow) { print_progress(buff, cause); } if (cause != no_error) { if (buff->remaining == 0) { TRACE(ft_t_warn, "foo?\n" KERN_INFO "count : %d\n" KERN_INFO "offset: %d\n" KERN_INFO "soft : %08x\n" KERN_INFO "hard : %08x", buff->sector_count, buff->sector_offset, buff->soft_error_map, buff->hard_error_map); } /* 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 != data_crc_error) { skip_bad_sector(buff); } update_error_maps(buff, buff->sector_offset - 1); } TRACE_EXIT;}static int calc_steps(int cmd){ if (ftape_current_cylinder > cmd) { return ftape_current_cylinder - cmd; } else { return ftape_current_cylinder + cmd; }}static void pause_tape(int retry, int mode){ int result; __u8 out[3] = {FDC_SEEK, ft_drive_sel, 0}; TRACE_FUN(ft_t_any); /* We'll use a raw seek command to get the tape to rewind and * stop for a retry. */ ++ft_history.rewinds; if (qic117_cmds[ftape_current_command].non_intr) { TRACE(ft_t_warn, "motion command may be issued too soon"); } if (retry && (mode == fdc_reading_data || mode == fdc_reading_id || mode == fdc_verifying)) { ftape_current_command = QIC_MICRO_STEP_PAUSE; ftape_might_be_off_track = 1; } else { ftape_current_command = QIC_PAUSE; } out[2] = calc_steps(ftape_current_command); result = fdc_command(out, 3); /* issue QIC_117 command */ ftape_current_cylinder = out[ 2]; if (result < 0) { TRACE(ft_t_noise, "qic-pause failed, status = %d", result); } else { ft_location.known = 0; ft_runner_status = idle; ft_hide_interrupt = 1; ftape_tape_running = 0; } TRACE_EXIT;}static void continue_xfer(buffer_struct *buff, fdc_mode_enum mode, unsigned int skip){ int write = 0; TRACE_FUN(ft_t_any); if (mode == fdc_writing_data || mode == fdc_deleting) { write = 1; } /* This part can be removed if it never happens */ if (skip > 0 && (ft_runner_status != running || (write && (buff->status != writing)) || (!write && (buff->status != reading && buff->status != verifying)))) { TRACE(ft_t_err, "unexpected runner/buffer state %d/%d", ft_runner_status, buff->status); buff->status = error; /* finish this buffer: */ (void)ftape_next_buffer(ft_queue_head); ft_runner_status = aborting; fdc_mode = fdc_idle;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -