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