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

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

DEFINITIONS

This source file includes following definitions.
  1. fix_tape
  2. find_end_of_bsm_list
  3. store_bad_sector_map
  4. put_sector
  5. get_sector
  6. extract_bad_sector_map
  7. cvt2map
  8. cvt2segment
  9. forward_seek_entry
  10. backwards_seek_entry
  11. put_bad_sector_entry
  12. get_bad_sector_entry
  13. ftape_init_bsm

   1 /*
   2  *      Copyright (C) 1994-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-bsm.c,v $
  19  $Author: bas $
  20  *
  21  $Revision: 1.7 $
  22  $Date: 1995/04/30 13:15:14 $
  23  $State: Beta $
  24  *
  25  *      This file contains the bad-sector map handling code for
  26  *      the QIC-117 floppy tape driver for Linux.
  27  *      QIC-40, QIC-80, QIC-3010 and QIC-3020 maps are implemented.
  28  */
  29 
  30 #include <linux/module.h>
  31 #include <linux/ftape.h>
  32 #include <linux/string.h>
  33 
  34 #include "tracing.h"
  35 #include "ftape-bsm.h"
  36 #include "ftape-ctl.h"
  37 #include "ftape-rw.h"
  38 
  39 
  40 /*      Global vars.
  41  */
  42 int bad_sector_map_changed = 0;
  43 
  44 /*      Local vars.
  45  */
  46 static byte bad_sector_map[BAD_SECTOR_MAP_SIZE];
  47 typedef enum {
  48         forward, backward
  49 } mode_type;
  50 
  51 #if 0
  52 /*  fix_tape converts a normal QIC-80 tape into a 'wide' tape.
  53  *  For testing purposes only !
  54  */
  55 void fix_tape(byte * buffer)
     /* [previous][next][first][last][top][bottom][index][help] */
  56 {
  57         static byte list[BAD_SECTOR_MAP_SIZE];
  58         unsigned long *src_ptr = (unsigned long *) list;
  59         byte *dst_ptr = bad_sector_map;
  60         unsigned long map;
  61         unsigned sector = 1;
  62         int i;
  63 
  64         memcpy(list, bad_sector_map, sizeof(list));
  65         memset(bad_sector_map, 0, sizeof(bad_sector_map));
  66         while ((byte *) src_ptr - list < sizeof(list)) {
  67                 map = *src_ptr++;
  68                 if (map == EMPTY_SEGMENT) {
  69                         *(unsigned long *) dst_ptr = 0x800000 + sector;
  70                         dst_ptr += 3;
  71                         sector += SECTORS_PER_SEGMENT;
  72                 } else {
  73                         for (i = 0; i < SECTORS_PER_SEGMENT; ++i) {
  74                                 if (map & 1) {
  75                                         *(unsigned long *) dst_ptr = sector;
  76                                         dst_ptr += 3;
  77                                 }
  78                                 map >>= 1;
  79                                 ++sector;
  80                         }
  81                 }
  82         }
  83         bad_sector_map_changed = 1;
  84         *(buffer + 4) = 4;      /* put new format code */
  85         format_code = 4;
  86 }
  87 
  88 #endif
  89 
  90 byte *
  91  find_end_of_bsm_list(byte * ptr, byte * limit)
     /* [previous][next][first][last][top][bottom][index][help] */
  92 {
  93         while (ptr + 2 < limit) {
  94                 if (ptr[0] || ptr[1] || ptr[2]) {
  95                         ptr += 3;
  96                 } else {
  97                         return ptr;
  98                 }
  99         }
 100         return NULL;
 101 }
 102 
 103 void store_bad_sector_map(byte * buffer)
     /* [previous][next][first][last][top][bottom][index][help] */
 104 {
 105         TRACE_FUN(8, "store_bad_sector_map");
 106         size_t count;
 107         size_t offset;
 108 
 109         /*  Store the bad sector map in buffer.
 110          */
 111         if (format_code == 4) {
 112                 offset = 256;
 113                 count = sizeof(bad_sector_map);
 114         } else {
 115                 offset = 2 * SECTOR_SIZE;       /* skip failed sector log */
 116                 count = sizeof(bad_sector_map) - (offset - 256);
 117         }
 118         memcpy(buffer + offset, bad_sector_map, count);
 119         TRACE_EXIT;
 120 }
 121 
 122 void put_sector(byte ** ptr, unsigned long sector)
     /* [previous][next][first][last][top][bottom][index][help] */
 123 {
 124         *(*ptr)++ = sector & 0xff;
 125         sector >>= 8;
 126         *(*ptr)++ = sector & 0xff;
 127         sector >>= 8;
 128         *(*ptr)++ = sector & 0xff;
 129 }
 130 
 131 unsigned long get_sector(byte ** ptr, mode_type mode)
     /* [previous][next][first][last][top][bottom][index][help] */
 132 {
 133         unsigned long sector;
 134 
 135         if (mode == forward) {
 136                 sector = *(*ptr)++;
 137                 sector += *(*ptr)++ << 8;
 138                 sector += *(*ptr)++ << 16;
 139         } else {
 140                 sector = *--(*ptr) << 16;
 141                 sector += *--(*ptr) << 8;
 142                 sector += *--(*ptr);
 143         }
 144         return sector;
 145 }
 146 
 147 void extract_bad_sector_map(byte * buffer)
     /* [previous][next][first][last][top][bottom][index][help] */
 148 {
 149         TRACE_FUN(8, "extract_bad_sector_map");
 150 
 151         /*  Fill the bad sector map with the contents of buffer.
 152          */
 153         if (format_code == 4) {
 154                 /* QIC-3010/3020 and wide QIC-80 tapes no longer have a failed
 155                  * sector log but use this area to extend the bad sector map.
 156                  */
 157                 memcpy(bad_sector_map, buffer + 256, sizeof(bad_sector_map));
 158         } else {
 159                 /* non-wide QIC-80 tapes have a failed sector log area that
 160                  * mustn't be included in the bad sector map.
 161                  */
 162                 memcpy(bad_sector_map, buffer + 256 + FAILED_SECTOR_LOG_SIZE,
 163                        sizeof(bad_sector_map) - FAILED_SECTOR_LOG_SIZE);
 164         }
 165 #if 0
 166         /* for testing of bad sector handling at end of tape
 167          */
 168         ((unsigned long *) bad_sector_map)[segments_per_track * tracks_per_tape - 3] = 0x000003e0;
 169         ((unsigned long *) bad_sector_map)[segments_per_track * tracks_per_tape - 2] = 0xff3fffff;
 170         ((unsigned long *) bad_sector_map)[segments_per_track * tracks_per_tape - 1] = 0xffffe000;
 171 #endif
 172 #if 0
 173         /*  Enable to test bad sector handling
 174          */
 175         ((unsigned long *) bad_sector_map)[30] = 0xfffffffe;
 176         ((unsigned long *) bad_sector_map)[32] = 0x7fffffff;
 177         ((unsigned long *) bad_sector_map)[34] = 0xfffeffff;
 178         ((unsigned long *) bad_sector_map)[36] = 0x55555555;
 179         ((unsigned long *) bad_sector_map)[38] = 0xffffffff;
 180         ((unsigned long *) bad_sector_map)[50] = 0xffff0000;
 181         ((unsigned long *) bad_sector_map)[51] = 0xffffffff;
 182         ((unsigned long *) bad_sector_map)[52] = 0xffffffff;
 183         ((unsigned long *) bad_sector_map)[53] = 0x0000ffff;
 184 #endif
 185 #if 0
 186         /*  Enable when testing multiple volume tar dumps.
 187          */
 188         for (i = first_data_segment; i <= ftape_last_segment.id - 7; ++i) {
 189                 ((unsigned long *) bad_sector_map)[i] = EMPTY_SEGMENT;
 190         }
 191 #endif
 192 #if 0
 193         /*  Enable when testing bit positions in *_error_map
 194          */
 195         for (i = first_data_segment; i <= ftape_last_segment.id; ++i) {
 196                 ((unsigned long *) bad_sector_map)[i] |= 0x00ff00ff;
 197         }
 198 #endif
 199         if (tracing > 2) {
 200                 unsigned int map;
 201                 int good_sectors = 0;
 202                 int bad_sectors;
 203                 unsigned int total_bad = 0;
 204                 int i;
 205 
 206                 if (format_code == 4 || format_code == 3) {
 207                         byte *ptr = bad_sector_map;
 208                         unsigned sector;
 209 
 210                         do {
 211                                 sector = get_sector(&ptr, forward);
 212                                 if (sector != 0) {
 213                                         if (format_code == 4 && sector & 0x800000) {
 214                                                 total_bad += SECTORS_PER_SEGMENT - 3;
 215                                                 TRACEx1(6, "bad segment at sector: %6d", sector & 0x7fffff);
 216                                         } else {
 217                                                 ++total_bad;
 218                                                 TRACEx1(6, "bad sector: %6d", sector);
 219                                         }
 220                                 }
 221                         } while (sector != 0);
 222                         /*  Display end-of-file marks
 223                          */
 224                         do {
 225                                 sector = *((unsigned short *) ptr)++;
 226                                 if (sector) {
 227                                         TRACEx2(4, "eof mark: %4d/%2d", sector,
 228                                             *((unsigned short *) ptr)++);
 229                                 }
 230                         } while (sector);
 231                 } else {
 232                         for (i = first_data_segment;
 233                          i < segments_per_track * tracks_per_tape; ++i) {
 234                                 map = ((unsigned long *) bad_sector_map)[i];
 235                                 bad_sectors = count_ones(map);
 236                                 if (bad_sectors > 0) {
 237                                         TRACEx2(6, "bsm for segment %4d: 0x%08x", i, map);
 238                                         if (bad_sectors > SECTORS_PER_SEGMENT - 3) {
 239                                                 bad_sectors = SECTORS_PER_SEGMENT - 3;
 240                                         }
 241                                         total_bad += bad_sectors;
 242                                 }
 243                         }
 244                 }
 245                 good_sectors = ((segments_per_track * tracks_per_tape - first_data_segment)
 246                                 * (SECTORS_PER_SEGMENT - 3)) - total_bad;
 247                 TRACEx1(3, "%d Kb usable on this tape",
 248                         good_sectors - ftape_last_segment.free);
 249                 if (total_bad == 0) {
 250                         TRACE(1, "WARNING: this tape has no bad blocks registered !");
 251                 } else {
 252                         TRACEx1(2, "%d bad sectors", total_bad);
 253                 }
 254         }
 255         TRACE_EXIT;
 256 }
 257 
 258 unsigned long cvt2map(int sector)
     /* [previous][next][first][last][top][bottom][index][help] */
 259 {
 260         return 1 << (((sector & 0x7fffff) - 1) % SECTORS_PER_SEGMENT);
 261 }
 262 
 263 int cvt2segment(int sector)
     /* [previous][next][first][last][top][bottom][index][help] */
 264 {
 265         return ((sector & 0x7fffff) - 1) / SECTORS_PER_SEGMENT;
 266 }
 267 
 268 int forward_seek_entry(int segment_id, byte ** ptr, unsigned long *map)
     /* [previous][next][first][last][top][bottom][index][help] */
 269 {
 270         byte *tmp_ptr;
 271         unsigned long sector;
 272         int segment;
 273         int count;
 274 
 275         do {
 276                 sector = get_sector(ptr, forward);
 277                 segment = cvt2segment(sector);
 278         } while (sector != 0 && segment < segment_id);
 279         tmp_ptr = *ptr - 3;     /* point to first sector >= segment_id */
 280         /*  Get all sectors in segment_id
 281          */
 282         if (format_code == 4 && (sector & 0x800000) && segment == segment_id) {
 283                 *map = EMPTY_SEGMENT;
 284                 count = 32;
 285         } else {
 286                 *map = 0;
 287                 count = 0;
 288                 while (sector != 0 && segment == segment_id) {
 289                         *map |= cvt2map(sector);
 290                         sector = get_sector(ptr, forward);
 291                         segment = cvt2segment(sector);
 292                         ++count;
 293                 }
 294         }
 295         *ptr = tmp_ptr;
 296         return count;
 297 }
 298 
 299 int backwards_seek_entry(int segment_id, byte ** ptr, unsigned long *map)
     /* [previous][next][first][last][top][bottom][index][help] */
 300 {
 301         unsigned long sector;
 302         int segment;
 303         int count;
 304 
 305         *map = 0;
 306         if (*ptr > bad_sector_map) {
 307                 do {
 308                         sector = get_sector(ptr, backward);
 309                         segment = cvt2segment(sector);
 310                 } while (*ptr > bad_sector_map && segment > segment_id);
 311                 count = 0;
 312                 if (segment > segment_id) {
 313                         /*  at start of list, no entry found */
 314                 } else if (segment < segment_id) {
 315                         /*  before smaller entry, adjust for overshoot */
 316                         *ptr += 3;
 317                 } else {
 318                         /*  get all sectors in segment_id */
 319                         if (format_code == 4 && (sector & 0x800000)) {
 320                                 *map = EMPTY_SEGMENT;
 321                                 count = 32;
 322                         } else {
 323                                 do {
 324                                         *map |= cvt2map(sector);
 325                                         ++count;
 326                                         if (*ptr <= bad_sector_map) {
 327                                                 break;
 328                                         }
 329                                         sector = get_sector(ptr, backward);
 330                                         segment = cvt2segment(sector);
 331                                 } while (segment == segment_id);
 332                                 if (segment < segment_id) {
 333                                         *ptr += 3;
 334                                 }
 335                         }
 336                 }
 337         } else {
 338                 count = 0;
 339         }
 340         return count;
 341 }
 342 
 343 void put_bad_sector_entry(int segment_id, unsigned long new_map)
     /* [previous][next][first][last][top][bottom][index][help] */
 344 {
 345         byte *ptr = bad_sector_map;
 346         int count;
 347         int new_count;
 348         unsigned long map;
 349 
 350         if (format_code == 3 || format_code == 4) {
 351                 count = forward_seek_entry(segment_id, &ptr, &map);
 352                 new_count = count_ones(new_map);
 353                 /*  If format code == 4 put empty segment instead of 32 bad sectors.
 354                  */
 355                 if (new_count == 32 && format_code == 4) {
 356                         new_count = 1;
 357                 }
 358                 if (count != new_count) {
 359                         /*  insert (or delete if < 0) new_count - count entries.
 360                          *  Move trailing part of list including terminating 0.
 361                          */
 362                         byte *hi_ptr = ptr;
 363 
 364                         do {
 365                         } while (get_sector(&hi_ptr, forward) != 0);
 366                         memmove(ptr + new_count, ptr + count, hi_ptr - (ptr + count));
 367                 }
 368                 if (new_count == 1 && new_map == EMPTY_SEGMENT) {
 369                         put_sector(&ptr, 0x800001 + segment_id * SECTORS_PER_SEGMENT);
 370                 } else {
 371                         int i = 0;
 372 
 373                         while (new_map) {
 374                                 if (new_map & 1) {
 375                                         put_sector(&ptr, 1 + segment_id * SECTORS_PER_SEGMENT + i);
 376                                 }
 377                                 ++i;
 378                                 new_map >>= 1;
 379                         }
 380                 }
 381         } else {
 382                 ((unsigned long *) bad_sector_map)[segment_id] = new_map;
 383         }
 384         bad_sector_map_changed = 1;
 385 }
 386 
 387 unsigned long get_bad_sector_entry(int segment_id)
     /* [previous][next][first][last][top][bottom][index][help] */
 388 {
 389         TRACE_FUN(8, "get_bad_sector_entry");
 390         static unsigned long map = 0;
 391 
 392         if (used_header_segment == -1) {
 393                 /*  When reading header segment we'll need a blank map.
 394                  */
 395                 map = 0;
 396         } else if (format_code == 3 || format_code == 4) {
 397                 /*  Invariants:
 398                  *    map - mask value returned on last call.
 399                  *    ptr - points to first sector greater or equal to
 400                  *          first sector in last_referenced segment.
 401                  *    last_referenced - segment id used in the last call,
 402                  *                      sector and map belong to this id.
 403                  *  This code is designed for sequential access and retries.
 404                  *  For true random access it may have to be redesigned.
 405                  */
 406                 static int last_reference = -1;
 407                 static byte *ptr = bad_sector_map;
 408 
 409                 if (segment_id > last_reference) {
 410                         /*  Skip all sectors before segment_id
 411                          */
 412                         forward_seek_entry(segment_id, &ptr, &map);
 413                 } else if (segment_id < last_reference) {
 414                         /*  Skip backwards until begin of buffer or first sector in segment_id
 415                          */
 416                         backwards_seek_entry(segment_id, &ptr, &map);
 417                 }               /* segment_id == last_reference : keep map */
 418                 last_reference = segment_id;
 419         } else {
 420                 map = ((unsigned long *) bad_sector_map)[segment_id];
 421         }
 422         TRACE_EXIT;
 423         return map;
 424 }
 425 
 426 void ftape_init_bsm(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 427 {
 428         memset(bad_sector_map, 0, sizeof(bad_sector_map));
 429 }

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