patch-1.3.72 linux/drivers/char/ftape/ftape-bsm.c
Next file: linux/drivers/char/ftape/ftape-bsm.h
Previous file: linux/drivers/char/ftape/fdc-isr.h
Back to the patch index
Back to the overall index
- Lines: 430
- Date:
Wed Mar 6 15:07:19 1996
- Orig file:
v1.3.71/linux/drivers/char/ftape/ftape-bsm.c
- Orig date:
Thu Jan 1 02:00:00 1970
diff -u --recursive --new-file v1.3.71/linux/drivers/char/ftape/ftape-bsm.c linux/drivers/char/ftape/ftape-bsm.c
@@ -0,0 +1,429 @@
+/*
+ * Copyright (C) 1994-1995 Bas Laarhoven.
+
+ 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: /home/bas/distr/ftape-2.03b/RCS/ftape-bsm.c,v $
+ $Author: bas $
+ *
+ $Revision: 1.7 $
+ $Date: 1995/04/30 13:15:14 $
+ $State: Beta $
+ *
+ * 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/module.h>
+#include <linux/ftape.h>
+#include <linux/string.h>
+
+#include "tracing.h"
+#include "ftape-bsm.h"
+#include "ftape-ctl.h"
+#include "ftape-rw.h"
+
+
+/* Global vars.
+ */
+int bad_sector_map_changed = 0;
+
+/* Local vars.
+ */
+static byte bad_sector_map[BAD_SECTOR_MAP_SIZE];
+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(byte * buffer)
+{
+ static byte list[BAD_SECTOR_MAP_SIZE];
+ unsigned long *src_ptr = (unsigned long *) list;
+ byte *dst_ptr = bad_sector_map;
+ unsigned long map;
+ unsigned sector = 1;
+ int i;
+
+ memcpy(list, bad_sector_map, sizeof(list));
+ memset(bad_sector_map, 0, sizeof(bad_sector_map));
+ while ((byte *) src_ptr - list < sizeof(list)) {
+ map = *src_ptr++;
+ if (map == EMPTY_SEGMENT) {
+ *(unsigned long *) dst_ptr = 0x800000 + sector;
+ dst_ptr += 3;
+ sector += SECTORS_PER_SEGMENT;
+ } else {
+ for (i = 0; i < SECTORS_PER_SEGMENT; ++i) {
+ if (map & 1) {
+ *(unsigned long *) dst_ptr = sector;
+ dst_ptr += 3;
+ }
+ map >>= 1;
+ ++sector;
+ }
+ }
+ }
+ bad_sector_map_changed = 1;
+ *(buffer + 4) = 4; /* put new format code */
+ format_code = 4;
+}
+
+#endif
+
+byte *
+ find_end_of_bsm_list(byte * ptr, byte * limit)
+{
+ while (ptr + 2 < limit) {
+ if (ptr[0] || ptr[1] || ptr[2]) {
+ ptr += 3;
+ } else {
+ return ptr;
+ }
+ }
+ return NULL;
+}
+
+void store_bad_sector_map(byte * buffer)
+{
+ TRACE_FUN(8, "store_bad_sector_map");
+ size_t count;
+ size_t offset;
+
+ /* Store the bad sector map in buffer.
+ */
+ if (format_code == 4) {
+ offset = 256;
+ count = sizeof(bad_sector_map);
+ } else {
+ offset = 2 * SECTOR_SIZE; /* skip failed sector log */
+ count = sizeof(bad_sector_map) - (offset - 256);
+ }
+ memcpy(buffer + offset, bad_sector_map, count);
+ TRACE_EXIT;
+}
+
+void put_sector(byte ** ptr, unsigned long sector)
+{
+ *(*ptr)++ = sector & 0xff;
+ sector >>= 8;
+ *(*ptr)++ = sector & 0xff;
+ sector >>= 8;
+ *(*ptr)++ = sector & 0xff;
+}
+
+unsigned long get_sector(byte ** ptr, mode_type mode)
+{
+ unsigned long sector;
+
+ if (mode == forward) {
+ sector = *(*ptr)++;
+ sector += *(*ptr)++ << 8;
+ sector += *(*ptr)++ << 16;
+ } else {
+ sector = *--(*ptr) << 16;
+ sector += *--(*ptr) << 8;
+ sector += *--(*ptr);
+ }
+ return sector;
+}
+
+void extract_bad_sector_map(byte * buffer)
+{
+ TRACE_FUN(8, "extract_bad_sector_map");
+
+ /* Fill the bad sector map with the contents of buffer.
+ */
+ if (format_code == 4) {
+ /* 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.
+ */
+ memcpy(bad_sector_map, buffer + 256, sizeof(bad_sector_map));
+ } else {
+ /* non-wide QIC-80 tapes have a failed sector log area that
+ * mustn't be included in the bad sector map.
+ */
+ memcpy(bad_sector_map, buffer + 256 + FAILED_SECTOR_LOG_SIZE,
+ sizeof(bad_sector_map) - FAILED_SECTOR_LOG_SIZE);
+ }
+#if 0
+ /* for testing of bad sector handling at end of tape
+ */
+ ((unsigned long *) bad_sector_map)[segments_per_track * tracks_per_tape - 3] = 0x000003e0;
+ ((unsigned long *) bad_sector_map)[segments_per_track * tracks_per_tape - 2] = 0xff3fffff;
+ ((unsigned long *) bad_sector_map)[segments_per_track * tracks_per_tape - 1] = 0xffffe000;
+#endif
+#if 0
+ /* Enable to test bad sector handling
+ */
+ ((unsigned long *) bad_sector_map)[30] = 0xfffffffe;
+ ((unsigned long *) bad_sector_map)[32] = 0x7fffffff;
+ ((unsigned long *) bad_sector_map)[34] = 0xfffeffff;
+ ((unsigned long *) bad_sector_map)[36] = 0x55555555;
+ ((unsigned long *) bad_sector_map)[38] = 0xffffffff;
+ ((unsigned long *) bad_sector_map)[50] = 0xffff0000;
+ ((unsigned long *) bad_sector_map)[51] = 0xffffffff;
+ ((unsigned long *) bad_sector_map)[52] = 0xffffffff;
+ ((unsigned long *) bad_sector_map)[53] = 0x0000ffff;
+#endif
+#if 0
+ /* Enable when testing multiple volume tar dumps.
+ */
+ for (i = first_data_segment; i <= ftape_last_segment.id - 7; ++i) {
+ ((unsigned long *) bad_sector_map)[i] = EMPTY_SEGMENT;
+ }
+#endif
+#if 0
+ /* Enable when testing bit positions in *_error_map
+ */
+ for (i = first_data_segment; i <= ftape_last_segment.id; ++i) {
+ ((unsigned long *) bad_sector_map)[i] |= 0x00ff00ff;
+ }
+#endif
+ if (tracing > 2) {
+ unsigned int map;
+ int good_sectors = 0;
+ int bad_sectors;
+ unsigned int total_bad = 0;
+ int i;
+
+ if (format_code == 4 || format_code == 3) {
+ byte *ptr = bad_sector_map;
+ unsigned sector;
+
+ do {
+ sector = get_sector(&ptr, forward);
+ if (sector != 0) {
+ if (format_code == 4 && sector & 0x800000) {
+ total_bad += SECTORS_PER_SEGMENT - 3;
+ TRACEx1(6, "bad segment at sector: %6d", sector & 0x7fffff);
+ } else {
+ ++total_bad;
+ TRACEx1(6, "bad sector: %6d", sector);
+ }
+ }
+ } while (sector != 0);
+ /* Display end-of-file marks
+ */
+ do {
+ sector = *((unsigned short *) ptr)++;
+ if (sector) {
+ TRACEx2(4, "eof mark: %4d/%2d", sector,
+ *((unsigned short *) ptr)++);
+ }
+ } while (sector);
+ } else {
+ for (i = first_data_segment;
+ i < segments_per_track * tracks_per_tape; ++i) {
+ map = ((unsigned long *) bad_sector_map)[i];
+ bad_sectors = count_ones(map);
+ if (bad_sectors > 0) {
+ TRACEx2(6, "bsm for segment %4d: 0x%08x", i, map);
+ if (bad_sectors > SECTORS_PER_SEGMENT - 3) {
+ bad_sectors = SECTORS_PER_SEGMENT - 3;
+ }
+ total_bad += bad_sectors;
+ }
+ }
+ }
+ good_sectors = ((segments_per_track * tracks_per_tape - first_data_segment)
+ * (SECTORS_PER_SEGMENT - 3)) - total_bad;
+ TRACEx1(3, "%d Kb usable on this tape",
+ good_sectors - ftape_last_segment.free);
+ if (total_bad == 0) {
+ TRACE(1, "WARNING: this tape has no bad blocks registered !");
+ } else {
+ TRACEx1(2, "%d bad sectors", total_bad);
+ }
+ }
+ TRACE_EXIT;
+}
+
+unsigned long cvt2map(int sector)
+{
+ return 1 << (((sector & 0x7fffff) - 1) % SECTORS_PER_SEGMENT);
+}
+
+int cvt2segment(int sector)
+{
+ return ((sector & 0x7fffff) - 1) / SECTORS_PER_SEGMENT;
+}
+
+int forward_seek_entry(int segment_id, byte ** ptr, unsigned long *map)
+{
+ byte *tmp_ptr;
+ unsigned long sector;
+ int segment;
+ int count;
+
+ do {
+ sector = get_sector(ptr, forward);
+ segment = cvt2segment(sector);
+ } while (sector != 0 && segment < segment_id);
+ tmp_ptr = *ptr - 3; /* point to first sector >= segment_id */
+ /* Get all sectors in segment_id
+ */
+ if (format_code == 4 && (sector & 0x800000) && segment == segment_id) {
+ *map = EMPTY_SEGMENT;
+ count = 32;
+ } else {
+ *map = 0;
+ count = 0;
+ while (sector != 0 && segment == segment_id) {
+ *map |= cvt2map(sector);
+ sector = get_sector(ptr, forward);
+ segment = cvt2segment(sector);
+ ++count;
+ }
+ }
+ *ptr = tmp_ptr;
+ return count;
+}
+
+int backwards_seek_entry(int segment_id, byte ** ptr, unsigned long *map)
+{
+ unsigned long sector;
+ int segment;
+ int count;
+
+ *map = 0;
+ if (*ptr > bad_sector_map) {
+ do {
+ sector = get_sector(ptr, backward);
+ segment = cvt2segment(sector);
+ } while (*ptr > bad_sector_map && segment > segment_id);
+ count = 0;
+ if (segment > segment_id) {
+ /* at start of list, no entry found */
+ } else if (segment < segment_id) {
+ /* before smaller entry, adjust for overshoot */
+ *ptr += 3;
+ } else {
+ /* get all sectors in segment_id */
+ if (format_code == 4 && (sector & 0x800000)) {
+ *map = EMPTY_SEGMENT;
+ count = 32;
+ } else {
+ do {
+ *map |= cvt2map(sector);
+ ++count;
+ if (*ptr <= bad_sector_map) {
+ break;
+ }
+ sector = get_sector(ptr, backward);
+ segment = cvt2segment(sector);
+ } while (segment == segment_id);
+ if (segment < segment_id) {
+ *ptr += 3;
+ }
+ }
+ }
+ } else {
+ count = 0;
+ }
+ return count;
+}
+
+void put_bad_sector_entry(int segment_id, unsigned long new_map)
+{
+ byte *ptr = bad_sector_map;
+ int count;
+ int new_count;
+ unsigned long map;
+
+ if (format_code == 3 || format_code == 4) {
+ 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 (new_count == 32 && format_code == 4) {
+ new_count = 1;
+ }
+ if (count != new_count) {
+ /* insert (or delete if < 0) new_count - count entries.
+ * Move trailing part of list including terminating 0.
+ */
+ byte *hi_ptr = ptr;
+
+ do {
+ } while (get_sector(&hi_ptr, forward) != 0);
+ memmove(ptr + new_count, ptr + count, hi_ptr - (ptr + count));
+ }
+ if (new_count == 1 && new_map == EMPTY_SEGMENT) {
+ put_sector(&ptr, 0x800001 + segment_id * SECTORS_PER_SEGMENT);
+ } else {
+ int i = 0;
+
+ while (new_map) {
+ if (new_map & 1) {
+ put_sector(&ptr, 1 + segment_id * SECTORS_PER_SEGMENT + i);
+ }
+ ++i;
+ new_map >>= 1;
+ }
+ }
+ } else {
+ ((unsigned long *) bad_sector_map)[segment_id] = new_map;
+ }
+ bad_sector_map_changed = 1;
+}
+
+unsigned long get_bad_sector_entry(int segment_id)
+{
+ TRACE_FUN(8, "get_bad_sector_entry");
+ static unsigned long map = 0;
+
+ if (used_header_segment == -1) {
+ /* When reading header segment we'll need a blank map.
+ */
+ map = 0;
+ } else if (format_code == 3 || format_code == 4) {
+ /* Invariants:
+ * map - mask value returned on last call.
+ * 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 byte *ptr = bad_sector_map;
+
+ if (segment_id > last_reference) {
+ /* Skip all sectors before segment_id
+ */
+ forward_seek_entry(segment_id, &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, &ptr, &map);
+ } /* segment_id == last_reference : keep map */
+ last_reference = segment_id;
+ } else {
+ map = ((unsigned long *) bad_sector_map)[segment_id];
+ }
+ TRACE_EXIT;
+ return map;
+}
+
+void ftape_init_bsm(void)
+{
+ memset(bad_sector_map, 0, sizeof(bad_sector_map));
+}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov
with Sam's (original) version of this