File: /usr/src/linux/drivers/char/ftape/lowlevel/ftape-bsm.c

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      */
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     
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     
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     
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     
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     
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     
253     void 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     
284     static inline SectorMap cvt2map(unsigned int sector)
285     {
286     	return 1 << (((sector & 0x7fffff) - 1) % FT_SECTORS_PER_SEGMENT);
287     }
288     
289     static inline int cvt2segment(unsigned int sector)
290     {
291     	return ((sector & 0x7fffff) - 1) / FT_SECTORS_PER_SEGMENT;
292     }
293     
294     static 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     
329     static 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     
376     void 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     
440     SectorMap 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      */
479     void ftape_init_bsm(void)
480     {
481     	bad_sector_map = NULL;
482     	bsm_hash_ptr   = NULL;
483     }
484