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 static void ftape_put_bad_sector_entry(int segment_id
, SectorMap new_map
);
54 /* fix_tape converts a normal QIC-80 tape into a 'wide' tape.
55 * For testing purposes only !
57 void fix_tape(__u8
* buffer
, ft_format_type new_code
)
59 static __u8 list
[BAD_SECTOR_MAP_SIZE
];
60 SectorMap
*src_ptr
= (SectorMap
*) list
;
61 __u8
*dst_ptr
= bad_sector_map
;
63 unsigned int sector
= 1;
66 if (format_code
!= fmt_var
&& format_code
!= fmt_big
) {
67 memcpy(list
, bad_sector_map
, sizeof(list
));
68 memset(bad_sector_map
, 0, sizeof(bad_sector_map
));
69 while ((__u8
*) src_ptr
- list
< sizeof(list
)) {
71 if (map
== EMPTY_SEGMENT
) {
72 *(SectorMap
*) dst_ptr
= 0x800000 + sector
;
74 sector
+= SECTORS_PER_SEGMENT
;
76 for (i
= 0; i
< SECTORS_PER_SEGMENT
; ++i
) {
78 *(SewctorMap
*) dst_ptr
= sector
;
87 bad_sector_map_changed
= 1;
88 *(buffer
+ 4) = new_code
; /* put new format code */
89 if (format_code
!= fmt_var
&& new_code
== fmt_big
) {
90 PUT4(buffer
, FT_6_HSEG_1
, (__u32
)GET2(buffer
, 6));
91 PUT4(buffer
, FT_6_HSEG_2
, (__u32
)GET2(buffer
, 8));
92 PUT4(buffer
, FT_6_FRST_SEG
, (__u32
)GET2(buffer
, 10));
93 PUT4(buffer
, FT_6_LAST_SEG
, (__u32
)GET2(buffer
, 12));
94 memset(buffer
+6, '\0', 8);
96 format_code
= new_code
;
101 /* given buffer that contains a header segment, find the end of
104 __u8
* ftape_find_end_of_bsm_list(__u8
* address
)
106 __u8
*ptr
= address
+ FT_HEADER_END
; /* start of bsm list */
107 __u8
*limit
= address
+ FT_SEGMENT_SIZE
;
108 while (ptr
+ 2 < limit
) {
109 if (ptr
[0] || ptr
[1] || ptr
[2]) {
118 static inline void put_sector(SectorCount
*ptr
, unsigned int sector
)
120 ptr
->bytes
[0] = sector
& 0xff;
122 ptr
->bytes
[1] = sector
& 0xff;
124 ptr
->bytes
[2] = sector
& 0xff;
127 static inline unsigned int get_sector(SectorCount
*ptr
)
132 sector
= ptr
->bytes
[0];
133 sector
+= ptr
->bytes
[1] << 8;
134 sector
+= ptr
->bytes
[2] << 16;
138 /* GET4 gets the next four bytes in Intel little endian order
139 * and converts them to host byte order and handles unaligned
142 return (GET4(ptr
, 0) & 0x00ffffff); /* back to host byte order */
146 static void bsm_debug_fake(void)
148 /* for testing of bad sector handling at end of tape
151 ftape_put_bad_sector_entry(segments_per_track
* tracks_per_tape
- 3,
153 ftape_put_bad_sector_entry(segments_per_track
* tracks_per_tape
- 2,
155 ftape_put_bad_sector_entry(segments_per_track
* tracks_per_tape
- 1,
158 /* Enable to test bad sector handling
161 ftape_put_bad_sector_entry(30, 0xfffffffe)
162 ftape_put_bad_sector_entry(32, 0x7fffffff);
163 ftape_put_bad_sector_entry(34, 0xfffeffff);
164 ftape_put_bad_sector_entry(36, 0x55555555);
165 ftape_put_bad_sector_entry(38, 0xffffffff);
166 ftape_put_bad_sector_entry(50, 0xffff0000);
167 ftape_put_bad_sector_entry(51, 0xffffffff);
168 ftape_put_bad_sector_entry(52, 0xffffffff);
169 ftape_put_bad_sector_entry(53, 0x0000ffff);
171 /* Enable when testing multiple volume tar dumps.
177 for (i
= ft_first_data_segment
;
178 i
<= ft_last_data_segment
- 7; ++i
) {
179 ftape_put_bad_sector_entry(i
, EMPTY_SEGMENT
);
183 /* Enable when testing bit positions in *_error_map
189 for (i
= first_data_segment
; i
<= last_data_segment
; ++i
) {
190 ftape_put_bad_sector_entry(i
,
191 ftape_get_bad_sector_entry(i
)
198 static void print_bad_sector_map(void)
200 unsigned int good_sectors
;
201 unsigned int total_bad
= 0;
203 TRACE_FUN(ft_t_flow
);
205 if (ft_format_code
== fmt_big
||
206 ft_format_code
== fmt_var
||
207 ft_format_code
== fmt_1100ft
) {
208 SectorCount
*ptr
= (SectorCount
*)bad_sector_map
;
212 while((sector
= get_sector(ptr
++)) != 0) {
213 if ((ft_format_code
== fmt_big
||
214 ft_format_code
== fmt_var
) &&
216 total_bad
+= FT_SECTORS_PER_SEGMENT
- 3;
217 TRACE(ft_t_noise
, "bad segment at sector: %6d",
221 TRACE(ft_t_noise
, "bad sector: %6d", sector
);
224 /* Display old ftape's end-of-file marks
227 while ((sector
= get_unaligned(ptr16
++)) != 0) {
228 TRACE(ft_t_noise
, "Old ftape eof mark: %4d/%2d",
229 sector
, get_unaligned(ptr16
++));
231 } else { /* fixed size format */
232 for (i
= ft_first_data_segment
;
233 i
< (int)(ft_segments_per_track
* ft_tracks_per_tape
); ++i
) {
234 SectorMap map
= ((SectorMap
*) bad_sector_map
)[i
];
238 "bsm for segment %4d: 0x%08x", i
, (unsigned int)map
);
239 total_bad
+= ((map
== EMPTY_SEGMENT
)
240 ? FT_SECTORS_PER_SEGMENT
- 3
246 ((ft_segments_per_track
* ft_tracks_per_tape
- ft_first_data_segment
)
247 * (FT_SECTORS_PER_SEGMENT
- 3)) - total_bad
;
248 TRACE(ft_t_info
, "%d Kb usable on this tape", good_sectors
);
249 if (total_bad
== 0) {
251 "WARNING: this tape has no bad blocks registered !");
253 TRACE(ft_t_info
, "%d bad sectors", total_bad
);
259 void ftape_extract_bad_sector_map(__u8
* buffer
)
263 /* Fill the bad sector map with the contents of buffer.
265 if (ft_format_code
== fmt_var
|| ft_format_code
== fmt_big
) {
266 /* QIC-3010/3020 and wide QIC-80 tapes no longer have a failed
267 * sector log but use this area to extend the bad sector map.
269 bad_sector_map
= &buffer
[FT_HEADER_END
];
271 /* non-wide QIC-80 tapes have a failed sector log area that
272 * mustn't be included in the bad sector map.
274 bad_sector_map
= &buffer
[FT_FSL
+ FT_FSL_SIZE
];
276 if (ft_format_code
== fmt_1100ft
||
277 ft_format_code
== fmt_var
||
278 ft_format_code
== fmt_big
) {
279 bsm_hash_ptr
= (SectorCount
*)bad_sector_map
;
284 if (TRACE_LEVEL
>= ft_t_info
) {
285 print_bad_sector_map();
290 static inline SectorMap
cvt2map(unsigned int sector
)
292 return 1 << (((sector
& 0x7fffff) - 1) % FT_SECTORS_PER_SEGMENT
);
295 static inline int cvt2segment(unsigned int sector
)
297 return ((sector
& 0x7fffff) - 1) / FT_SECTORS_PER_SEGMENT
;
300 static int forward_seek_entry(int segment_id
,
308 sector
= get_sector((*ptr
)++);
309 segment
= cvt2segment(sector
);
310 } while (sector
!= 0 && segment
< segment_id
);
311 (*ptr
) --; /* point to first sector >= segment_id */
312 /* Get all sectors in segment_id
314 if (sector
== 0 || segment
!= segment_id
) {
317 } else if ((sector
& 0x800000) &&
318 (ft_format_code
== fmt_var
|| ft_format_code
== fmt_big
)) {
319 *map
= EMPTY_SEGMENT
;
320 return FT_SECTORS_PER_SEGMENT
;
323 SectorCount
*tmp_ptr
= (*ptr
) + 1;
325 *map
= cvt2map(sector
);
326 while ((sector
= get_sector(tmp_ptr
++)) != 0 &&
327 (segment
= cvt2segment(sector
)) == segment_id
) {
328 *map
|= cvt2map(sector
);
335 static int backwards_seek_entry(int segment_id
,
340 int segment
; /* max unsigned int */
342 if (*ptr
<= (SectorCount
*)bad_sector_map
) {
347 sector
= get_sector(--(*ptr
));
348 segment
= cvt2segment(sector
);
349 } while (*ptr
> (SectorCount
*)bad_sector_map
&& segment
> segment_id
);
350 if (segment
> segment_id
) { /* at start of list, no entry found */
353 } else if (segment
< segment_id
) {
354 /* before smaller entry, adjust for overshoot */
358 } else if ((sector
& 0x800000) &&
359 (ft_format_code
== fmt_big
|| ft_format_code
== fmt_var
)) {
360 *map
= EMPTY_SEGMENT
;
361 return FT_SECTORS_PER_SEGMENT
;
362 } else { /* get all sectors in segment_id */
365 *map
= cvt2map(sector
);
366 while(*ptr
> (SectorCount
*)bad_sector_map
) {
367 sector
= get_sector(--(*ptr
));
368 segment
= cvt2segment(sector
);
369 if (segment
!= segment_id
) {
372 *map
|= cvt2map(sector
);
375 if (segment
< segment_id
) {
383 static void ftape_put_bad_sector_entry(int segment_id
, SectorMap new_map
)
385 SectorCount
*ptr
= (SectorCount
*)bad_sector_map
;
391 if (ft_format_code
== fmt_1100ft
||
392 ft_format_code
== fmt_var
||
393 ft_format_code
== fmt_big
) {
394 count
= forward_seek_entry(segment_id
, &ptr
, &map
);
395 new_count
= count_ones(new_map
);
396 /* If format code == 4 put empty segment instead of 32
399 if (ft_format_code
== fmt_var
|| ft_format_code
== fmt_big
) {
400 if (new_count
== FT_SECTORS_PER_SEGMENT
) {
403 if (count
== FT_SECTORS_PER_SEGMENT
) {
407 if (count
!= new_count
) {
408 /* insert (or delete if < 0) new_count - count
409 * entries. Move trailing part of list
410 * including terminating 0.
412 SectorCount
*hi_ptr
= ptr
;
415 } while (get_sector(hi_ptr
++) != 0);
416 /* Note: ptr is of type byte *, and each bad sector
419 memmove(ptr
+ new_count
, ptr
+ count
,
420 (size_t)(hi_ptr
- (ptr
+ count
))*sizeof(SectorCount
));
422 TRACE(ft_t_noise
, "putting map 0x%08x at %p, segment %d",
423 (unsigned int)new_map
, ptr
, segment_id
);
424 if (new_count
== 1 && new_map
== EMPTY_SEGMENT
) {
425 put_sector(ptr
++, (0x800001 +
427 FT_SECTORS_PER_SEGMENT
));
435 FT_SECTORS_PER_SEGMENT
+ i
);
442 ((SectorMap
*) bad_sector_map
)[segment_id
] = new_map
;
448 SectorMap
ftape_get_bad_sector_entry(int segment_id
)
450 if (ft_used_header_segment
== -1) {
451 /* When reading header segment we'll need a blank map.
454 } else if (bsm_hash_ptr
!= NULL
) {
456 * map - mask value returned on last call.
457 * bsm_hash_ptr - points to first sector greater or equal to
458 * first sector in last_referenced segment.
459 * last_referenced - segment id used in the last call,
460 * sector and map belong to this id.
461 * This code is designed for sequential access and retries.
462 * For true random access it may have to be redesigned.
464 static int last_reference
= -1;
465 static SectorMap map
;
467 if (segment_id
> last_reference
) {
468 /* Skip all sectors before segment_id
470 forward_seek_entry(segment_id
, &bsm_hash_ptr
, &map
);
471 } else if (segment_id
< last_reference
) {
472 /* Skip backwards until begin of buffer or
473 * first sector in segment_id
475 backwards_seek_entry(segment_id
, &bsm_hash_ptr
, &map
);
476 } /* segment_id == last_reference : keep map */
477 last_reference
= segment_id
;
480 return ((SectorMap
*) bad_sector_map
)[segment_id
];
484 /* This is simply here to prevent us from overwriting other kernel
485 * data. Writes will result in NULL Pointer dereference.
487 void ftape_init_bsm(void)
489 bad_sector_map
= NULL
;