linux-old/drivers/char/ftape/lowlevel/ftape-bsm.c
<<
>>
Prefs
   1/*
   2 *      Copyright (C) 1994-1996 Bas Laarhoven,
   3 *                (C) 1996-1997 Claus 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-bsm.c,v $
  21 * $Revision: 1.3 $
  22 * $Date: 1997/10/05 19:15:15 $
  23 *
  24 *      This file contains the bad-sector map handling code for
  25 *      the QIC-117 floppy tape driver for Linux.
  26 *      QIC-40, QIC-80, QIC-3010 and QIC-3020 maps are implemented.
  27 */
  28
  29#include <linux/string.h>
  30
  31#include <linux/ftape.h>
  32#include "../lowlevel/ftape-tracing.h"
  33#include "../lowlevel/ftape-bsm.h"
  34#include "../lowlevel/ftape-ctl.h"
  35#include "../lowlevel/ftape-rw.h"
  36
  37/*      Global vars.
  38 */
  39
  40/*      Local vars.
  41 */
  42static __u8 *bad_sector_map;
  43static SectorCount *bsm_hash_ptr; 
  44
  45typedef enum {
  46        forward, backward
  47} mode_type;
  48
  49#if 0
  50/*  fix_tape converts a normal QIC-80 tape into a 'wide' tape.
  51 *  For testing purposes only !
  52 */
  53void fix_tape(__u8 * buffer, ft_format_type new_code)
  54{
  55        static __u8 list[BAD_SECTOR_MAP_SIZE];
  56        SectorMap *src_ptr = (SectorMap *) list;
  57        __u8 *dst_ptr = bad_sector_map;
  58        SectorMap map;
  59        unsigned int sector = 1;
  60        int i;
  61
  62        if (format_code != fmt_var && format_code != fmt_big) {
  63                memcpy(list, bad_sector_map, sizeof(list));
  64                memset(bad_sector_map, 0, sizeof(bad_sector_map));
  65                while ((__u8 *) src_ptr - list < sizeof(list)) {
  66                        map = *src_ptr++;
  67                        if (map == EMPTY_SEGMENT) {
  68                                *(SectorMap *) dst_ptr = 0x800000 + sector;
  69                                dst_ptr += 3;
  70                                sector += SECTORS_PER_SEGMENT;
  71                        } else {
  72                                for (i = 0; i < SECTORS_PER_SEGMENT; ++i) {
  73                                        if (map & 1) {
  74                                                *(SewctorMap *) dst_ptr = sector;
  75                                                dst_ptr += 3;
  76                                        }
  77                                        map >>= 1;
  78                                        ++sector;
  79                                }
  80                        }
  81                }
  82        }
  83        bad_sector_map_changed = 1;
  84        *(buffer + 4) = new_code;       /* put new format code */
  85        if (format_code != fmt_var && new_code == fmt_big) {
  86                PUT4(buffer, FT_6_HSEG_1,   (__u32)GET2(buffer, 6));
  87                PUT4(buffer, FT_6_HSEG_2,   (__u32)GET2(buffer, 8));
  88                PUT4(buffer, FT_6_FRST_SEG, (__u32)GET2(buffer, 10));
  89                PUT4(buffer, FT_6_LAST_SEG, (__u32)GET2(buffer, 12));
  90                memset(buffer+6, '\0', 8);
  91        }
  92        format_code = new_code;
  93}
  94
  95#endif
  96
  97/*   given buffer that contains a header segment, find the end of
  98 *   of the bsm list
  99 */
 100__u8 * ftape_find_end_of_bsm_list(__u8 * address)
 101{
 102        __u8 *ptr   = address + FT_HEADER_END; /* start of bsm list */
 103        __u8 *limit = address + FT_SEGMENT_SIZE;
 104        while (ptr + 2 < limit) {
 105                if (ptr[0] || ptr[1] || ptr[2]) {
 106                        ptr += 3;
 107                } else {
 108                        return ptr;
 109                }
 110        }
 111        return NULL;
 112}
 113
 114static inline void put_sector(SectorCount *ptr, unsigned int sector)
 115{
 116        ptr->bytes[0] = sector & 0xff;
 117        sector >>= 8;
 118        ptr->bytes[1] = sector & 0xff;
 119        sector >>= 8;
 120        ptr->bytes[2] = sector & 0xff;
 121}
 122
 123static inline unsigned int get_sector(SectorCount *ptr)
 124{
 125#if 1
 126        unsigned int sector;
 127
 128        sector  = ptr->bytes[0];
 129        sector += ptr->bytes[1] <<  8;
 130        sector += ptr->bytes[2] << 16;
 131
 132        return sector;
 133#else
 134        /*  GET4 gets the next four bytes in Intel little endian order
 135         *  and converts them to host byte order and handles unaligned
 136         *  access.
 137         */
 138        return (GET4(ptr, 0) & 0x00ffffff); /* back to host byte order */
 139#endif
 140}
 141
 142static void bsm_debug_fake(void)
 143{
 144        /* for testing of bad sector handling at end of tape
 145         */
 146#if 0
 147        ftape_put_bad_sector_entry(segments_per_track * tracks_per_tape - 3,
 148                                   0x000003e0;
 149        ftape_put_bad_sector_entry(segments_per_track * tracks_per_tape - 2,
 150                                   0xff3fffff;
 151        ftape_put_bad_sector_entry(segments_per_track * tracks_per_tape - 1,
 152                                   0xffffe000;
 153#endif
 154        /*  Enable to test bad sector handling
 155         */
 156#if 0
 157        ftape_put_bad_sector_entry(30, 0xfffffffe)
 158        ftape_put_bad_sector_entry(32, 0x7fffffff);
 159        ftape_put_bad_sector_entry(34, 0xfffeffff);
 160        ftape_put_bad_sector_entry(36, 0x55555555);
 161        ftape_put_bad_sector_entry(38, 0xffffffff);
 162        ftape_put_bad_sector_entry(50, 0xffff0000);
 163        ftape_put_bad_sector_entry(51, 0xffffffff);
 164        ftape_put_bad_sector_entry(52, 0xffffffff);
 165        ftape_put_bad_sector_entry(53, 0x0000ffff);
 166#endif
 167        /*  Enable when testing multiple volume tar dumps.
 168         */
 169#if 0
 170        {
 171                int i;
 172
 173                for (i = ft_first_data_segment;
 174                     i <= ft_last_data_segment - 7; ++i) {
 175                        ftape_put_bad_sector_entry(i, EMPTY_SEGMENT);
 176                }
 177        }
 178#endif
 179        /*  Enable when testing bit positions in *_error_map
 180         */
 181#if 0
 182        {
 183                int i;
 184                
 185                for (i = first_data_segment; i <= last_data_segment; ++i) {
 186                        ftape_put_bad_sector_entry(i,
 187                                           ftape_get_bad_sector_entry(i) 
 188                                           | 0x00ff00ff);
 189                }
 190        }
 191#endif
 192}
 193
 194static void print_bad_sector_map(void)
 195{
 196        unsigned int good_sectors;
 197        unsigned int total_bad = 0;
 198        int i;
 199        TRACE_FUN(ft_t_flow);
 200
 201        if (ft_format_code == fmt_big || 
 202            ft_format_code == fmt_var || 
 203            ft_format_code == fmt_1100ft) {
 204                SectorCount *ptr = (SectorCount *)bad_sector_map;
 205                unsigned int sector;
 206
 207                while((sector = get_sector(ptr++)) != 0) {
 208                        if ((ft_format_code == fmt_big || 
 209                             ft_format_code == fmt_var) &&
 210                            sector & 0x800000) {
 211                                total_bad += FT_SECTORS_PER_SEGMENT - 3;
 212                                TRACE(ft_t_noise, "bad segment at sector: %6d",
 213                                      sector & 0x7fffff);
 214                        } else {
 215                                ++total_bad;
 216                                TRACE(ft_t_noise, "bad sector: %6d", sector);
 217                        }
 218                }
 219                /*  Display old ftape's end-of-file marks
 220                 */
 221                while ((sector = get_unaligned(((__u16*)ptr)++)) != 0) {
 222                        TRACE(ft_t_noise, "Old ftape eof mark: %4d/%2d",
 223                              sector, get_unaligned(((__u16*)ptr)++));
 224                }
 225        } else { /* fixed size format */
 226                for (i = ft_first_data_segment;
 227                     i < (int)(ft_segments_per_track * ft_tracks_per_tape); ++i) {
 228                        SectorMap map = ((SectorMap *) bad_sector_map)[i];
 229
 230                        if (map) {
 231                                TRACE(ft_t_noise,
 232                                      "bsm for segment %4d: 0x%08x", i, (unsigned int)map);
 233                                total_bad += ((map == EMPTY_SEGMENT)
 234                                               ? FT_SECTORS_PER_SEGMENT - 3
 235                                               : count_ones(map));
 236                        }
 237                }
 238        }
 239        good_sectors =
 240                ((ft_segments_per_track * ft_tracks_per_tape - ft_first_data_segment)
 241                 * (FT_SECTORS_PER_SEGMENT - 3)) - total_bad;
 242        TRACE(ft_t_info, "%d Kb usable on this tape", good_sectors);
 243        if (total_bad == 0) {
 244                TRACE(ft_t_info,
 245                      "WARNING: this tape has no bad blocks registered !");
 246        } else {
 247                TRACE(ft_t_info, "%d bad sectors", total_bad);
 248        }
 249        TRACE_EXIT;
 250}
 251
 252
 253void ftape_extract_bad_sector_map(__u8 * buffer)
 254{
 255        TRACE_FUN(ft_t_any);
 256
 257        /*  Fill the bad sector map with the contents of buffer.
 258         */
 259        if (ft_format_code == fmt_var || ft_format_code == fmt_big) {
 260                /* QIC-3010/3020 and wide QIC-80 tapes no longer have a failed
 261                 * sector log but use this area to extend the bad sector map.
 262                 */
 263                bad_sector_map = &buffer[FT_HEADER_END];
 264        } else {
 265                /* non-wide QIC-80 tapes have a failed sector log area that
 266                 * mustn't be included in the bad sector map.
 267                 */
 268                bad_sector_map = &buffer[FT_FSL + FT_FSL_SIZE];
 269        }
 270        if (ft_format_code == fmt_1100ft || 
 271            ft_format_code == fmt_var    ||
 272            ft_format_code == fmt_big) {
 273                bsm_hash_ptr = (SectorCount *)bad_sector_map;
 274        } else {
 275                bsm_hash_ptr = NULL;
 276        }
 277        bsm_debug_fake();
 278        if (TRACE_LEVEL >= ft_t_info) {
 279                print_bad_sector_map();
 280        }
 281        TRACE_EXIT;
 282}
 283
 284static inline SectorMap cvt2map(unsigned int sector)
 285{
 286        return 1 << (((sector & 0x7fffff) - 1) % FT_SECTORS_PER_SEGMENT);
 287}
 288
 289static inline int cvt2segment(unsigned int sector)
 290{
 291        return ((sector & 0x7fffff) - 1) / FT_SECTORS_PER_SEGMENT;
 292}
 293
 294static int forward_seek_entry(int segment_id, 
 295                              SectorCount **ptr, 
 296                              SectorMap *map)
 297{
 298        unsigned int sector;
 299        int segment;
 300
 301        do {
 302                sector = get_sector((*ptr)++);
 303                segment = cvt2segment(sector);
 304        } while (sector != 0 && segment < segment_id);
 305        (*ptr) --; /* point to first sector >= segment_id */
 306        /*  Get all sectors in segment_id
 307         */
 308        if (sector == 0 || segment != segment_id) {
 309                *map = 0;
 310                return 0;
 311        } else if ((sector & 0x800000) &&
 312                   (ft_format_code == fmt_var || ft_format_code == fmt_big)) {
 313                *map = EMPTY_SEGMENT;
 314                return FT_SECTORS_PER_SEGMENT;
 315        } else {
 316                int count = 1;
 317                SectorCount *tmp_ptr = (*ptr) + 1;
 318                
 319                *map = cvt2map(sector);
 320                while ((sector = get_sector(tmp_ptr++)) != 0 &&
 321                       (segment = cvt2segment(sector)) == segment_id) {
 322                        *map |= cvt2map(sector);
 323                        ++count;
 324                }
 325                return count;
 326        }
 327}
 328
 329static int backwards_seek_entry(int segment_id,
 330                                SectorCount **ptr,
 331                                SectorMap *map)
 332{
 333        unsigned int sector;
 334        int segment; /* max unsigned int */
 335
 336        if (*ptr <= (SectorCount *)bad_sector_map) {
 337                *map = 0;
 338                return 0;
 339        }
 340        do {
 341                sector  = get_sector(--(*ptr));
 342                segment = cvt2segment(sector);
 343        } while (*ptr > (SectorCount *)bad_sector_map && segment > segment_id);
 344        if (segment > segment_id) { /*  at start of list, no entry found */
 345                *map = 0;
 346                return 0;
 347        } else if (segment < segment_id) {
 348                /*  before smaller entry, adjust for overshoot */
 349                (*ptr) ++;
 350                *map = 0;
 351                return 0;
 352        } else if ((sector & 0x800000) &&
 353                   (ft_format_code == fmt_big || ft_format_code == fmt_var)) {
 354                *map = EMPTY_SEGMENT;
 355                return FT_SECTORS_PER_SEGMENT;
 356        } else { /*  get all sectors in segment_id */
 357                int count = 1;
 358
 359                *map = cvt2map(sector);
 360                while(*ptr > (SectorCount *)bad_sector_map) {
 361                        sector = get_sector(--(*ptr));
 362                        segment = cvt2segment(sector);
 363                        if (segment != segment_id) {
 364                                break;
 365                        }
 366                        *map |= cvt2map(sector);
 367                        ++count;
 368                }
 369                if (segment < segment_id) {
 370                        (*ptr) ++;
 371                }
 372                return count;
 373        }
 374}
 375
 376void ftape_put_bad_sector_entry(int segment_id, SectorMap new_map)
 377{
 378        SectorCount *ptr = (SectorCount *)bad_sector_map;
 379        int count;
 380        int new_count;
 381        SectorMap map;
 382        TRACE_FUN(ft_t_any);
 383
 384        if (ft_format_code == fmt_1100ft || 
 385            ft_format_code == fmt_var || 
 386            ft_format_code == fmt_big) {
 387                count = forward_seek_entry(segment_id, &ptr, &map);
 388                new_count = count_ones(new_map);
 389                /* If format code == 4 put empty segment instead of 32
 390                 * bad sectors.
 391                 */
 392                if (ft_format_code == fmt_var || ft_format_code == fmt_big) {
 393                        if (new_count == FT_SECTORS_PER_SEGMENT) {
 394                                new_count = 1;
 395                        }
 396                        if (count == FT_SECTORS_PER_SEGMENT) {
 397                                count = 1;
 398                        }
 399                }
 400                if (count != new_count) {
 401                        /* insert (or delete if < 0) new_count - count
 402                         * entries.  Move trailing part of list
 403                         * including terminating 0.
 404                         */
 405                        SectorCount *hi_ptr = ptr;
 406
 407                        do {
 408                        } while (get_sector(hi_ptr++) != 0);
 409                        /*  Note: ptr is of type byte *, and each bad sector
 410                         *  consumes 3 bytes.
 411                         */
 412                        memmove(ptr + new_count, ptr + count,
 413                                (size_t)(hi_ptr - (ptr + count))*sizeof(SectorCount));
 414                }
 415                TRACE(ft_t_noise, "putting map 0x%08x at %p, segment %d",
 416                      (unsigned int)new_map, ptr, segment_id);
 417                if (new_count == 1 && new_map == EMPTY_SEGMENT) {
 418                        put_sector(ptr++, (0x800001 + 
 419                                          segment_id * 
 420                                          FT_SECTORS_PER_SEGMENT));
 421                } else {
 422                        int i = 0;
 423
 424                        while (new_map) {
 425                                if (new_map & 1) {
 426                                        put_sector(ptr++, 
 427                                                   1 + segment_id * 
 428                                                   FT_SECTORS_PER_SEGMENT + i);
 429                                }
 430                                ++i;
 431                                new_map >>= 1;
 432                        }
 433                }
 434        } else {
 435                ((SectorMap *) bad_sector_map)[segment_id] = new_map;
 436        }
 437        TRACE_EXIT;
 438}
 439
 440SectorMap ftape_get_bad_sector_entry(int segment_id)
 441{
 442        if (ft_used_header_segment == -1) {
 443                /*  When reading header segment we'll need a blank map.
 444                 */
 445                return 0;
 446        } else if (bsm_hash_ptr != NULL) {
 447                /*  Invariants:
 448                 *    map - mask value returned on last call.
 449                 *    bsm_hash_ptr - points to first sector greater or equal to
 450                 *          first sector in last_referenced segment.
 451                 *    last_referenced - segment id used in the last call,
 452                 *                      sector and map belong to this id.
 453                 *  This code is designed for sequential access and retries.
 454                 *  For true random access it may have to be redesigned.
 455                 */
 456                static int last_reference = -1;
 457                static SectorMap map;
 458
 459                if (segment_id > last_reference) {
 460                        /*  Skip all sectors before segment_id
 461                         */
 462                        forward_seek_entry(segment_id, &bsm_hash_ptr, &map);
 463                } else if (segment_id < last_reference) {
 464                        /* Skip backwards until begin of buffer or
 465                         * first sector in segment_id 
 466                         */
 467                        backwards_seek_entry(segment_id, &bsm_hash_ptr, &map);
 468                }               /* segment_id == last_reference : keep map */
 469                last_reference = segment_id;
 470                return map;
 471        } else {
 472                return ((SectorMap *) bad_sector_map)[segment_id];
 473        }
 474}
 475
 476/*  This is simply here to prevent us from overwriting other kernel
 477 *  data. Writes will result in NULL Pointer dereference.
 478 */
 479void ftape_init_bsm(void)
 480{
 481        bad_sector_map = NULL;
 482        bsm_hash_ptr   = NULL;
 483}
 484
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.