root/drivers/char/ftape/ftape-rw.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. next_buffer
  2. valid_segment_no
  3. count_ones
  4. setup_segment
  5. setup_new_segment
  6. calc_next_cluster
  7. check_bot_eot
  8. ftape_read_id
  9. logical_forward
  10. stop_tape
  11. ftape_dumb_stop
  12. wait_segment
  13. fast_seek
  14. validate
  15. seek_forward
  16. skip_reverse
  17. determine_position
  18. ftape_start_tape

   1 /*
   2  *      Copyright (C) 1993-1995 Bas Laarhoven.
   3 
   4  This program is free software; you can redistribute it and/or modify
   5  it under the terms of the GNU General Public License as published by
   6  the Free Software Foundation; either version 2, or (at your option)
   7  any later version.
   8 
   9  This program is distributed in the hope that it will be useful,
  10  but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  GNU General Public License for more details.
  13 
  14  You should have received a copy of the GNU General Public License
  15  along with this program; see the file COPYING.  If not, write to
  16  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  17 
  18  $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-rw.c,v $
  19  $Author: bas $
  20  *
  21  $Revision: 1.54 $
  22  $Date: 1995/05/27 08:55:27 $
  23  $State: Beta $
  24  *
  25  *      This file contains some common code for the segment read and segment
  26  *      write routines for the QIC-117 floppy-tape driver for Linux.
  27  */
  28 
  29 #include <linux/string.h>
  30 #include <linux/errno.h>
  31 #include <linux/ftape.h>
  32 
  33 #include "tracing.h"
  34 #include "ftape-rw.h"
  35 #include "fdc-io.h"
  36 #include "kernel-interface.h"
  37 #include "qic117.h"
  38 #include "ftape-io.h"
  39 #include "ftape-ctl.h"
  40 #include "ftape-read.h"
  41 #include "ftape-eof.h"
  42 #include "ecc.h"
  43 #include "ftape-bsm.h"
  44 
  45 /*      Global vars.
  46  */
  47 volatile enum runner_status_enum runner_status = idle;
  48 byte deblock_buffer[(SECTORS_PER_SEGMENT - 3) * SECTOR_SIZE];
  49 byte scratch_buffer[(SECTORS_PER_SEGMENT - 3) * SECTOR_SIZE];
  50 buffer_struct buffer[NR_BUFFERS];
  51 struct wait_queue *wait_intr = NULL;
  52 volatile int head;
  53 volatile int tail;              /* not volatile but need same type as head */
  54 int fdc_setup_error;
  55 ftape_last_segment_struct ftape_last_segment;
  56 int header_segment_1 = -1;
  57 int header_segment_2 = -1;
  58 int used_header_segment = -1;
  59 location_record location =
  60 {-1, 0};
  61 volatile int tape_running = 0;
  62 format_type format_code;
  63 
  64 /*      Local vars.
  65  */
  66 static int overrun_count_offset = 0;
  67 static int inhibit_correction = 0;
  68 
  69 
  70 /*      Increment cyclic buffer nr.
  71  */
  72 buffer_struct *
  73  next_buffer(volatile int *x)
     /* [previous][next][first][last][top][bottom][index][help] */
  74 {
  75         if (++*x >= NR_BUFFERS) {
  76                 *x = 0;
  77         }
  78         return &buffer[*x];
  79 }
  80 
  81 int valid_segment_no(unsigned segment)
     /* [previous][next][first][last][top][bottom][index][help] */
  82 {
  83         return (segment >= first_data_segment && segment <= ftape_last_segment.id);
  84 }
  85 
  86 /*      Count nr of 1's in pattern.
  87  */
  88 int count_ones(unsigned long mask)
     /* [previous][next][first][last][top][bottom][index][help] */
  89 {
  90         int bits;
  91 
  92         for (bits = 0; mask != 0; mask >>= 1) {
  93                 if (mask & 1) {
  94                         ++bits;
  95                 }
  96         }
  97         return bits;
  98 }
  99 
 100 /*      Calculate Floppy Disk Controller and DMA parameters for a segment.
 101  *      head:   selects buffer struct in array.
 102  *      offset: number of physical sectors to skip (including bad ones).
 103  *      count:  number of physical sectors to handle (including bad ones).
 104  */
 105 static int setup_segment(buffer_struct * buff, unsigned int segment_id,
     /* [previous][next][first][last][top][bottom][index][help] */
 106         unsigned int sector_offset, unsigned int sector_count, int retry)
 107 {
 108         TRACE_FUN(8, "setup_segment");
 109         unsigned long offset_mask;
 110         unsigned long mask;
 111 
 112         buff->segment_id = segment_id;
 113         buff->sector_offset = sector_offset;
 114         buff->remaining = sector_count;
 115         buff->head = segment_id / segments_per_head;
 116         buff->cyl = (segment_id % segments_per_head) / segments_per_cylinder;
 117         buff->sect = (segment_id % segments_per_cylinder) * SECTORS_PER_SEGMENT + 1;
 118         buff->deleted = 0;
 119         offset_mask = (1 << buff->sector_offset) - 1;
 120         mask = get_bad_sector_entry(segment_id) & offset_mask;
 121         while (mask) {
 122                 if (mask & 1) {
 123                         offset_mask >>= 1;      /* don't count bad sector */
 124                 }
 125                 mask >>= 1;
 126         }
 127         buff->data_offset = count_ones(offset_mask);    /* good sectors to skip */
 128         buff->ptr = buff->address + buff->data_offset * SECTOR_SIZE;
 129         TRACEx1(5, "data offset = %d sectors", buff->data_offset);
 130         if (retry) {
 131                 buff->soft_error_map &= offset_mask;    /* keep skipped part */
 132         } else {
 133                 buff->hard_error_map = buff->soft_error_map = 0;
 134         }
 135         buff->bad_sector_map = get_bad_sector_entry(buff->segment_id);
 136         if (buff->bad_sector_map != 0) {
 137                 TRACEx2(4, "segment: %d, bad sector map: %08lx",
 138                         buff->segment_id, buff->bad_sector_map);
 139         } else {
 140                 TRACEx1(5, "segment: %d", buff->segment_id);
 141         }
 142         if (buff->sector_offset > 0) {
 143                 buff->bad_sector_map >>= buff->sector_offset;
 144         }
 145         if (buff->sector_offset != 0 || buff->remaining != SECTORS_PER_SEGMENT) {
 146                 TRACEx2(5, "sector offset = %d, count = %d",
 147                         buff->sector_offset, buff->remaining);
 148         }
 149         /*
 150          *    Segments with 3 or less sectors are not written with
 151          *    valid data because there is no space left for the ecc.
 152          *    The data written is whatever happens to be in the buffer.
 153          *    Reading such a segment will return a zero byte-count.
 154          *    To allow us to read/write segments with all bad sectors
 155          *    we fake one readable sector in the segment. This prevents
 156          *    having to handle these segments in a very special way.
 157          *    It is not important if the reading of this bad sector
 158          *    fails or not (the data is ignored). It is only read to
 159          *    keep the driver running.
 160          *    The QIC-40/80 spec. has no information on how to handle
 161          *    this case, so this is my interpretation.
 162          */
 163         if (buff->bad_sector_map == EMPTY_SEGMENT) {
 164                 TRACE(5, "empty segment, fake first sector good");
 165                 buff->bad_sector_map = FAKE_SEGMENT;
 166         }
 167         fdc_setup_error = 0;
 168         buff->next_segment = segment_id + 1;
 169         TRACE_EXIT;
 170         return 0;
 171 }
 172 
 173 /*      Calculate Floppy Disk Controller and DMA parameters for a new segment.
 174  */
 175 int setup_new_segment(buffer_struct * buff, unsigned int segment_id, int skip)
     /* [previous][next][first][last][top][bottom][index][help] */
 176 {
 177         TRACE_FUN(5, "setup_new_segment");
 178         int result = 0;
 179         static int old_segment_id = -1;
 180         static int old_ftape_state = idle;
 181         int retry = 0;
 182         unsigned offset = 0;
 183         int count = SECTORS_PER_SEGMENT;
 184 
 185         TRACEx3(5, "%s segment %d (old = %d)",
 186                 (ftape_state == reading) ? "reading" : "writing",
 187                 segment_id, old_segment_id);
 188         if (ftape_state != old_ftape_state) {   /* when verifying */
 189                 old_segment_id = -1;
 190                 old_ftape_state = ftape_state;
 191         }
 192         if (segment_id == old_segment_id) {
 193                 ++buff->retry;
 194                 ++history.retries;
 195                 TRACEx1(5, "setting up for retry nr %d", buff->retry);
 196                 retry = 1;
 197                 if (skip && buff->skip > 0) {   /* allow skip on retry */
 198                         offset = buff->skip;
 199                         count -= offset;
 200                         TRACEx1(5, "skipping %d sectors", offset);
 201                 }
 202         } else {
 203                 buff->retry = 0;
 204                 buff->skip = 0;
 205                 old_segment_id = segment_id;
 206         }
 207         result = setup_segment(buff, segment_id, offset, count, retry);
 208         TRACE_EXIT;
 209         return result;
 210 }
 211 
 212 /*      Determine size of next cluster of good sectors.
 213  */
 214 int calc_next_cluster(buffer_struct * buff)
     /* [previous][next][first][last][top][bottom][index][help] */
 215 {
 216         /* Skip bad sectors.
 217          */
 218         while (buff->remaining > 0 && (buff->bad_sector_map & 1) != 0) {
 219                 buff->bad_sector_map >>= 1;
 220                 ++buff->sector_offset;
 221                 --buff->remaining;
 222         }
 223         /* Find next cluster of good sectors
 224          */
 225         if (buff->bad_sector_map == 0) {        /* speed up */
 226                 buff->sector_count = buff->remaining;
 227         } else {
 228                 unsigned long map = buff->bad_sector_map;
 229 
 230                 buff->sector_count = 0;
 231                 while (buff->sector_count < buff->remaining && (map & 1) == 0) {
 232                         ++buff->sector_count;
 233                         map >>= 1;
 234                 }
 235         }
 236         return buff->sector_count;
 237 }
 238 
 239 int check_bot_eot(int status)
     /* [previous][next][first][last][top][bottom][index][help] */
 240 {
 241         TRACE_FUN(5, "check_bot_eot");
 242 
 243         if (status & (QIC_STATUS_AT_BOT | QIC_STATUS_AT_EOT)) {
 244                 location.bot = ((location.track & 1) == 0 ?
 245                                 (status & QIC_STATUS_AT_BOT) :
 246                                 (status & QIC_STATUS_AT_EOT));
 247                 location.eot = !location.bot;
 248                 location.segment = (location.track +
 249                         (location.bot ? 0 : 1)) * segments_per_track - 1;
 250                 location.sector = -1;
 251                 location.known = 1;
 252                 TRACEx1(5, "tape at logical %s", location.bot ? "bot" : "eot");
 253                 TRACEx1(5, "segment = %d", location.segment);
 254         } else {
 255                 location.known = 0;
 256         }
 257         TRACE_EXIT;
 258         return location.known;
 259 }
 260 
 261 /*      Read Id of first sector passing tape head.
 262  */
 263 int ftape_read_id(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 264 {
 265         TRACE_FUN(8, "ftape_read_id");
 266         int result;
 267         int status;
 268         byte out[2];
 269 
 270         /* Assume tape is running on entry, be able to handle
 271          * situation where it stopped or is stopping.
 272          */
 273         location.known = 0;     /* default is location not known */
 274         out[0] = FDC_READID;
 275         out[1] = FTAPE_UNIT;
 276         result = fdc_command(out, 2);
 277         if (result < 0) {
 278                 TRACE(1, "fdc_command failed");
 279         } else {
 280                 result = fdc_interrupt_wait(20 * SECOND);
 281                 if (result == 0) {
 282                         if (fdc_sect == 0) {
 283                                 result = ftape_report_drive_status(&status);
 284                                 if (result == 0) {
 285                                         if (status & QIC_STATUS_READY) {
 286                                                 tape_running = 0;
 287                                                 TRACE(5, "tape has stopped");
 288                                                 check_bot_eot(status);
 289                                                 if (!location.known) {
 290                                                         result = -EIO;
 291                                                 }
 292                                         } else {
 293                                                 /*  If read-id failed because of a hard or soft
 294                                                  *  error, return an error. Higher level must retry!
 295                                                  */
 296                                                 result = -EIO;
 297                                         }
 298                                 }
 299                         } else {
 300                                 location.known = 1;
 301                                 location.segment = (segments_per_head * fdc_head
 302                                         + segments_per_cylinder * fdc_cyl
 303                                  + (fdc_sect - 1) / SECTORS_PER_SEGMENT);
 304                                 location.sector = (fdc_sect - 1) % SECTORS_PER_SEGMENT;
 305                                 location.eot =
 306                                     location.bot = 0;
 307                         }
 308                 } else if (result == -ETIME) {
 309                         /*  Didn't find id on tape, must be near end: Wait until stopped.
 310                          */
 311                         result = ftape_ready_wait(FOREVER, &status);
 312                         if (result >= 0) {
 313                                 tape_running = 0;
 314                                 TRACE(5, "tape has stopped");
 315                                 check_bot_eot(status);
 316                                 if (!location.known) {
 317                                         result = -EIO;
 318                                 }
 319                         }
 320                 } else {
 321                         /* Interrupted or otherwise failing fdc_interrupt_wait()
 322                          */
 323                         TRACE(1, "fdc_interrupt_wait failed :(");
 324                         result = -EIO;
 325                 }
 326         }
 327         if (!location.known) {
 328                 TRACE(5, "no id found");
 329         } else {
 330                 if (location.sector == 0) {
 331                         TRACEx2(5, "passing segment %d/%d", location.segment, location.sector);
 332                 } else {
 333                         TRACEx2(6, "passing segment %d/%d", location.segment, location.sector);
 334                 }
 335         }
 336         TRACE_EXIT;
 337         return result;
 338 }
 339 
 340 static int logical_forward(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 341 {
 342         tape_running = 1;
 343         return ftape_command(QIC_LOGICAL_FORWARD);
 344 }
 345 
 346 static int stop_tape(int *pstatus)
     /* [previous][next][first][last][top][bottom][index][help] */
 347 {
 348         TRACE_FUN(5, "stop_tape");
 349         int retry = 0;
 350         int result;
 351 
 352         do {
 353                 result = ftape_command_wait(QIC_STOP_TAPE, timeout.stop, pstatus);
 354                 if (result == 0) {
 355                         if ((*pstatus & QIC_STATUS_READY) == 0) {
 356                                 result = -EIO;
 357                         } else {
 358                                 tape_running = 0;
 359                         }
 360                 }
 361         } while (result < 0 && ++retry <= 3);
 362         if (result < 0) {
 363                 TRACE(1, "failed ! (fatal)");
 364         }
 365         TRACE_EXIT;
 366         return result;
 367 }
 368 
 369 int ftape_dumb_stop(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 370 {
 371         TRACE_FUN(5, "ftape_dumb_stop");
 372         int result;
 373         int status;
 374 
 375         /* Abort current fdc operation if it's busy (probably read
 376          * or write operation pending) with a reset.
 377          */
 378         result = fdc_ready_wait(100 /* usec */ );
 379         if (result < 0) {
 380                 TRACE(1, "aborting fdc operation");
 381                 fdc_reset();
 382         }
 383         /*  Reading id's after the last segment on a track may fail
 384          *  but eventually the drive will become ready (logical eot).
 385          */
 386         result = ftape_report_drive_status(&status);
 387         location.known = 0;
 388         do {
 389                 if (result == 0 && status & QIC_STATUS_READY) {
 390                         /* Tape is not running any more.
 391                          */
 392                         TRACE(5, "tape already halted");
 393                         check_bot_eot(status);
 394                         tape_running = 0;
 395                 } else if (tape_running) {
 396                         /* Tape is (was) still moving.
 397                          */
 398 #ifdef TESTING
 399                         ftape_read_id();
 400 #endif
 401                         result = stop_tape(&status);
 402                 } else {
 403                         /* Tape not yet ready but stopped.
 404                          */
 405                         result = ftape_ready_wait(timeout.pause, &status);
 406                 }
 407         } while (tape_running);
 408 #ifndef TESTING
 409         location.known = 0;
 410 #endif
 411         TRACE_EXIT;
 412         return result;
 413 }
 414 
 415 /*      Wait until runner has finished tail buffer.
 416  */
 417 int wait_segment(buffer_state_enum state)
     /* [previous][next][first][last][top][bottom][index][help] */
 418 {
 419         TRACE_FUN(5, "wait_segment");
 420         int result = 0;
 421 
 422         while (buffer[tail].status == state) {
 423                 /*  First buffer still being worked on, wait up to timeout.
 424                  */
 425                 result = fdc_interrupt_wait(50 * SECOND);
 426                 if (result < 0) {
 427                         if (result != -EINTR) {
 428                                 TRACE(1, "fdc_interrupt_wait failed");
 429                                 result = -ETIME;
 430                         }
 431                         break;
 432                 }
 433                 if (fdc_setup_error) {
 434                         TRACE(1, "setup error");
 435                         /* recover... */
 436                         result = -EIO;
 437                         break;
 438                 }
 439         }
 440         TRACE_EXIT;
 441         return result;
 442 }
 443 
 444 /* forward */ static int seek_forward(int segment_id);
 445 
 446 int fast_seek(int count, int reverse)
     /* [previous][next][first][last][top][bottom][index][help] */
 447 {
 448         TRACE_FUN(5, "fast_seek");
 449         int result = 0;
 450         int status;
 451 
 452         if (count > 0) {
 453                 /*  If positioned at begin or end of tape, fast seeking needs
 454                  *  special treatment.
 455                  *  Starting from logical bot needs a (slow) seek to the first
 456                  *  segment before the high speed seek. Most drives do this
 457                  *  automatically but some older don't, so we treat them
 458                  *  all the same.
 459                  *  Starting from logical eot is even more difficult because
 460                  *  we cannot (slow) reverse seek to the last segment.
 461                  *  TO BE IMPLEMENTED.
 462                  */
 463                 inhibit_correction = 0;
 464                 if (location.known &&
 465                     ((location.bot && !reverse) ||
 466                      (location.eot && reverse))) {
 467                         if (!reverse) {
 468                                 /*  (slow) skip to first segment on a track
 469                                  */
 470                                 seek_forward(location.track * segments_per_track);
 471                                 --count;
 472                         } else {
 473                                 /*  When seeking backwards from end-of-tape the number
 474                                  *  of erased gaps found seems to be higher than expected.
 475                                  *  Therefor the drive must skip some more segments than
 476                                  *  calculated, but we don't know how many.
 477                                  *  Thus we will prevent the re-calculation of offset
 478                                  *  and overshoot when seeking backwards.
 479                                  */
 480                                 inhibit_correction = 1;
 481                                 count += 3;     /* best guess */
 482                         }
 483                 }
 484         } else {
 485                 TRACEx1(5, "warning: zero or negative count: %d", count);
 486         }
 487         if (count > 0) {
 488                 int i;
 489                 int nibbles = count > 255 ? 3 : 2;
 490 
 491                 if (count > 4095) {
 492                         TRACE(4, "skipping clipped at 4095 segment");
 493                         count = 4095;
 494                 }
 495                 /* Issue this tape command first. */
 496                 if (!reverse) {
 497                         TRACEx1(4, "skipping %d segment(s)", count);
 498                         result = ftape_command(nibbles == 3 ?
 499                            QIC_SKIP_EXTENDED_FORWARD : QIC_SKIP_FORWARD);
 500                 } else {
 501                         TRACEx1(4, "backing up %d segment(s)", count);
 502                         result = ftape_command(nibbles == 3 ?
 503                            QIC_SKIP_EXTENDED_REVERSE : QIC_SKIP_REVERSE);
 504                 }
 505                 if (result < 0) {
 506                         TRACE(4, "Skip command failed");
 507                 } else {
 508                         --count;        /* 0 means one gap etc. */
 509                         for (i = 0; i < nibbles; ++i) {
 510                                 if (result >= 0) {
 511                                         result = ftape_parameter(count & 15);
 512                                         count /= 16;
 513                                 }
 514                         }
 515                         result = ftape_ready_wait(timeout.rewind, &status);
 516                         if (result >= 0) {
 517                                 tape_running = 0;
 518                         }
 519                 }
 520         }
 521         TRACE_EXIT;
 522         return result;
 523 }
 524 
 525 static int validate(int id)
     /* [previous][next][first][last][top][bottom][index][help] */
 526 {
 527         /*  Check to see if position found is off-track as reported once.
 528          *  Because all tracks in one direction lie next to each other,
 529          *  if off-track the error will be approximately 2 * segments_per_track.
 530          */
 531         if (location.track == -1) {
 532                 return 1;       /* unforeseen situation, don't generate error */
 533         } else {
 534                 /*  Use margin of segments_per_track on both sides because ftape
 535                  *  needs some margin and the error we're looking for is much larger !
 536                  */
 537                 int lo = (location.track - 1) * segments_per_track;
 538                 int hi = (location.track + 2) * segments_per_track;
 539 
 540                 return (id >= lo && id < hi);
 541         }
 542 }
 543 
 544 static int seek_forward(int segment_id)
     /* [previous][next][first][last][top][bottom][index][help] */
 545 {
 546         TRACE_FUN(5, "seek_forward");
 547         int failures = 0;
 548         int result = 0;
 549         int count;
 550         static int margin = 1;  /* fixed: stop this before target */
 551         static int overshoot = 1;
 552         static int min_count = 8;
 553         int expected = -1;
 554         int target = segment_id - margin;
 555         int fast_seeking;
 556 
 557         if (!location.known) {
 558                 TRACE(1, "fatal: cannot seek from unknown location");
 559                 result = -EIO;
 560         } else if (!validate(segment_id)) {
 561                 TRACE(1, "fatal: head off track (bad hardware?)");
 562                 ftape_sleep(1 * SECOND);
 563                 ftape_failure = 1;
 564                 result = -EIO;
 565         } else {
 566                 int prev_segment = location.segment;
 567 
 568                 TRACEx4(4, "from %d/%d to %d/0 - %d", location.segment,
 569                         location.sector, segment_id, margin);
 570                 count = target - location.segment - overshoot;
 571                 fast_seeking = (count > min_count + (location.bot ? 1 : 0));
 572                 if (fast_seeking) {
 573                         TRACEx1(5, "fast skipping %d segments", count);
 574                         expected = segment_id - margin;
 575                         fast_seek(count, 0);
 576                 }
 577                 if (!tape_running) {
 578                         logical_forward();
 579                 }
 580                 while (location.segment < segment_id) {
 581                         /*  This requires at least one sector in a (bad) segment to
 582                          *  have a valid and readable sector id !
 583                          *  It looks like this is not guaranteed, so we must try
 584                          *  to find a way to skip an EMPTY_SEGMENT. !!! FIXME !!!
 585                          */
 586                         if (ftape_read_id() < 0 || !location.known) {
 587                                 location.known = 0;
 588                                 if (!tape_running || ++failures > SECTORS_PER_SEGMENT ||
 589                                     (current->signal & _DONT_BLOCK)) {
 590                                         TRACE(1, "read_id failed completely");
 591                                         result = -EIO;
 592                                         break;
 593                                 } else {
 594                                         TRACEx1(5, "read_id failed, retry (%d)", failures);
 595                                 }
 596                         } else if (fast_seeking) {
 597                                 TRACEx4(4, "ended at %d/%d (%d,%d)", location.segment,
 598                                         location.sector, overshoot, inhibit_correction);
 599                                 if (!inhibit_correction &&
 600                                     (location.segment < expected ||
 601                                  location.segment > expected + margin)) {
 602                                         int error = location.segment - expected;
 603                                         TRACEx2(4, "adjusting overshoot from %d to %d",
 604                                            overshoot, overshoot + error);
 605                                         overshoot += error;
 606                                         /*  All overshoots have the same direction, so it should
 607                                          *  never become negative, but who knows.
 608                                          */
 609                                         if (overshoot < -5 || overshoot > 10) {
 610                                                 if (overshoot < 0) {
 611                                                         overshoot = -5;         /* keep sane value */
 612                                                 } else {
 613                                                         overshoot = 10;         /* keep sane value */
 614                                                 }
 615                                                 TRACEx1(4, "clipped overshoot to %d", overshoot);
 616                                         }
 617                                 }
 618                                 fast_seeking = 0;
 619                         }
 620                         if (location.known) {
 621                                 if (location.segment > prev_segment + 1) {
 622                                         TRACEx1(4, "missed segment %d while skipping", prev_segment + 1);
 623                                 }
 624                                 prev_segment = location.segment;
 625                         }
 626                 }
 627                 if (location.segment > segment_id) {
 628                         TRACEx2(4, "failed: skip ended at segment %d/%d",
 629                                 location.segment, location.sector);
 630                         result = -EIO;
 631                 }
 632         }
 633         TRACE_EXIT;
 634         return result;
 635 }
 636 
 637 static int skip_reverse(int segment_id, int *pstatus)
     /* [previous][next][first][last][top][bottom][index][help] */
 638 {
 639         TRACE_FUN(5, "skip_reverse");
 640         int result = 0;
 641         int failures = 0;
 642         static int overshoot = 1;
 643         static int min_rewind = 2;      /* 1 + overshoot */
 644         static const int margin = 1;    /* stop this before target */
 645         int expected = 0;
 646         int count;
 647         int short_seek;
 648         int target = segment_id - margin;
 649 
 650         if (location.known && !validate(segment_id)) {
 651                 TRACE(1, "fatal: head off track (bad hardware?)");
 652                 ftape_sleep(1 * SECOND);
 653                 ftape_failure = 1;
 654                 result = -EIO;
 655         } else
 656                 do {
 657                         if (!location.known) {
 658                                 TRACE(-1, "warning: location not known");
 659                         }
 660                         TRACEx4(4, "from %d/%d to %d/0 - %d",
 661                                 location.segment, location.sector, segment_id, margin);
 662                         /*  min_rewind == 1 + overshoot_when_doing_minimum_rewind
 663                          *  overshoot  == overshoot_when_doing_larger_rewind
 664                          *  Initially min_rewind == 1 + overshoot, optimization
 665                          *  of both values will be done separately.
 666                          *  overshoot and min_rewind can be negative as both are
 667                          *  sums of three components:
 668                          *  any_overshoot == rewind_overshoot - stop_overshoot - start_overshoot
 669                          */
 670                         if (location.segment - target - (min_rewind - 1) < 1) {
 671                                 short_seek = 1;
 672                         } else {
 673                                 count = location.segment - target - overshoot;
 674                                 short_seek = (count < 1);
 675                         }
 676                         if (short_seek) {
 677                                 count = 1;      /* do shortest rewind */
 678                                 expected = location.segment - min_rewind;
 679                                 if (expected / segments_per_track != location.track) {
 680                                         expected = location.track * segments_per_track;
 681                                 }
 682                         } else {
 683                                 expected = target;
 684                         }
 685                         fast_seek(count, 1);
 686                         logical_forward();
 687                         result = ftape_read_id();
 688                         if (result == 0 && location.known) {
 689                                 TRACEx5(4, "ended at %d/%d (%d,%d,%d)", location.segment,
 690                                         location.sector, min_rewind, overshoot, inhibit_correction);
 691                                 if (!inhibit_correction &&
 692                                     (location.segment < expected ||
 693                                  location.segment > expected + margin)) {
 694                                         int error = expected - location.segment;
 695                                         if (short_seek) {
 696                                                 TRACEx2(4, "adjusting min_rewind from %d to %d",
 697                                                         min_rewind, min_rewind + error);
 698                                                 min_rewind += error;
 699                                                 if (min_rewind < -5) {  /* is this right ? FIXME ! */
 700                                                         min_rewind = -5;        /* keep sane value */
 701                                                         TRACEx1(4, "clipped min_rewind to %d", min_rewind);
 702                                                 }
 703                                         } else {
 704                                                 TRACEx2(4, "adjusting overshoot from %d to %d",
 705                                                         overshoot, overshoot + error);
 706                                                 overshoot += error;
 707                                                 if (overshoot < -5 || overshoot > 10) {
 708                                                         if (overshoot < 0) {
 709                                                                 overshoot = -5;         /* keep sane value */
 710                                                         } else {
 711                                                                 overshoot = 10;         /* keep sane value */
 712                                                         }
 713                                                         TRACEx1(4, "clipped overshoot to %d", overshoot);
 714                                                 }
 715                                         }
 716                                 }
 717                         } else {
 718                                 if ((!tape_running && !location.known) ||
 719                                     ++failures > SECTORS_PER_SEGMENT) {
 720                                         TRACE(1, "read_id failed completely");
 721                                         result = -EIO;
 722                                         break;
 723                                 } else {
 724                                         TRACEx1(5, "ftape_read_id failed, retry (%d)", failures);
 725                                 }
 726                                 result = ftape_report_drive_status(pstatus);
 727                                 if (result < 0) {
 728                                         TRACEi(1, "ftape_report_drive_status failed with code", result);
 729                                         break;
 730                                 }
 731                         }
 732                 } while (location.segment > segment_id &&
 733                          (current->signal & _DONT_BLOCK) == 0);
 734         if (location.known) {
 735                 TRACEx2(4, "current location: %d/%d", location.segment, location.sector);
 736         }
 737         TRACE_EXIT;
 738         return result;
 739 }
 740 
 741 static int determine_position(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 742 {
 743         TRACE_FUN(5, "determine_position");
 744         int retry = 0;
 745         int fatal = 0;
 746         int status;
 747         int result;
 748 
 749         if (!tape_running) {
 750                 /*  This should only happen if tape is stopped by isr.
 751                  */
 752                 TRACE(5, "waiting for tape stop");
 753                 result = ftape_ready_wait(timeout.pause, &status);
 754                 if (result < 0) {
 755                         TRACE(5, "drive still running (fatal)");
 756                         tape_running = 1;       /* ? */
 757                 }
 758         } else {
 759                 ftape_report_drive_status(&status);
 760         }
 761         if (status & QIC_STATUS_READY) {
 762                 /*  Drive must be ready to check error state !
 763                  */
 764                 TRACE(5, "drive is ready");
 765                 if (status & QIC_STATUS_ERROR) {
 766                         int error;
 767                         int command;
 768 
 769                         /*  Report and clear error state, try to continue.
 770                          */
 771                         TRACE(5, "error status set");
 772                         ftape_report_error(&error, &command, 1);
 773                         ftape_ready_wait(timeout.reset, &status);
 774                         tape_running = 0;       /* ? */
 775                 }
 776                 if (check_bot_eot(status)) {
 777                         if (location.bot) {
 778                                 if ((status & QIC_STATUS_READY) == 0) {
 779                                         /* tape moving away from bot/eot, let's see if we
 780                                          * can catch up with the first segment on this track.
 781                                          */
 782                                 } else {
 783                                         TRACE(5, "start tape from logical bot");
 784                                         logical_forward();      /* start moving */
 785                                 }
 786                         } else {
 787                                 if ((status & QIC_STATUS_READY) == 0) {
 788                                         TRACE(4, "waiting for logical end of track");
 789                                         result = ftape_ready_wait(timeout.reset, &status);
 790                                         /* error handling needed ? */
 791                                 } else {
 792                                         TRACE(4, "tape at logical end of track");
 793                                 }
 794                         }
 795                 } else {
 796                         TRACE(5, "start tape");
 797                         logical_forward();      /* start moving */
 798                         location.known = 0;     /* not cleared by logical forward ! */
 799                 }
 800         }
 801         if (!location.known) {
 802                 /* tape should be moving now, start reading id's
 803                  */
 804                 TRACE(5, "location unknown");
 805                 do {
 806                         result = ftape_read_id();
 807                         if (result < 0) {
 808                                 /*  read-id somehow failed, tape may have reached end
 809                                  *  or some other error happened.
 810                                  */
 811                                 TRACE(5, "read-id failed");
 812                                 ftape_report_drive_status(&status);
 813                                 if (status & QIC_STATUS_READY) {
 814                                         tape_running = 0;
 815                                         TRACEx1(4, "tape stopped for unknown reason ! status = 0x%02x",
 816                                                 status);
 817                                         if (status & QIC_STATUS_ERROR) {
 818                                                 fatal = 1;
 819                                         } else {
 820                                                 if (check_bot_eot(status)) {
 821                                                         result = 0;
 822                                                 } else {
 823                                                         fatal = 1;      /* oops, tape stopped but not at end ! */
 824                                                 }
 825                                         }
 826                                 }
 827                                 result = -EIO;
 828                         }
 829                 } while (result < 0 && !fatal && ++retry < SECTORS_PER_SEGMENT);
 830         } else {
 831                 result = 0;
 832         }
 833         TRACEx1(5, "tape is positioned at segment %d", location.segment);
 834         TRACE_EXIT;
 835         return result;
 836 }
 837 
 838 /*      Get the tape running and position it just before the
 839  *      requested segment.
 840  *      Seek tape-track and reposition as needed.
 841  */
 842 int ftape_start_tape(int segment_id, int sector_offset)
     /* [previous][next][first][last][top][bottom][index][help] */
 843 {
 844         TRACE_FUN(5, "ftape_start_tape");
 845         int track = segment_id / segments_per_track;
 846         int result = -EIO;
 847         int status;
 848         static int last_segment = -1;
 849         static int bad_bus_timing = 0;
 850         /* number of segments passing the head between starting the tape
 851          * and being able to access the first sector.
 852          */
 853         static int start_offset = 1;
 854         int retry = 0;
 855 
 856         /* If sector_offset > 0, seek into wanted segment instead of
 857          * into previous.
 858          * This allows error recovery if a part of the segment is bad
 859          * (erased) causing the tape drive to generate an index pulse
 860          * thus causing a no-data error before the requested sector
 861          * is reached.
 862          */
 863         tape_running = 0;
 864         TRACEx3(4, "target segment: %d/%d%s", segment_id, sector_offset,
 865                 buffer[head].retry > 0 ? " retry" : "");
 866         if (buffer[head].retry > 0) {   /* this is a retry */
 867                 if (!bad_bus_timing && ftape_data_rate == 1 &&
 868                     history.overrun_errors - overrun_count_offset >= 8) {
 869                         ftape_set_data_rate(ftape_data_rate + 1);
 870                         bad_bus_timing = 1;
 871                         TRACE(2, "reduced datarate because of excessive overrun errors");
 872                 }
 873         }
 874         last_segment = segment_id;
 875         if (location.track != track || (might_be_off_track &&
 876                                         buffer[head].retry == 0)) {
 877                 /* current track unknown or not equal to destination
 878                  */
 879                 ftape_ready_wait(timeout.seek, &status);
 880                 ftape_seek_head_to_track(track);
 881                 overrun_count_offset = history.overrun_errors;
 882         }
 883         do {
 884                 if (!location.known) {
 885                         determine_position();
 886                 }
 887                 /*  Check if we are able to catch the requested segment in time.
 888                  */
 889                 if (location.known && location.segment >= segment_id -
 890                     ((tape_running || location.bot) ? 0 : start_offset)) {
 891                         /*  Too far ahead (in or past target segment).
 892                          */
 893                         if (tape_running) {
 894                                 result = stop_tape(&status);
 895                                 if (result < 0) {
 896                                         TRACEi(1, "stop tape failed with code", result);
 897                                         break;
 898                                 }
 899                                 TRACE(5, "tape stopped");
 900                                 tape_running = 0;
 901                         }
 902                         TRACE(5, "repositioning");
 903                         ++history.rewinds;
 904                         if (segment_id % segments_per_track < start_offset) {
 905                                 /*  If seeking to first segments on track better do a complete
 906                                  *  rewind to logical begin of track to get a more steady tape
 907                                  *  motion.
 908                                  */
 909                                 result = ftape_command_wait((location.track & 1) ?
 910                                                    QIC_PHYSICAL_FORWARD :
 911                                                     QIC_PHYSICAL_REVERSE,
 912                                                 timeout.rewind, &status);
 913                                 check_bot_eot(status);  /* update location */
 914                         } else {
 915                                 result = skip_reverse(segment_id - start_offset, &status);
 916                         }
 917                 }
 918                 if (!location.known) {
 919                         TRACE(-1, "panic: location not known");
 920                         result = -EIO;
 921                         if ((current->signal & _DONT_BLOCK) || ftape_failure) {
 922                                 break;
 923                         } else {
 924                                 continue;
 925                         }
 926                 }
 927                 TRACEx2(4, "current segment: %d/%d", location.segment, location.sector);
 928                 /*  We're on the right track somewhere before the wanted segment.
 929                  *  Start tape movement if needed and skip to just before or inside
 930                  *  the requested segment. Keep tape running.
 931                  */
 932                 result = 0;
 933                 if (location.segment < segment_id -
 934                     ((tape_running || location.bot) ? 0 : start_offset)) {
 935                         if (sector_offset > 0) {
 936                                 result = seek_forward(segment_id);
 937                         } else {
 938                                 result = seek_forward(segment_id - 1);
 939                         }
 940                 }
 941                 if (result == 0 &&
 942                     location.segment != segment_id - (sector_offset > 0 ? 0 : 1)) {
 943                         result = -EIO;
 944                 }
 945         } while (result < 0 && !ftape_failure &&
 946                  (current->signal & _DONT_BLOCK) == 0 &&
 947                  ++retry <= 5);
 948         if (result < 0) {
 949                 TRACE(1, "failed to reposition");
 950         }
 951         TRACE_EXIT;
 952         return result;
 953 }

/* [previous][next][first][last][top][bottom][index][help] */