patch-2.1.66 linux/drivers/char/ftape/lowlevel/ftape-bsm.c

Next file: linux/drivers/char/ftape/lowlevel/ftape-bsm.h
Previous file: linux/drivers/char/ftape/lowlevel/fdc-isr.h
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-bsm.c linux/drivers/char/ftape/lowlevel/ftape-bsm.c
@@ -0,0 +1,490 @@
+/*
+ *      Copyright (C) 1994-1996 Bas Laarhoven,
+ *                (C) 1996-1997 Claus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-bsm.c,v $
+ * $Revision: 1.3 $
+ * $Date: 1997/10/05 19:15:15 $
+ *
+ *      This file contains the bad-sector map handling code for
+ *      the QIC-117 floppy tape driver for Linux.
+ *      QIC-40, QIC-80, QIC-3010 and QIC-3020 maps are implemented.
+ */
+
+#include <linux/string.h>
+
+#include <linux/ftape.h>
+#include "../lowlevel/ftape-tracing.h"
+#include "../lowlevel/ftape-bsm.h"
+#include "../lowlevel/ftape-ctl.h"
+#include "../lowlevel/ftape-rw.h"
+
+/*      Global vars.
+ */
+
+/*      Local vars.
+ */
+static __u8 *bad_sector_map = NULL;
+static SectorCount *bsm_hash_ptr = NULL; 
+
+typedef enum {
+	forward, backward
+} mode_type;
+
+#if 0
+/*  fix_tape converts a normal QIC-80 tape into a 'wide' tape.
+ *  For testing purposes only !
+ */
+void fix_tape(__u8 * buffer, ft_format_type new_code)
+{
+	static __u8 list[BAD_SECTOR_MAP_SIZE];
+	SectorMap *src_ptr = (SectorMap *) list;
+	__u8 *dst_ptr = bad_sector_map;
+	SectorMap map;
+	unsigned int sector = 1;
+	int i;
+
+	if (format_code != fmt_var && format_code != fmt_big) {
+		memcpy(list, bad_sector_map, sizeof(list));
+		memset(bad_sector_map, 0, sizeof(bad_sector_map));
+		while ((__u8 *) src_ptr - list < sizeof(list)) {
+			map = *src_ptr++;
+			if (map == EMPTY_SEGMENT) {
+				*(SectorMap *) dst_ptr = 0x800000 + sector;
+				dst_ptr += 3;
+				sector += SECTORS_PER_SEGMENT;
+			} else {
+				for (i = 0; i < SECTORS_PER_SEGMENT; ++i) {
+					if (map & 1) {
+						*(SewctorMap *) dst_ptr = sector;
+						dst_ptr += 3;
+					}
+					map >>= 1;
+					++sector;
+				}
+			}
+		}
+	}
+	bad_sector_map_changed = 1;
+	*(buffer + 4) = new_code;	/* put new format code */
+	if (format_code != fmt_var && new_code == fmt_big) {
+		PUT4(buffer, FT_6_HSEG_1,   (__u32)GET2(buffer, 6));
+		PUT4(buffer, FT_6_HSEG_2,   (__u32)GET2(buffer, 8));
+		PUT4(buffer, FT_6_FRST_SEG, (__u32)GET2(buffer, 10));
+		PUT4(buffer, FT_6_LAST_SEG, (__u32)GET2(buffer, 12));
+		memset(buffer+6, '\0', 8);
+	}
+	format_code = new_code;
+}
+
+#endif
+
+/*   given buffer that contains a header segment, find the end of
+ *   of the bsm list
+ */
+__u8 * ftape_find_end_of_bsm_list(__u8 * address)
+{
+	__u8 *ptr   = address + FT_HEADER_END; /* start of bsm list */
+	__u8 *limit = address + FT_SEGMENT_SIZE;
+	while (ptr + 2 < limit) {
+		if (ptr[0] || ptr[1] || ptr[2]) {
+			ptr += 3;
+		} else {
+			return ptr;
+		}
+	}
+	return NULL;
+}
+
+static inline void put_sector(SectorCount *ptr, unsigned int sector)
+{
+	ptr->bytes[0] = sector & 0xff;
+	sector >>= 8;
+	ptr->bytes[1] = sector & 0xff;
+	sector >>= 8;
+	ptr->bytes[2] = sector & 0xff;
+}
+
+static inline unsigned int get_sector(SectorCount *ptr)
+{
+#if 1
+	unsigned int sector;
+
+	sector  = ptr->bytes[0];
+	sector += ptr->bytes[1] <<  8;
+	sector += ptr->bytes[2] << 16;
+
+	return sector;
+#else
+	/*  GET4 gets the next four bytes in Intel little endian order
+	 *  and converts them to host byte order and handles unaligned
+	 *  access.
+	 */
+	return (GET4(ptr, 0) & 0x00ffffff); /* back to host byte order */
+#endif
+}
+
+static void bsm_debug_fake(void)
+{
+	/* for testing of bad sector handling at end of tape
+	 */
+#if 0
+	ftape_put_bad_sector_entry(segments_per_track * tracks_per_tape - 3,
+				   0x000003e0;
+	ftape_put_bad_sector_entry(segments_per_track * tracks_per_tape - 2,
+				   0xff3fffff;
+	ftape_put_bad_sector_entry(segments_per_track * tracks_per_tape - 1,
+				   0xffffe000;
+#endif
+	/*  Enable to test bad sector handling
+	 */
+#if 0
+	ftape_put_bad_sector_entry(30, 0xfffffffe)
+	ftape_put_bad_sector_entry(32, 0x7fffffff);
+	ftape_put_bad_sector_entry(34, 0xfffeffff);
+	ftape_put_bad_sector_entry(36, 0x55555555);
+	ftape_put_bad_sector_entry(38, 0xffffffff);
+	ftape_put_bad_sector_entry(50, 0xffff0000);
+	ftape_put_bad_sector_entry(51, 0xffffffff);
+	ftape_put_bad_sector_entry(52, 0xffffffff);
+	ftape_put_bad_sector_entry(53, 0x0000ffff);
+#endif
+	/*  Enable when testing multiple volume tar dumps.
+	 */
+#if 0
+	{
+		int i;
+
+		for (i = ft_first_data_segment;
+		     i <= ft_last_data_segment - 7; ++i) {
+			ftape_put_bad_sector_entry(i, EMPTY_SEGMENT);
+		}
+	}
+#endif
+	/*  Enable when testing bit positions in *_error_map
+	 */
+#if 0
+	{
+		int i;
+		
+		for (i = first_data_segment; i <= last_data_segment; ++i) {
+			ftape_put_bad_sector_entry(i,
+					   ftape_get_bad_sector_entry(i) 
+					   | 0x00ff00ff);
+		}
+	}
+#endif
+}
+
+static void print_bad_sector_map(void)
+{
+	unsigned int good_sectors;
+	unsigned int total_bad = 0;
+	int i;
+	TRACE_FUN(ft_t_flow);
+
+	if (ft_format_code == fmt_big || 
+	    ft_format_code == fmt_var || 
+	    ft_format_code == fmt_1100ft) {
+		SectorCount *ptr = (SectorCount *)bad_sector_map;
+		unsigned int sector;
+
+		while((sector = get_sector(ptr++)) != 0) {
+			if ((ft_format_code == fmt_big || 
+			     ft_format_code == fmt_var) &&
+			    sector & 0x800000) {
+				total_bad += FT_SECTORS_PER_SEGMENT - 3;
+				TRACE(ft_t_noise, "bad segment at sector: %6d",
+				      sector & 0x7fffff);
+			} else {
+				++total_bad;
+				TRACE(ft_t_noise, "bad sector: %6d", sector);
+			}
+		}
+		/*  Display old ftape's end-of-file marks
+		 */
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,0,0)
+		while ((sector = get_unaligned(((__u16*)ptr)++)) != 0) {
+			TRACE(ft_t_noise, "Old ftape eof mark: %4d/%2d",
+			      sector, get_unaligned(((__u16*)ptr)++));
+		}
+#else
+		while ((sector = *((__u16*)ptr)++) != 0) {
+			TRACE(ft_t_noise, "Old ftape eof mark: %4d/%2d",
+			      sector, *((__u16*)ptr)++);
+		}
+#endif			
+	} else { /* fixed size format */
+		for (i = ft_first_data_segment;
+		     i < (int)(ft_segments_per_track * ft_tracks_per_tape); ++i) {
+			SectorMap map = ((SectorMap *) bad_sector_map)[i];
+
+			if (map) {
+				TRACE(ft_t_noise,
+				      "bsm for segment %4d: 0x%08x", i, (unsigned int)map);
+				total_bad += ((map == EMPTY_SEGMENT)
+					       ? FT_SECTORS_PER_SEGMENT - 3
+					       : count_ones(map));
+			}
+		}
+	}
+	good_sectors =
+		((ft_segments_per_track * ft_tracks_per_tape - ft_first_data_segment)
+		 * (FT_SECTORS_PER_SEGMENT - 3)) - total_bad;
+	TRACE(ft_t_info, "%d Kb usable on this tape", good_sectors);
+	if (total_bad == 0) {
+		TRACE(ft_t_info,
+		      "WARNING: this tape has no bad blocks registered !");
+	} else {
+		TRACE(ft_t_info, "%d bad sectors", total_bad);
+	}
+	TRACE_EXIT;
+}
+
+
+void ftape_extract_bad_sector_map(__u8 * buffer)
+{
+	TRACE_FUN(ft_t_any);
+
+	/*  Fill the bad sector map with the contents of buffer.
+	 */
+	if (ft_format_code == fmt_var || ft_format_code == fmt_big) {
+		/* QIC-3010/3020 and wide QIC-80 tapes no longer have a failed
+		 * sector log but use this area to extend the bad sector map.
+		 */
+		bad_sector_map = &buffer[FT_HEADER_END];
+	} else {
+		/* non-wide QIC-80 tapes have a failed sector log area that
+		 * mustn't be included in the bad sector map.
+		 */
+		bad_sector_map = &buffer[FT_FSL + FT_FSL_SIZE];
+	}
+	if (ft_format_code == fmt_1100ft || 
+	    ft_format_code == fmt_var    ||
+	    ft_format_code == fmt_big) {
+		bsm_hash_ptr = (SectorCount *)bad_sector_map;
+	} else {
+		bsm_hash_ptr = NULL;
+	}
+	bsm_debug_fake();
+	if (TRACE_LEVEL >= ft_t_info) {
+		print_bad_sector_map();
+	}
+	TRACE_EXIT;
+}
+
+static inline SectorMap cvt2map(unsigned int sector)
+{
+	return 1 << (((sector & 0x7fffff) - 1) % FT_SECTORS_PER_SEGMENT);
+}
+
+static inline int cvt2segment(unsigned int sector)
+{
+	return ((sector & 0x7fffff) - 1) / FT_SECTORS_PER_SEGMENT;
+}
+
+static int forward_seek_entry(int segment_id, 
+			      SectorCount **ptr, 
+			      SectorMap *map)
+{
+	unsigned int sector;
+	int segment;
+
+	do {
+		sector = get_sector((*ptr)++);
+		segment = cvt2segment(sector);
+	} while (sector != 0 && segment < segment_id);
+	(*ptr) --; /* point to first sector >= segment_id */
+	/*  Get all sectors in segment_id
+	 */
+	if (sector == 0 || segment != segment_id) {
+		*map = 0;
+		return 0;
+	} else if ((sector & 0x800000) &&
+		   (ft_format_code == fmt_var || ft_format_code == fmt_big)) {
+		*map = EMPTY_SEGMENT;
+		return FT_SECTORS_PER_SEGMENT;
+	} else {
+		int count = 1;
+		SectorCount *tmp_ptr = (*ptr) + 1;
+		
+		*map = cvt2map(sector);
+		while ((sector = get_sector(tmp_ptr++)) != 0 &&
+		       (segment = cvt2segment(sector)) == segment_id) {
+			*map |= cvt2map(sector);
+			++count;
+		}
+		return count;
+	}
+}
+
+static int backwards_seek_entry(int segment_id,
+				SectorCount **ptr,
+				SectorMap *map)
+{
+	unsigned int sector;
+	int segment; /* max unsigned int */
+
+	if (*ptr <= (SectorCount *)bad_sector_map) {
+		*map = 0;
+		return 0;
+	}
+	do {
+		sector  = get_sector(--(*ptr));
+		segment = cvt2segment(sector);
+	} while (*ptr > (SectorCount *)bad_sector_map && segment > segment_id);
+	if (segment > segment_id) { /*  at start of list, no entry found */
+		*map = 0;
+		return 0;
+	} else if (segment < segment_id) {
+		/*  before smaller entry, adjust for overshoot */
+		(*ptr) ++;
+		*map = 0;
+		return 0;
+	} else if ((sector & 0x800000) &&
+		   (ft_format_code == fmt_big || ft_format_code == fmt_var)) {
+		*map = EMPTY_SEGMENT;
+		return FT_SECTORS_PER_SEGMENT;
+	} else { /*  get all sectors in segment_id */
+		int count = 1;
+
+		*map = cvt2map(sector);
+		while(*ptr > (SectorCount *)bad_sector_map) {
+			sector = get_sector(--(*ptr));
+			segment = cvt2segment(sector);
+			if (segment != segment_id) {
+				break;
+			}
+			*map |= cvt2map(sector);
+			++count;
+		}
+		if (segment < segment_id) {
+			(*ptr) ++;
+		}
+		return count;
+	}
+}
+
+void ftape_put_bad_sector_entry(int segment_id, SectorMap new_map)
+{
+	SectorCount *ptr = (SectorCount *)bad_sector_map;
+	int count;
+	int new_count;
+	SectorMap map;
+	TRACE_FUN(ft_t_any);
+
+	if (ft_format_code == fmt_1100ft || 
+	    ft_format_code == fmt_var || 
+	    ft_format_code == fmt_big) {
+		count = forward_seek_entry(segment_id, &ptr, &map);
+		new_count = count_ones(new_map);
+		/* If format code == 4 put empty segment instead of 32
+		 * bad sectors.
+		 */
+		if (ft_format_code == fmt_var || ft_format_code == fmt_big) {
+			if (new_count == FT_SECTORS_PER_SEGMENT) {
+				new_count = 1;
+			}
+			if (count == FT_SECTORS_PER_SEGMENT) {
+				count = 1;
+			}
+		}
+		if (count != new_count) {
+			/* insert (or delete if < 0) new_count - count
+			 * entries.  Move trailing part of list
+			 * including terminating 0.
+			 */
+			SectorCount *hi_ptr = ptr;
+
+			do {
+			} while (get_sector(hi_ptr++) != 0);
+			/*  Note: ptr is of type byte *, and each bad sector
+			 *  consumes 3 bytes.
+			 */
+			memmove(ptr + new_count, ptr + count,
+				(size_t)(hi_ptr - (ptr + count))*sizeof(SectorCount));
+		}
+		TRACE(ft_t_noise, "putting map 0x%08x at %p, segment %d",
+		      (unsigned int)new_map, ptr, segment_id);
+		if (new_count == 1 && new_map == EMPTY_SEGMENT) {
+			put_sector(ptr++, (0x800001 + 
+					  segment_id * 
+					  FT_SECTORS_PER_SEGMENT));
+		} else {
+			int i = 0;
+
+			while (new_map) {
+				if (new_map & 1) {
+					put_sector(ptr++, 
+						   1 + segment_id * 
+						   FT_SECTORS_PER_SEGMENT + i);
+				}
+				++i;
+				new_map >>= 1;
+			}
+		}
+	} else {
+		((SectorMap *) bad_sector_map)[segment_id] = new_map;
+	}
+	TRACE_EXIT;
+}
+
+SectorMap ftape_get_bad_sector_entry(int segment_id)
+{
+	if (ft_used_header_segment == -1) {
+		/*  When reading header segment we'll need a blank map.
+		 */
+		return 0;
+	} else if (bsm_hash_ptr != NULL) {
+		/*  Invariants:
+		 *    map - mask value returned on last call.
+		 *    bsm_hash_ptr - points to first sector greater or equal to
+		 *          first sector in last_referenced segment.
+		 *    last_referenced - segment id used in the last call,
+		 *                      sector and map belong to this id.
+		 *  This code is designed for sequential access and retries.
+		 *  For true random access it may have to be redesigned.
+		 */
+		static int last_reference = -1;
+		static SectorMap map = 0;
+
+		if (segment_id > last_reference) {
+			/*  Skip all sectors before segment_id
+			 */
+			forward_seek_entry(segment_id, &bsm_hash_ptr, &map);
+		} else if (segment_id < last_reference) {
+			/* Skip backwards until begin of buffer or
+			 * first sector in segment_id 
+			 */
+			backwards_seek_entry(segment_id, &bsm_hash_ptr, &map);
+		}		/* segment_id == last_reference : keep map */
+		last_reference = segment_id;
+		return map;
+	} else {
+		return ((SectorMap *) bad_sector_map)[segment_id];
+	}
+}
+
+/*  This is simply here to prevent us from overwriting other kernel
+ *  data. Writes will result in NULL Pointer dereference.
+ */
+void ftape_init_bsm(void)
+{
+	bad_sector_map = NULL;
+	bsm_hash_ptr   = NULL;
+}

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov