📄 ftape-rw.c
字号:
TRACE_EXIT 0;}static int logical_forward(void){ ftape_tape_running = 1; return ftape_command(QIC_LOGICAL_FORWARD);}int ftape_stop_tape(int *pstatus){ int retry = 0; int result; TRACE_FUN(ft_t_flow); do { result = ftape_command_wait(QIC_STOP_TAPE, ftape_timeout.stop, pstatus); if (result == 0) { if ((*pstatus & QIC_STATUS_READY) == 0) { result = -EIO; } else { ftape_tape_running = 0; } } } while (result < 0 && ++retry <= 3); if (result < 0) { TRACE(ft_t_err, "failed ! (fatal)"); } TRACE_EXIT result;}int ftape_dumb_stop(void){ int result; int status; TRACE_FUN(ft_t_flow); /* Abort current fdc operation if it's busy (probably read * or write operation pending) with a reset. */ if (fdc_ready_wait(100 /* usec */) < 0) { TRACE(ft_t_noise, "aborting fdc operation"); fdc_reset(); } /* Reading id's after the last segment on a track may fail * but eventually the drive will become ready (logical eot). */ result = ftape_report_drive_status(&status); ft_location.known = 0; do { if (result == 0 && status & QIC_STATUS_READY) { /* Tape is not running any more. */ TRACE(ft_t_noise, "tape already halted"); check_bot_eot(status); ftape_tape_running = 0; } else if (ftape_tape_running) { /* Tape is (was) still moving. */#ifdef TESTING ftape_read_id();#endif result = ftape_stop_tape(&status); } else { /* Tape not yet ready but stopped. */ result = ftape_ready_wait(ftape_timeout.pause,&status); } } while (ftape_tape_running && !(sigtestsetmask(¤t->pending.signal, _NEVER_BLOCK)));#ifndef TESTING ft_location.known = 0;#endif if (ft_runner_status == aborting || ft_runner_status == do_abort) { ft_runner_status = idle; } TRACE_EXIT result;}/* Wait until runner has finished tail buffer. * */int ftape_wait_segment(buffer_state_enum state){ int status; int result = 0; TRACE_FUN(ft_t_flow); while (ft_buffer[ft_tail]->status == state) { TRACE(ft_t_flow, "state: %d", ft_buffer[ft_tail]->status); /* First buffer still being worked on, wait up to timeout. * * Note: we check two times for being killed. 50 * seconds are quite long. Note that * fdc_interrupt_wait() is not killable by any * means. ftape_read_segment() wants us to return * -EINTR in case of a signal. */ FT_SIGNAL_EXIT(_DONT_BLOCK); result = fdc_interrupt_wait(50 * FT_SECOND); FT_SIGNAL_EXIT(_DONT_BLOCK); if (result < 0) { TRACE_ABORT(result, ft_t_err, "fdc_interrupt_wait failed"); } if (fdc_setup_error) { /* recover... FIXME */ TRACE_ABORT(-EIO, ft_t_err, "setup error"); } } if (ft_buffer[ft_tail]->status != error) { TRACE_EXIT 0; } TRACE_CATCH(ftape_report_drive_status(&status),); TRACE(ft_t_noise, "ftape_report_drive_status: 0x%02x", status); if ((status & QIC_STATUS_READY) && (status & QIC_STATUS_ERROR)) { unsigned int error; qic117_cmd_t command; /* Report and clear error state. * In case the drive can't operate at the selected * rate, select the next lower data rate. */ ftape_report_error(&error, &command, 1); if (error == 31 && command == QIC_LOGICAL_FORWARD) { /* drive does not accept this data rate */ if (ft_data_rate > 250) { TRACE(ft_t_info, "Probable data rate conflict"); TRACE(ft_t_info, "Lowering data rate to %d Kbps", ft_data_rate / 2); ftape_half_data_rate(); if (ft_buffer[ft_tail]->retry > 0) { /* give it a chance */ --ft_buffer[ft_tail]->retry; } } else { /* no rate is accepted... */ TRACE(ft_t_err, "We're dead :("); } } else { TRACE(ft_t_err, "Unknown error"); } TRACE_EXIT -EIO; /* g.p. error */ } TRACE_EXIT 0;}/* forward */ static int seek_forward(int segment_id, int fast);static int fast_seek(int count, int reverse){ int result = 0; int status; TRACE_FUN(ft_t_flow); if (count > 0) { /* If positioned at begin or end of tape, fast seeking needs * special treatment. * Starting from logical bot needs a (slow) seek to the first * segment before the high speed seek. Most drives do this * automatically but some older don't, so we treat them * all the same. * Starting from logical eot is even more difficult because * we cannot (slow) reverse seek to the last segment. * TO BE IMPLEMENTED. */ inhibit_correction = 0; if (ft_location.known && ((ft_location.bot && !reverse) || (ft_location.eot && reverse))) { if (!reverse) { /* (slow) skip to first segment on a track */ seek_forward(ft_location.track * ft_segments_per_track, 0); --count; } else { /* When seeking backwards from * end-of-tape the number of erased * gaps found seems to be higher than * expected. Therefor the drive must * skip some more segments than * calculated, but we don't know how * many. Thus we will prevent the * re-calculation of offset and * overshoot when seeking backwards. */ inhibit_correction = 1; count += 3; /* best guess */ } } } else { TRACE(ft_t_flow, "warning: zero or negative count: %d", count); } if (count > 0) { int i; int nibbles = count > 255 ? 3 : 2; if (count > 4095) { TRACE(ft_t_noise, "skipping clipped at 4095 segment"); count = 4095; } /* Issue this tape command first. */ if (!reverse) { TRACE(ft_t_noise, "skipping %d segment(s)", count); result = ftape_command(nibbles == 3 ? QIC_SKIP_EXTENDED_FORWARD : QIC_SKIP_FORWARD); } else { TRACE(ft_t_noise, "backing up %d segment(s)", count); result = ftape_command(nibbles == 3 ? QIC_SKIP_EXTENDED_REVERSE : QIC_SKIP_REVERSE); } if (result < 0) { TRACE(ft_t_noise, "Skip command failed"); } else { --count; /* 0 means one gap etc. */ for (i = 0; i < nibbles; ++i) { if (result >= 0) { result = ftape_parameter(count & 15); count /= 16; } } result = ftape_ready_wait(ftape_timeout.rewind, &status); if (result >= 0) { ftape_tape_running = 0; } } } TRACE_EXIT result;}static int validate(int id){ /* Check to see if position found is off-track as reported * once. Because all tracks in one direction lie next to * each other, if off-track the error will be approximately * 2 * ft_segments_per_track. */ if (ft_location.track == -1) { return 1; /* unforseen situation, don't generate error */ } else { /* Use margin of ft_segments_per_track on both sides * because ftape needs some margin and the error we're * looking for is much larger ! */ int lo = (ft_location.track - 1) * ft_segments_per_track; int hi = (ft_location.track + 2) * ft_segments_per_track; return (id >= lo && id < hi); }}static int seek_forward(int segment_id, int fast){ int failures = 0; int count; static int margin = 1; /* fixed: stop this before target */ static int overshoot = 1; static int min_count = 8; int expected = -1; int target = segment_id - margin; int fast_seeking; int prev_segment = ft_location.segment; TRACE_FUN(ft_t_flow); if (!ft_location.known) { TRACE_ABORT(-EIO, ft_t_err, "fatal: cannot seek from unknown location"); } if (!validate(segment_id)) { ftape_sleep(1 * FT_SECOND); ft_failure = 1; TRACE_ABORT(-EIO, ft_t_err, "fatal: head off track (bad hardware?)"); } TRACE(ft_t_noise, "from %d/%d to %d/0 - %d", ft_location.segment, ft_location.sector,segment_id,margin); count = target - ft_location.segment - overshoot; fast_seeking = (fast && count > (min_count + (ft_location.bot ? 1 : 0))); if (fast_seeking) { TRACE(ft_t_noise, "fast skipping %d segments", count); expected = segment_id - margin; fast_seek(count, 0); } if (!ftape_tape_running) { logical_forward(); } while (ft_location.segment < segment_id) { /* This requires at least one sector in a (bad) segment to * have a valid and readable sector id ! * It looks like this is not guaranteed, so we must try * to find a way to skip an EMPTY_SEGMENT. !!! FIXME !!! */ if (ftape_read_id() < 0 || !ft_location.known || sigtestsetmask(¤t->pending.signal, _DONT_BLOCK)) { ft_location.known = 0; if (!ftape_tape_running || ++failures > FT_SECTORS_PER_SEGMENT) { TRACE_ABORT(-EIO, ft_t_err, "read_id failed completely"); } FT_SIGNAL_EXIT(_DONT_BLOCK); TRACE(ft_t_flow, "read_id failed, retry (%d)", failures); continue; } if (fast_seeking) { TRACE(ft_t_noise, "ended at %d/%d (%d,%d)", ft_location.segment, ft_location.sector, overshoot, inhibit_correction); if (!inhibit_correction && (ft_location.segment < expected || ft_location.segment > expected + margin)) { int error = ft_location.segment - expected; TRACE(ft_t_noise, "adjusting overshoot from %d to %d", overshoot, overshoot + error); overshoot += error; /* All overshoots have the same * direction, so it should never * become negative, but who knows. */ if (overshoot < -5 || overshoot > OVERSHOOT_LIMIT) { if (overshoot < 0) { /* keep sane value */ overshoot = -5; } else { /* keep sane value */ overshoot = OVERSHOOT_LIMIT; } TRACE(ft_t_noise, "clipped overshoot to %d", overshoot); } } fast_seeking = 0; } if (ft_location.known) { if (ft_location.segment > prev_segment + 1) { TRACE(ft_t_noise, "missed segment %d while skipping", prev_segment + 1); } prev_segment = ft_location.segment; } } if (ft_location.segment > segment_id) { TRACE_ABORT(-EIO, ft_t_noise, "failed: skip ended at segment %d/%d", ft_location.segment, ft_location.sector); } TRACE_EXIT 0;}static int skip_reverse(int segment_id, int *pstatus){ int failures = 0; static int overshoot = 1; static int min_rewind = 2; /* 1 + overshoot */ static const int margin = 1; /* stop this before target */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -