blob: d1a301cc344f4d5503b33b28979716253342c952 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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
50static void ftape_put_bad_sector_entry(int segment_id, SectorMap new_map);
51#endif
52
53#if 0
54/* fix_tape converts a normal QIC-80 tape into a 'wide' tape.
55 * For testing purposes only !
56 */
57void fix_tape(__u8 * buffer, ft_format_type new_code)
58{
59 static __u8 list[BAD_SECTOR_MAP_SIZE];
60 SectorMap *src_ptr = (SectorMap *) list;
61 __u8 *dst_ptr = bad_sector_map;
62 SectorMap map;
63 unsigned int sector = 1;
64 int i;
65
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)) {
70 map = *src_ptr++;
71 if (map == EMPTY_SEGMENT) {
72 *(SectorMap *) dst_ptr = 0x800000 + sector;
73 dst_ptr += 3;
74 sector += SECTORS_PER_SEGMENT;
75 } else {
76 for (i = 0; i < SECTORS_PER_SEGMENT; ++i) {
77 if (map & 1) {
78 *(SewctorMap *) dst_ptr = sector;
79 dst_ptr += 3;
80 }
81 map >>= 1;
82 ++sector;
83 }
84 }
85 }
86 }
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);
95 }
96 format_code = new_code;
97}
98
99#endif
100
101/* given buffer that contains a header segment, find the end of
102 * of the bsm list
103 */
104__u8 * ftape_find_end_of_bsm_list(__u8 * address)
105{
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]) {
110 ptr += 3;
111 } else {
112 return ptr;
113 }
114 }
115 return NULL;
116}
117
118static inline void put_sector(SectorCount *ptr, unsigned int sector)
119{
120 ptr->bytes[0] = sector & 0xff;
121 sector >>= 8;
122 ptr->bytes[1] = sector & 0xff;
123 sector >>= 8;
124 ptr->bytes[2] = sector & 0xff;
125}
126
127static inline unsigned int get_sector(SectorCount *ptr)
128{
129#if 1
130 unsigned int sector;
131
132 sector = ptr->bytes[0];
133 sector += ptr->bytes[1] << 8;
134 sector += ptr->bytes[2] << 16;
135
136 return sector;
137#else
138 /* GET4 gets the next four bytes in Intel little endian order
139 * and converts them to host byte order and handles unaligned
140 * access.
141 */
142 return (GET4(ptr, 0) & 0x00ffffff); /* back to host byte order */
143#endif
144}
145
146static void bsm_debug_fake(void)
147{
148 /* for testing of bad sector handling at end of tape
149 */
150#if 0
151 ftape_put_bad_sector_entry(segments_per_track * tracks_per_tape - 3,
152 0x000003e0;
153 ftape_put_bad_sector_entry(segments_per_track * tracks_per_tape - 2,
154 0xff3fffff;
155 ftape_put_bad_sector_entry(segments_per_track * tracks_per_tape - 1,
156 0xffffe000;
157#endif
158 /* Enable to test bad sector handling
159 */
160#if 0
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);
170#endif
171 /* Enable when testing multiple volume tar dumps.
172 */
173#if 0
174 {
175 int i;
176
177 for (i = ft_first_data_segment;
178 i <= ft_last_data_segment - 7; ++i) {
179 ftape_put_bad_sector_entry(i, EMPTY_SEGMENT);
180 }
181 }
182#endif
183 /* Enable when testing bit positions in *_error_map
184 */
185#if 0
186 {
187 int i;
188
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)
192 | 0x00ff00ff);
193 }
194 }
195#endif
196}
197
198static void print_bad_sector_map(void)
199{
200 unsigned int good_sectors;
201 unsigned int total_bad = 0;
202 int i;
203 TRACE_FUN(ft_t_flow);
204
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;
209 unsigned int sector;
210 __u16 *ptr16;
211
212 while((sector = get_sector(ptr++)) != 0) {
213 if ((ft_format_code == fmt_big ||
214 ft_format_code == fmt_var) &&
215 sector & 0x800000) {
216 total_bad += FT_SECTORS_PER_SEGMENT - 3;
217 TRACE(ft_t_noise, "bad segment at sector: %6d",
218 sector & 0x7fffff);
219 } else {
220 ++total_bad;
221 TRACE(ft_t_noise, "bad sector: %6d", sector);
222 }
223 }
224 /* Display old ftape's end-of-file marks
225 */
226 ptr16 = (__u16*)ptr;
227 while ((sector = get_unaligned(ptr16++)) != 0) {
228 TRACE(ft_t_noise, "Old ftape eof mark: %4d/%2d",
229 sector, get_unaligned(ptr16++));
230 }
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];
235
236 if (map) {
237 TRACE(ft_t_noise,
238 "bsm for segment %4d: 0x%08x", i, (unsigned int)map);
239 total_bad += ((map == EMPTY_SEGMENT)
240 ? FT_SECTORS_PER_SEGMENT - 3
241 : count_ones(map));
242 }
243 }
244 }
245 good_sectors =
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) {
250 TRACE(ft_t_info,
251 "WARNING: this tape has no bad blocks registered !");
252 } else {
253 TRACE(ft_t_info, "%d bad sectors", total_bad);
254 }
255 TRACE_EXIT;
256}
257
258
259void ftape_extract_bad_sector_map(__u8 * buffer)
260{
261 TRACE_FUN(ft_t_any);
262
263 /* Fill the bad sector map with the contents of buffer.
264 */
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.
268 */
269 bad_sector_map = &buffer[FT_HEADER_END];
270 } else {
271 /* non-wide QIC-80 tapes have a failed sector log area that
272 * mustn't be included in the bad sector map.
273 */
274 bad_sector_map = &buffer[FT_FSL + FT_FSL_SIZE];
275 }
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;
280 } else {
281 bsm_hash_ptr = NULL;
282 }
283 bsm_debug_fake();
284 if (TRACE_LEVEL >= ft_t_info) {
285 print_bad_sector_map();
286 }
287 TRACE_EXIT;
288}
289
290static inline SectorMap cvt2map(unsigned int sector)
291{
292 return 1 << (((sector & 0x7fffff) - 1) % FT_SECTORS_PER_SEGMENT);
293}
294
295static inline int cvt2segment(unsigned int sector)
296{
297 return ((sector & 0x7fffff) - 1) / FT_SECTORS_PER_SEGMENT;
298}
299
300static int forward_seek_entry(int segment_id,
301 SectorCount **ptr,
302 SectorMap *map)
303{
304 unsigned int sector;
305 int segment;
306
307 do {
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
313 */
314 if (sector == 0 || segment != segment_id) {
315 *map = 0;
316 return 0;
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;
321 } else {
322 int count = 1;
323 SectorCount *tmp_ptr = (*ptr) + 1;
324
325 *map = cvt2map(sector);
326 while ((sector = get_sector(tmp_ptr++)) != 0 &&
327 (segment = cvt2segment(sector)) == segment_id) {
328 *map |= cvt2map(sector);
329 ++count;
330 }
331 return count;
332 }
333}
334
335static int backwards_seek_entry(int segment_id,
336 SectorCount **ptr,
337 SectorMap *map)
338{
339 unsigned int sector;
340 int segment; /* max unsigned int */
341
342 if (*ptr <= (SectorCount *)bad_sector_map) {
343 *map = 0;
344 return 0;
345 }
346 do {
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 */
351 *map = 0;
352 return 0;
353 } else if (segment < segment_id) {
354 /* before smaller entry, adjust for overshoot */
355 (*ptr) ++;
356 *map = 0;
357 return 0;
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 */
363 int count = 1;
364
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) {
370 break;
371 }
372 *map |= cvt2map(sector);
373 ++count;
374 }
375 if (segment < segment_id) {
376 (*ptr) ++;
377 }
378 return count;
379 }
380}
381
382#if 0
383static void ftape_put_bad_sector_entry(int segment_id, SectorMap new_map)
384{
385 SectorCount *ptr = (SectorCount *)bad_sector_map;
386 int count;
387 int new_count;
388 SectorMap map;
389 TRACE_FUN(ft_t_any);
390
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
397 * bad sectors.
398 */
399 if (ft_format_code == fmt_var || ft_format_code == fmt_big) {
400 if (new_count == FT_SECTORS_PER_SEGMENT) {
401 new_count = 1;
402 }
403 if (count == FT_SECTORS_PER_SEGMENT) {
404 count = 1;
405 }
406 }
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.
411 */
412 SectorCount *hi_ptr = ptr;
413
414 do {
415 } while (get_sector(hi_ptr++) != 0);
416 /* Note: ptr is of type byte *, and each bad sector
417 * consumes 3 bytes.
418 */
419 memmove(ptr + new_count, ptr + count,
420 (size_t)(hi_ptr - (ptr + count))*sizeof(SectorCount));
421 }
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 +
426 segment_id *
427 FT_SECTORS_PER_SEGMENT));
428 } else {
429 int i = 0;
430
431 while (new_map) {
432 if (new_map & 1) {
433 put_sector(ptr++,
434 1 + segment_id *
435 FT_SECTORS_PER_SEGMENT + i);
436 }
437 ++i;
438 new_map >>= 1;
439 }
440 }
441 } else {
442 ((SectorMap *) bad_sector_map)[segment_id] = new_map;
443 }
444 TRACE_EXIT;
445}
446#endif /* 0 */
447
448SectorMap ftape_get_bad_sector_entry(int segment_id)
449{
450 if (ft_used_header_segment == -1) {
451 /* When reading header segment we'll need a blank map.
452 */
453 return 0;
454 } else if (bsm_hash_ptr != NULL) {
455 /* Invariants:
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.
463 */
464 static int last_reference = -1;
465 static SectorMap map;
466
467 if (segment_id > last_reference) {
468 /* Skip all sectors before segment_id
469 */
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
474 */
475 backwards_seek_entry(segment_id, &bsm_hash_ptr, &map);
476 } /* segment_id == last_reference : keep map */
477 last_reference = segment_id;
478 return map;
479 } else {
480 return ((SectorMap *) bad_sector_map)[segment_id];
481 }
482}
483
484/* This is simply here to prevent us from overwriting other kernel
485 * data. Writes will result in NULL Pointer dereference.
486 */
487void ftape_init_bsm(void)
488{
489 bad_sector_map = NULL;
490 bsm_hash_ptr = NULL;
491}