2 * Copyright (C) 1994-1996 Bas Laarhoven,
3 * (C) 1996-1997 Claus Heine.
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)
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.
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.
20 * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-bsm.c,v $
22 * $Date: 1997/10/05 19:15:15 $
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.
29 #include <linux/string.h>
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"
42 static __u8 *bad_sector_map;
43 static SectorCount *bsm_hash_ptr;
50 /* fix_tape converts a normal QIC-80 tape into a 'wide' tape.
51 * For testing purposes only !
53 void fix_tape(__u8 * buffer, ft_format_type new_code)
55 static __u8 list[BAD_SECTOR_MAP_SIZE];
56 SectorMap *src_ptr = (SectorMap *) list;
57 __u8 *dst_ptr = bad_sector_map;
59 unsigned int sector = 1;
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)) {
67 if (map == EMPTY_SEGMENT) {
68 *(SectorMap *) dst_ptr = 0x800000 + sector;
70 sector += SECTORS_PER_SEGMENT;
72 for (i = 0; i < SECTORS_PER_SEGMENT; ++i) {
74 *(SewctorMap *) dst_ptr = sector;
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);
92 format_code = new_code;
97 /* given buffer that contains a header segment, find the end of
100 __u8 * ftape_find_end_of_bsm_list(__u8 * address)
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]) {
114 static inline void put_sector(SectorCount *ptr, unsigned int sector)
116 ptr->bytes[0] = sector & 0xff;
118 ptr->bytes[1] = sector & 0xff;
120 ptr->bytes[2] = sector & 0xff;
123 static inline unsigned int get_sector(SectorCount *ptr)
128 sector = ptr->bytes[0];
129 sector += ptr->bytes[1] << 8;
130 sector += ptr->bytes[2] << 16;
134 /* GET4 gets the next four bytes in Intel little endian order
135 * and converts them to host byte order and handles unaligned
138 return (GET4(ptr, 0) & 0x00ffffff); /* back to host byte order */
142 static void bsm_debug_fake(void)
144 /* for testing of bad sector handling at end of tape
147 ftape_put_bad_sector_entry(segments_per_track * tracks_per_tape - 3,
149 ftape_put_bad_sector_entry(segments_per_track * tracks_per_tape - 2,
151 ftape_put_bad_sector_entry(segments_per_track * tracks_per_tape - 1,
154 /* Enable to test bad sector handling
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);
167 /* Enable when testing multiple volume tar dumps.
173 for (i = ft_first_data_segment;
174 i <= ft_last_data_segment - 7; ++i) {
175 ftape_put_bad_sector_entry(i, EMPTY_SEGMENT);
179 /* Enable when testing bit positions in *_error_map
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)
194 static void print_bad_sector_map(void)
196 unsigned int good_sectors;
197 unsigned int total_bad = 0;
199 TRACE_FUN(ft_t_flow);
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;
208 while((sector = get_sector(ptr++)) != 0) {
209 if ((ft_format_code == fmt_big ||
210 ft_format_code == fmt_var) &&
212 total_bad += FT_SECTORS_PER_SEGMENT - 3;
213 TRACE(ft_t_noise, "bad segment at sector: %6d",
217 TRACE(ft_t_noise, "bad sector: %6d", sector);
220 /* Display old ftape's end-of-file marks
223 while ((sector = get_unaligned(ptr16++)) != 0) {
224 TRACE(ft_t_noise, "Old ftape eof mark: %4d/%2d",
225 sector, get_unaligned(ptr16++));
227 } else { /* fixed size format */
228 for (i = ft_first_data_segment;
229 i < (int)(ft_segments_per_track * ft_tracks_per_tape); ++i) {
230 SectorMap map = ((SectorMap *) bad_sector_map)[i];
234 "bsm for segment %4d: 0x%08x", i, (unsigned int)map);
235 total_bad += ((map == EMPTY_SEGMENT)
236 ? FT_SECTORS_PER_SEGMENT - 3
242 ((ft_segments_per_track * ft_tracks_per_tape - ft_first_data_segment)
243 * (FT_SECTORS_PER_SEGMENT - 3)) - total_bad;
244 TRACE(ft_t_info, "%d Kb usable on this tape", good_sectors);
245 if (total_bad == 0) {
247 "WARNING: this tape has no bad blocks registered !");
249 TRACE(ft_t_info, "%d bad sectors", total_bad);
255 void ftape_extract_bad_sector_map(__u8 * buffer)
259 /* Fill the bad sector map with the contents of buffer.
261 if (ft_format_code == fmt_var || ft_format_code == fmt_big) {
262 /* QIC-3010/3020 and wide QIC-80 tapes no longer have a failed
263 * sector log but use this area to extend the bad sector map.
265 bad_sector_map = &buffer[FT_HEADER_END];
267 /* non-wide QIC-80 tapes have a failed sector log area that
268 * mustn't be included in the bad sector map.
270 bad_sector_map = &buffer[FT_FSL + FT_FSL_SIZE];
272 if (ft_format_code == fmt_1100ft ||
273 ft_format_code == fmt_var ||
274 ft_format_code == fmt_big) {
275 bsm_hash_ptr = (SectorCount *)bad_sector_map;
280 if (TRACE_LEVEL >= ft_t_info) {
281 print_bad_sector_map();
286 static inline SectorMap cvt2map(unsigned int sector)
288 return 1 << (((sector & 0x7fffff) - 1) % FT_SECTORS_PER_SEGMENT);
291 static inline int cvt2segment(unsigned int sector)
293 return ((sector & 0x7fffff) - 1) / FT_SECTORS_PER_SEGMENT;
296 static int forward_seek_entry(int segment_id,
304 sector = get_sector((*ptr)++);
305 segment = cvt2segment(sector);
306 } while (sector != 0 && segment < segment_id);
307 (*ptr) --; /* point to first sector >= segment_id */
308 /* Get all sectors in segment_id
310 if (sector == 0 || segment != segment_id) {
313 } else if ((sector & 0x800000) &&
314 (ft_format_code == fmt_var || ft_format_code == fmt_big)) {
315 *map = EMPTY_SEGMENT;
316 return FT_SECTORS_PER_SEGMENT;
319 SectorCount *tmp_ptr = (*ptr) + 1;
321 *map = cvt2map(sector);
322 while ((sector = get_sector(tmp_ptr++)) != 0 &&
323 (segment = cvt2segment(sector)) == segment_id) {
324 *map |= cvt2map(sector);
331 static int backwards_seek_entry(int segment_id,
336 int segment; /* max unsigned int */
338 if (*ptr <= (SectorCount *)bad_sector_map) {
343 sector = get_sector(--(*ptr));
344 segment = cvt2segment(sector);
345 } while (*ptr > (SectorCount *)bad_sector_map && segment > segment_id);
346 if (segment > segment_id) { /* at start of list, no entry found */
349 } else if (segment < segment_id) {
350 /* before smaller entry, adjust for overshoot */
354 } else if ((sector & 0x800000) &&
355 (ft_format_code == fmt_big || ft_format_code == fmt_var)) {
356 *map = EMPTY_SEGMENT;
357 return FT_SECTORS_PER_SEGMENT;
358 } else { /* get all sectors in segment_id */
361 *map = cvt2map(sector);
362 while(*ptr > (SectorCount *)bad_sector_map) {
363 sector = get_sector(--(*ptr));
364 segment = cvt2segment(sector);
365 if (segment != segment_id) {
368 *map |= cvt2map(sector);
371 if (segment < segment_id) {
378 void ftape_put_bad_sector_entry(int segment_id, SectorMap new_map)
380 SectorCount *ptr = (SectorCount *)bad_sector_map;
386 if (ft_format_code == fmt_1100ft ||
387 ft_format_code == fmt_var ||
388 ft_format_code == fmt_big) {
389 count = forward_seek_entry(segment_id, &ptr, &map);
390 new_count = count_ones(new_map);
391 /* If format code == 4 put empty segment instead of 32
394 if (ft_format_code == fmt_var || ft_format_code == fmt_big) {
395 if (new_count == FT_SECTORS_PER_SEGMENT) {
398 if (count == FT_SECTORS_PER_SEGMENT) {
402 if (count != new_count) {
403 /* insert (or delete if < 0) new_count - count
404 * entries. Move trailing part of list
405 * including terminating 0.
407 SectorCount *hi_ptr = ptr;
410 } while (get_sector(hi_ptr++) != 0);
411 /* Note: ptr is of type byte *, and each bad sector
414 memmove(ptr + new_count, ptr + count,
415 (size_t)(hi_ptr - (ptr + count))*sizeof(SectorCount));
417 TRACE(ft_t_noise, "putting map 0x%08x at %p, segment %d",
418 (unsigned int)new_map, ptr, segment_id);
419 if (new_count == 1 && new_map == EMPTY_SEGMENT) {
420 put_sector(ptr++, (0x800001 +
422 FT_SECTORS_PER_SEGMENT));
430 FT_SECTORS_PER_SEGMENT + i);
437 ((SectorMap *) bad_sector_map)[segment_id] = new_map;
442 SectorMap ftape_get_bad_sector_entry(int segment_id)
444 if (ft_used_header_segment == -1) {
445 /* When reading header segment we'll need a blank map.
448 } else if (bsm_hash_ptr != NULL) {
450 * map - mask value returned on last call.
451 * bsm_hash_ptr - points to first sector greater or equal to
452 * first sector in last_referenced segment.
453 * last_referenced - segment id used in the last call,
454 * sector and map belong to this id.
455 * This code is designed for sequential access and retries.
456 * For true random access it may have to be redesigned.
458 static int last_reference = -1;
459 static SectorMap map;
461 if (segment_id > last_reference) {
462 /* Skip all sectors before segment_id
464 forward_seek_entry(segment_id, &bsm_hash_ptr, &map);
465 } else if (segment_id < last_reference) {
466 /* Skip backwards until begin of buffer or
467 * first sector in segment_id
469 backwards_seek_entry(segment_id, &bsm_hash_ptr, &map);
470 } /* segment_id == last_reference : keep map */
471 last_reference = segment_id;
474 return ((SectorMap *) bad_sector_map)[segment_id];
478 /* This is simply here to prevent us from overwriting other kernel
479 * data. Writes will result in NULL Pointer dereference.
481 void ftape_init_bsm(void)
483 bad_sector_map = NULL;