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 */
42 static __u8 *bad_sector_map;
43 static SectorCount *bsm_hash_ptr;
44
45 typedef 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 */
53 void 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 */
ftape_find_end_of_bsm_list(__u8 * address)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
put_sector(SectorCount * ptr,unsigned int sector)114 static 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
get_sector(SectorCount * ptr)123 static 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
bsm_debug_fake(void)142 static 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
print_bad_sector_map(void)194 static 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 __u16 *ptr16;
207
208 while((sector = get_sector(ptr++)) != 0) {
209 if ((ft_format_code == fmt_big ||
210 ft_format_code == fmt_var) &&
211 sector & 0x800000) {
212 total_bad += FT_SECTORS_PER_SEGMENT - 3;
213 TRACE(ft_t_noise, "bad segment at sector: %6d",
214 sector & 0x7fffff);
215 } else {
216 ++total_bad;
217 TRACE(ft_t_noise, "bad sector: %6d", sector);
218 }
219 }
220 /* Display old ftape's end-of-file marks
221 */
222 ptr16 = (__u16*)ptr;
223 while ((sector = get_unaligned(ptr16++)) != 0) {
224 TRACE(ft_t_noise, "Old ftape eof mark: %4d/%2d",
225 sector, get_unaligned(ptr16++));
226 }
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];
231
232 if (map) {
233 TRACE(ft_t_noise,
234 "bsm for segment %4d: 0x%08x", i, (unsigned int)map);
235 total_bad += ((map == EMPTY_SEGMENT)
236 ? FT_SECTORS_PER_SEGMENT - 3
237 : count_ones(map));
238 }
239 }
240 }
241 good_sectors =
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) {
246 TRACE(ft_t_info,
247 "WARNING: this tape has no bad blocks registered !");
248 } else {
249 TRACE(ft_t_info, "%d bad sectors", total_bad);
250 }
251 TRACE_EXIT;
252 }
253
254
ftape_extract_bad_sector_map(__u8 * buffer)255 void ftape_extract_bad_sector_map(__u8 * buffer)
256 {
257 TRACE_FUN(ft_t_any);
258
259 /* Fill the bad sector map with the contents of buffer.
260 */
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.
264 */
265 bad_sector_map = &buffer[FT_HEADER_END];
266 } else {
267 /* non-wide QIC-80 tapes have a failed sector log area that
268 * mustn't be included in the bad sector map.
269 */
270 bad_sector_map = &buffer[FT_FSL + FT_FSL_SIZE];
271 }
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;
276 } else {
277 bsm_hash_ptr = NULL;
278 }
279 bsm_debug_fake();
280 if (TRACE_LEVEL >= ft_t_info) {
281 print_bad_sector_map();
282 }
283 TRACE_EXIT;
284 }
285
cvt2map(unsigned int sector)286 static inline SectorMap cvt2map(unsigned int sector)
287 {
288 return 1 << (((sector & 0x7fffff) - 1) % FT_SECTORS_PER_SEGMENT);
289 }
290
cvt2segment(unsigned int sector)291 static inline int cvt2segment(unsigned int sector)
292 {
293 return ((sector & 0x7fffff) - 1) / FT_SECTORS_PER_SEGMENT;
294 }
295
forward_seek_entry(int segment_id,SectorCount ** ptr,SectorMap * map)296 static int forward_seek_entry(int segment_id,
297 SectorCount **ptr,
298 SectorMap *map)
299 {
300 unsigned int sector;
301 int segment;
302
303 do {
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
309 */
310 if (sector == 0 || segment != segment_id) {
311 *map = 0;
312 return 0;
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;
317 } else {
318 int count = 1;
319 SectorCount *tmp_ptr = (*ptr) + 1;
320
321 *map = cvt2map(sector);
322 while ((sector = get_sector(tmp_ptr++)) != 0 &&
323 (segment = cvt2segment(sector)) == segment_id) {
324 *map |= cvt2map(sector);
325 ++count;
326 }
327 return count;
328 }
329 }
330
backwards_seek_entry(int segment_id,SectorCount ** ptr,SectorMap * map)331 static int backwards_seek_entry(int segment_id,
332 SectorCount **ptr,
333 SectorMap *map)
334 {
335 unsigned int sector;
336 int segment; /* max unsigned int */
337
338 if (*ptr <= (SectorCount *)bad_sector_map) {
339 *map = 0;
340 return 0;
341 }
342 do {
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 */
347 *map = 0;
348 return 0;
349 } else if (segment < segment_id) {
350 /* before smaller entry, adjust for overshoot */
351 (*ptr) ++;
352 *map = 0;
353 return 0;
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 */
359 int count = 1;
360
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) {
366 break;
367 }
368 *map |= cvt2map(sector);
369 ++count;
370 }
371 if (segment < segment_id) {
372 (*ptr) ++;
373 }
374 return count;
375 }
376 }
377
ftape_put_bad_sector_entry(int segment_id,SectorMap new_map)378 void ftape_put_bad_sector_entry(int segment_id, SectorMap new_map)
379 {
380 SectorCount *ptr = (SectorCount *)bad_sector_map;
381 int count;
382 int new_count;
383 SectorMap map;
384 TRACE_FUN(ft_t_any);
385
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
392 * bad sectors.
393 */
394 if (ft_format_code == fmt_var || ft_format_code == fmt_big) {
395 if (new_count == FT_SECTORS_PER_SEGMENT) {
396 new_count = 1;
397 }
398 if (count == FT_SECTORS_PER_SEGMENT) {
399 count = 1;
400 }
401 }
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.
406 */
407 SectorCount *hi_ptr = ptr;
408
409 do {
410 } while (get_sector(hi_ptr++) != 0);
411 /* Note: ptr is of type byte *, and each bad sector
412 * consumes 3 bytes.
413 */
414 memmove(ptr + new_count, ptr + count,
415 (size_t)(hi_ptr - (ptr + count))*sizeof(SectorCount));
416 }
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 +
421 segment_id *
422 FT_SECTORS_PER_SEGMENT));
423 } else {
424 int i = 0;
425
426 while (new_map) {
427 if (new_map & 1) {
428 put_sector(ptr++,
429 1 + segment_id *
430 FT_SECTORS_PER_SEGMENT + i);
431 }
432 ++i;
433 new_map >>= 1;
434 }
435 }
436 } else {
437 ((SectorMap *) bad_sector_map)[segment_id] = new_map;
438 }
439 TRACE_EXIT;
440 }
441
ftape_get_bad_sector_entry(int segment_id)442 SectorMap ftape_get_bad_sector_entry(int segment_id)
443 {
444 if (ft_used_header_segment == -1) {
445 /* When reading header segment we'll need a blank map.
446 */
447 return 0;
448 } else if (bsm_hash_ptr != NULL) {
449 /* Invariants:
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.
457 */
458 static int last_reference = -1;
459 static SectorMap map;
460
461 if (segment_id > last_reference) {
462 /* Skip all sectors before segment_id
463 */
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
468 */
469 backwards_seek_entry(segment_id, &bsm_hash_ptr, &map);
470 } /* segment_id == last_reference : keep map */
471 last_reference = segment_id;
472 return map;
473 } else {
474 return ((SectorMap *) bad_sector_map)[segment_id];
475 }
476 }
477
478 /* This is simply here to prevent us from overwriting other kernel
479 * data. Writes will result in NULL Pointer dereference.
480 */
ftape_init_bsm(void)481 void ftape_init_bsm(void)
482 {
483 bad_sector_map = NULL;
484 bsm_hash_ptr = NULL;
485 }
486