📄 fdc-isr.c
字号:
} else if (buff->remaining > 0 && ftape_calc_next_cluster(buff) > 0) { /* still sectors left in current segment, continue * with this segment */ if (fdc_setup_read_write(buff, mode) < 0) { /* failed, abort operation */ buff->bytes = buff->ptr - buff->address; buff->status = error; /* finish this buffer: */ (void)ftape_next_buffer(ft_queue_head); ft_runner_status = aborting; fdc_mode = fdc_idle; } } else { /* current segment completed */ unsigned int last_segment = buff->segment_id; int eot = ((last_segment + 1) % ft_segments_per_track) == 0; unsigned int next = buff->next_segment; /* 0 means stop ! */ buff->bytes = buff->ptr - buff->address; buff->status = done; buff = ftape_next_buffer(ft_queue_head); if (eot) { /* finished last segment on current track, * can't continue */ ft_runner_status = logical_eot; fdc_mode = fdc_idle; TRACE_EXIT; } if (next <= 0) { /* don't continue with next segment */ TRACE(ft_t_noise, "no %s allowed, stopping tape", (write) ? "write next" : "read ahead"); pause_tape(0, mode); ft_runner_status = idle; /* not quite true until * next irq */ TRACE_EXIT; } /* continue with next segment */ if (buff->status != waiting) { TRACE(ft_t_noise, "all input buffers %s, pausing tape", (write) ? "empty" : "full"); pause_tape(0, mode); ft_runner_status = idle; /* not quite true until * next irq */ TRACE_EXIT; } if (write && next != buff->segment_id) { TRACE(ft_t_noise, "segments out of order, aborting write"); ft_runner_status = do_abort; fdc_mode = fdc_idle; TRACE_EXIT; } ftape_setup_new_segment(buff, next, 0); if (stop_read_ahead) { buff->next_segment = 0; stop_read_ahead = 0; } if (ftape_calc_next_cluster(buff) == 0 || fdc_setup_read_write(buff, mode) != 0) { TRACE(ft_t_err, "couldn't start %s-ahead", write ? "write" : "read"); ft_runner_status = do_abort; fdc_mode = fdc_idle; } else { /* keep on going */ switch (ft_driver_state) { case reading: buff->status = reading; break; case verifying: buff->status = verifying; break; case writing: buff->status = writing; break; case deleting: buff->status = deleting; break; default: TRACE(ft_t_err, "BUG: ft_driver_state %d should be one out of " "{reading, writing, verifying, deleting}", ft_driver_state); buff->status = write ? writing : reading; break; } } } TRACE_EXIT;}static void retry_sector(buffer_struct *buff, int mode, unsigned int skip){ TRACE_FUN(ft_t_any); TRACE(ft_t_noise, "%s error, will retry", (mode == fdc_writing_data || mode == fdc_deleting) ? "write" : "read"); pause_tape(1, mode); ft_runner_status = aborting; buff->status = error; buff->skip = skip; TRACE_EXIT;}static unsigned int find_resume_point(buffer_struct *buff){ int i = 0; SectorMap mask; SectorMap map; TRACE_FUN(ft_t_any); /* This function is to be called after all variables have been * updated to point past the failing sector. * If there are any soft errors before the failing sector, * find the first soft error and return the sector offset. * Otherwise find the last hard error. * Note: there should always be at least one hard or soft error ! */ if (buff->sector_offset < 1 || buff->sector_offset > 32) { TRACE(ft_t_bug, "BUG: sector_offset = %d", buff->sector_offset); TRACE_EXIT 0; } if (buff->sector_offset >= 32) { /* C-limitation on shift ! */ mask = 0xffffffff; } else { mask = (1 << buff->sector_offset) - 1; } map = buff->soft_error_map & mask; if (map) { while ((map & (1 << i)) == 0) { ++i; } TRACE(ft_t_noise, "at sector %d", FT_SECTOR(i)); } else { map = buff->hard_error_map & mask; i = buff->sector_offset - 1; if (map) { while ((map & (1 << i)) == 0) { --i; } TRACE(ft_t_noise, "after sector %d", FT_SECTOR(i)); ++i; /* first sector after last hard error */ } else { TRACE(ft_t_bug, "BUG: no soft or hard errors"); } } TRACE_EXIT i;}/* check possible dma residue when formatting, update position record in * buffer struct. This is, of course, modelled after determine_progress(), but * we don't need to set up for retries because the format process cannot be * interrupted (except at the end of the tape track). */static int determine_fmt_progress(buffer_struct *buff, error_cause cause){ 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_info, "DMA residue = 0x%04x", dma_residue); fdc_mode = fdc_idle; switch(cause) { case no_error: ft_runner_status = aborting; buff->status = idle; 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 controller ?"); ft_runner_status = do_abort; buff->status = error; break; default: TRACE(ft_t_noise, "Unexpected error at segment %d", buff->segment_id); ft_runner_status = do_abort; buff->status = error; break; } TRACE_EXIT -EIO; /* can only retry entire track in format mode */ } /* Update var's influenced by the DMA operation. */ buff->ptr += FT_SECTORS_PER_SEGMENT * 4; buff->bytes -= FT_SECTORS_PER_SEGMENT * 4; buff->remaining -= FT_SECTORS_PER_SEGMENT; buff->segment_id ++; /* done with segment */ TRACE_EXIT 0;}/* * Continue formatting, switch buffers if there is no data left in * current buffer. This is, of course, modelled after * continue_xfer(), but we don't need to set up for retries because * the format process cannot be interrupted (except at the end of the * tape track). */static void continue_formatting(buffer_struct *buff){ TRACE_FUN(ft_t_any); if (buff->remaining <= 0) { /* no space left in dma buffer */ unsigned int next = buff->next_segment; if (next == 0) { /* end of tape track */ buff->status = done; ft_runner_status = logical_eot; fdc_mode = fdc_idle; TRACE(ft_t_noise, "Done formatting track %d", ft_location.track); TRACE_EXIT; } /* * switch to next buffer! */ buff->status = done; buff = ftape_next_buffer(ft_queue_head); if (buff->status != waiting || next != buff->segment_id) { goto format_setup_error; } } if (fdc_setup_formatting(buff) < 0) { goto format_setup_error; } buff->status = formatting; TRACE(ft_t_fdc_dma, "Formatting segment %d on track %d", buff->segment_id, ft_location.track); TRACE_EXIT; format_setup_error: ft_runner_status = do_abort; fdc_mode = fdc_idle; buff->status = error; TRACE(ft_t_err, "Error setting up for segment %d on track %d", buff->segment_id, ft_location.track); TRACE_EXIT;}/* this handles writing, read id, reading and formatting */static void handle_fdc_busy(buffer_struct *buff){ static int no_data_error_count; int retry = 0; error_cause cause; __u8 in[7]; int skip; fdc_mode_enum fmode = fdc_mode; TRACE_FUN(ft_t_any); if (fdc_result(in, 7) < 0) { /* better get it fast ! */ TRACE(ft_t_err, "Probably fatal error during FDC Result Phase\n" KERN_INFO "drive may hang until (power on) reset :-("); /* what to do next ???? */ TRACE_EXIT; } cause = decode_irq_cause(fdc_mode, in);#ifdef TESTING { int i; for (i = 0; i < (int)ft_nr_buffers; ++i) TRACE(ft_t_any, "buffer[%d] status: %d, segment_id: %d", i, ft_buffer[i]->status, ft_buffer[i]->segment_id); }#endif if (fmode == fdc_reading_data && ft_driver_state == verifying) { fmode = fdc_verifying; } switch (fmode) { case fdc_verifying: if (ft_runner_status == aborting || ft_runner_status == do_abort) { TRACE(ft_t_noise,"aborting %s",fdc_mode_txt(fdc_mode)); break; } if (buff->retry > 0) { TRACE(ft_t_flow, "this is retry nr %d", buff->retry); } switch (cause) { case no_error: no_data_error_count = 0; determine_verify_progress(buff, cause, in[5]); if (in[2] & 0x40) { /* This should not happen when verifying */ TRACE(ft_t_warn, "deleted data in segment %d/%d", buff->segment_id, FT_SECTOR(buff->sector_offset - 1)); buff->remaining = 0; /* abort transfer */ buff->hard_error_map = EMPTY_SEGMENT; skip = 1; } else { skip = 0; } continue_xfer(buff, fdc_mode, skip); break; case no_data_error: no_data_error_count ++; case overrun_error: retry ++; case id_am_error: case id_crc_error: case data_am_error: case data_crc_error: determine_verify_progress(buff, cause, in[5]); if (cause == no_data_error) { if (no_data_error_count >= 2) { TRACE(ft_t_warn, "retrying because of successive " "no data errors"); no_data_error_count = 0; } else { retry --; } } else { no_data_error_count = 0; } if (retry) { skip = find_resume_point(buff); } else { skip = buff->sector_offset; } if (retry && skip < 32) { retry_sector(buff, fdc_mode, skip); } else { continue_xfer(buff, fdc_mode, skip); } update_history(cause); break; default: /* Don't know why this could happen * but find out. */ determine_verify_progress(buff, cause, in[5]); retry_sector(buff, fdc_mode, 0); TRACE(ft_t_err, "Error: unexpected error"); break; } break; case fdc_reading_data:#ifdef TESTING /* I'm sorry, but: NOBODY ever used this trace * messages for ages. I guess that Bas was the last person * that ever really used this (thank you, between the lines) */ if (cause == no_error) { TRACE(ft_t_flow,"reading segment %d",buff->segment_id); } else { TRACE(ft_t_noise, "error reading segment %d", buff->segment_id); TRACE(ft_t_noise, "\n" KERN_INFO "IRQ:C: 0x%02x, H: 0x%02x, R: 0x%02x, N: 0x%02x\n" KERN_INFO "BUF:C: 0x%02x, H: 0x%02x, R: 0x%02x", in[3], in[4], in[5], in[6], buff->cyl, buff->head, buff->sect); }#endif if (ft_runner_status == aborting || ft_runner_status == do_abort) { TRACE(ft_t_noise,"aborting %s",fdc_mode_txt(fdc_mode)); break; } if (buff->bad_sector_map == FAKE_SEGMENT) { /* This condition occurs when reading a `fake' * sector that's not accessible. Doesn't * really matter as we would have ignored it * anyway ! * * Chance is that we're past the next segment * now, so the next operation may fail and * result in a retry. */ buff->remaining = 0; /* skip failing sector */ /* buff->ptr = buff->address; */ /* fake success: */ continue_xfer(buff, fdc_mode, 1); /* trace calls are expensive: place them AFTER * the real stuff has been done. * */ TRACE(ft_t_noise, "skipping empty segment %d (read), size? %d", buff->segment_id, buff->ptr - buff->address); TRACE_EXIT; } if (buff->retry > 0) { TRACE(ft_t_flow, "this is retry nr %d", buff->retry); } switch (cause) { case no_error: determine_progress(buff, cause, in[5]); if (in[2] & 0x40) { /* Handle deleted data in header segments. * Skip segment and force read-ahead. */ TRACE(ft_t_warn, "deleted data in segment %d/%d", buff->segment_id, FT_SECTOR(buff->sector_offset - 1)); buff->deleted = 1; buff->remaining = 0;/*abort transfer */ buff->soft_error_map |= (-1L << buff->sector_offset); if (buff->segment_id == 0) { /* stop on next segment */ stop_read_ahead = 1; } /* force read-ahead: */ buff->next_segment = buff->segment_id + 1; skip = (FT_SECTORS_PER_SEGMENT - buff->sector_offset); } else { skip = 0; } continue_xfer(buff, fdc_mode, skip); break; case no_data_error: /* Tape started too far ahead of or behind the * right sector. This may also happen in the * middle of a segment ! * * Handle no-data as soft error. If next * sector fails too, a retry (with needed * reposition) will follow. */ retry ++; case id_am_error: case id_crc_error: case data_am_error: case data_crc_error: case overrun_error: retry += (buff->soft_error_map != 0 || buff->hard_error_map != 0); determine_progress(buff, cause, in[5]); #if 1 || defined(TESTING) if (cause == overrun_error) retry ++;#endif if (retry) { skip = find_resume_point(buff); } else { skip = buff->sector_offset; } /* Try to resume with next sector on single * errors (let ecc correct it), but retry on * no_data (we'll be past the target when we * get here so we cannot retry) or on * multiple errors (reduce chance on ecc * failure). */ /* cH: 23/02/97: if the last sector in the * segment was a hard error, then there is * no sense in a retry. This occasion seldom * occurs but ... @:巢竊@%&
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -