patch-2.1.66 linux/drivers/char/ftape/zftape/zftape-rw.c

Next file: linux/drivers/char/ftape/zftape/zftape-rw.h
Previous file: linux/drivers/char/ftape/zftape/zftape-read.h
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/zftape/zftape-rw.c linux/drivers/char/ftape/zftape/zftape-rw.c
@@ -0,0 +1,377 @@
+/*
+ *      Copyright (C) 1996, 1997 Claus-Justus 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/zftape/zftape-rw.c,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:19:08 $
+ *
+ *      This file contains some common code for the r/w code for
+ *      zftape.
+ */
+
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <asm/segment.h>
+
+#include <linux/zftape.h>
+#include "../zftape/zftape-init.h"
+#include "../zftape/zftape-eof.h"
+#include "../zftape/zftape-ctl.h"
+#include "../zftape/zftape-write.h"
+#include "../zftape/zftape-read.h"
+#include "../zftape/zftape-rw.h"
+#include "../zftape/zftape-vtbl.h"
+
+/*      Global vars.
+ */
+
+__u8 *zft_deblock_buf = NULL;
+__u8 *zft_hseg_buf    = NULL;
+int zft_deblock_segment = -1;
+zft_status_enum zft_io_state = zft_idle;
+int zft_header_changed         = 0;
+int zft_bad_sector_map_changed = 0;
+int zft_qic113 = 0; /* conform to old specs. and old zftape */
+int zft_use_compression        = 0;
+zft_position zft_pos = {
+	-1, /* seg_pos */
+	0,  /* seg_byte_pos */
+	0,  /* tape_pos */
+	0   /* volume_pos */
+};
+unsigned int zft_blk_sz = CONFIG_ZFT_DFLT_BLK_SZ;
+__s64 zft_capacity      = 0;
+
+unsigned int zft_written_segments = 0;
+int zft_label_changed = 0;
+
+/*      Local vars.
+ */
+
+unsigned int zft_get_seg_sz(unsigned int segment)
+{
+	int size;
+	TRACE_FUN(ft_t_any);
+	
+	size = FT_SEGMENT_SIZE - 
+		count_ones(ftape_get_bad_sector_entry(segment))*FT_SECTOR_SIZE;
+	if (size > 0) {
+		TRACE_EXIT (unsigned)size; 
+	} else {
+		TRACE_EXIT 0;
+	}
+}
+
+/* ftape_set_flags(). Claus-Justus Heine, 1994/1995
+ */
+void zft_set_flags(unsigned minor_unit)
+{     
+	TRACE_FUN(ft_t_flow);
+	
+	zft_use_compression = zft_qic_mode = 0;
+	switch (minor_unit & ZFT_MINOR_OP_MASK) {
+	case (ZFT_Q80_MODE | ZFT_ZIP_MODE):
+	case ZFT_ZIP_MODE:
+		zft_use_compression = 1;
+	case 0:
+	case ZFT_Q80_MODE:
+		zft_qic_mode = 1;
+		if (zft_mt_compression) { /* override the default */
+			zft_use_compression = 1;
+		}
+		break;
+	case ZFT_RAW_MODE:
+		TRACE(ft_t_noise, "switching to raw mode");
+		break;
+	default:
+		TRACE(ft_t_warn, "Warning:\n"
+		      KERN_INFO "Wrong combination of minor device bits.\n"
+		      KERN_INFO "Switching to raw read-only mode.");
+		zft_write_protected = 1;
+		break;
+	}
+	TRACE_EXIT;
+}
+
+/* computes the segment and byte offset inside the segment
+ * corresponding to tape_pos.
+ *
+ * tape_pos gives the offset in bytes from the beginning of the
+ * ft_first_data_segment *seg_byte_pos is the offset in the current
+ * segment in bytes
+ *
+ * Of, if this routine was called often one should cache the last data
+ * pos it was called with, but actually this is only needed in
+ * ftape_seek_block(), that is, almost never.
+ */
+int zft_calc_seg_byte_coord(int *seg_byte_pos, __s64 tape_pos)
+{
+	int segment;
+	int seg_sz;
+	TRACE_FUN(ft_t_flow);
+	
+	if (tape_pos == 0) {
+		*seg_byte_pos = 0;
+		segment = ft_first_data_segment;
+	} else {
+		seg_sz = 0;
+		
+		for (segment = ft_first_data_segment; 
+		     ((tape_pos > 0) && (segment <= ft_last_data_segment));
+		     segment++) {
+			seg_sz = zft_get_seg_sz(segment); 
+			tape_pos -= seg_sz;
+		}
+		if(tape_pos >= 0) {
+			/* the case tape_pos > != 0 means that the
+			 * argument tape_pos lies beyond the EOT.
+			 */
+			*seg_byte_pos= 0;
+		} else { /* tape_pos < 0 */
+			segment--;
+			*seg_byte_pos= tape_pos + seg_sz;
+		}
+	}
+	TRACE_EXIT(segment);
+}
+
+/* ftape_calc_tape_pos().
+ *
+ * computes the offset in bytes from the beginning of the
+ * ft_first_data_segment inverse to ftape_calc_seg_byte_coord
+ *
+ * We should do some caching. But how:
+ *
+ * Each time the header segments are read in, this routine is called
+ * with ft_tracks_per_tape*segments_per_track argumnet. So this should be
+ * the time to reset the cache.
+ *
+ * Also, it might be in the future that the bad sector map gets
+ * changed.  -> reset the cache
+ */
+static int seg_pos    = 0;
+static __s64 tape_pos = 0;
+
+__s64 zft_get_capacity(void)
+{
+	seg_pos  = ft_first_data_segment;
+	tape_pos = 0;
+
+	while (seg_pos <= ft_last_data_segment) {
+		tape_pos += zft_get_seg_sz(seg_pos ++);
+	}
+	return tape_pos;
+}
+
+__s64 zft_calc_tape_pos(int segment)
+{
+	int d1, d2, d3;
+	TRACE_FUN(ft_t_any);
+	
+	if (segment > ft_last_data_segment) {
+	        TRACE_EXIT zft_capacity;
+	}
+	if (segment < ft_first_data_segment) {
+		TRACE_EXIT 0;
+	}
+	d2 = segment - seg_pos;
+	if (-d2 > 10) {
+		d1 = segment - ft_first_data_segment;
+		if (-d2 > d1) {
+			tape_pos = 0;
+			seg_pos = ft_first_data_segment;
+			d2 = d1;
+		}
+	}
+	if (d2 > 10) {
+		d3 = ft_last_data_segment - segment;
+		if (d2 > d3) {
+			tape_pos = zft_capacity;
+			seg_pos  = ft_last_data_segment + 1;
+			d2 = -d3;
+		}
+	}		
+	if (d2 > 0) {
+		while (seg_pos < segment) {
+			tape_pos +=  zft_get_seg_sz(seg_pos++);
+		}
+	} else {
+		while (seg_pos > segment) {
+			tape_pos -=  zft_get_seg_sz(--seg_pos);
+		}
+	}
+	TRACE(ft_t_noise, "new cached pos: %d", seg_pos);
+
+	TRACE_EXIT tape_pos;
+}
+
+/* copy Z-label string to buffer, keeps track of the correct offset in
+ * `buffer' 
+ */
+void zft_update_label(__u8 *buffer)
+{ 
+	TRACE_FUN(ft_t_flow);
+	
+	if (strncmp(&buffer[FT_LABEL], ZFTAPE_LABEL, 
+		    sizeof(ZFTAPE_LABEL)-1) != 0) {
+		TRACE(ft_t_info, "updating label from \"%s\" to \"%s\"",
+		      &buffer[FT_LABEL], ZFTAPE_LABEL);
+		strcpy(&buffer[FT_LABEL], ZFTAPE_LABEL);
+		memset(&buffer[FT_LABEL] + sizeof(ZFTAPE_LABEL) - 1, ' ', 
+		       FT_LABEL_SZ - sizeof(ZFTAPE_LABEL + 1));
+		PUT4(buffer, FT_LABEL_DATE, 0);
+		zft_label_changed = zft_header_changed = 1; /* changed */
+	}
+	TRACE_EXIT;
+}
+
+int zft_verify_write_segments(unsigned int segment, 
+			      __u8 *data, size_t size,
+			      __u8 *buffer)
+{
+	int result;
+	__u8 *write_buf;
+	__u8 *src_buf;
+	int single;
+	int seg_pos;
+	int seg_sz;
+	int remaining;
+	ft_write_mode_t write_mode;
+	TRACE_FUN(ft_t_flow);
+
+	seg_pos   = segment;
+	seg_sz    = zft_get_seg_sz(seg_pos);
+	src_buf   = data;
+	single    = size <= seg_sz;
+	remaining = size;
+	do {
+		TRACE(ft_t_noise, "\n"
+		      KERN_INFO "remaining: %d\n"
+		      KERN_INFO "seg_sz   : %d\n"
+		      KERN_INFO "segment  : %d",
+		      remaining, seg_sz, seg_pos);
+		if (remaining == seg_sz) {
+			write_buf = src_buf;
+			write_mode = single ? FT_WR_SINGLE : FT_WR_MULTI;
+			remaining = 0;
+		} else if (remaining > seg_sz) {
+			write_buf = src_buf;
+			write_mode = FT_WR_ASYNC; /* don't start tape */
+			remaining -= seg_sz;
+		} else { /* remaining < seg_sz */
+			write_buf = buffer;
+			memcpy(write_buf, src_buf, remaining);
+			memset(&write_buf[remaining],'\0',seg_sz-remaining);
+			write_mode = single ? FT_WR_SINGLE : FT_WR_MULTI;
+			remaining = 0;
+		}
+		if ((result = ftape_write_segment(seg_pos, 
+						  write_buf, 
+						  write_mode)) != seg_sz) {
+			TRACE(ft_t_err, "Error: "
+			      "Couldn't write segment %d", seg_pos);
+			TRACE_EXIT result < 0 ? result : -EIO; /* bail out */
+		}
+		zft_written_segments ++;
+		seg_sz = zft_get_seg_sz(++seg_pos);
+		src_buf += result;
+	} while (remaining > 0);
+	if (ftape_get_status()->fti_state == writing) {
+		TRACE_CATCH(ftape_loop_until_writes_done(),);
+		TRACE_CATCH(ftape_abort_operation(),);
+		zft_prevent_flush();
+	}
+	seg_pos = segment;
+	src_buf = data;
+	remaining = size;
+	do {
+		TRACE_CATCH(result = ftape_read_segment(seg_pos, buffer, 
+							single ? FT_RD_SINGLE
+							: FT_RD_AHEAD),);
+		if (memcmp(src_buf, buffer, 
+			   remaining > result ? result : remaining) != 0) {
+			TRACE_ABORT(-EIO, ft_t_err,
+				    "Failed to verify written segment %d",
+				    seg_pos);
+		}
+		remaining -= result;
+		TRACE(ft_t_noise, "verify successful:\n"
+		      KERN_INFO "segment  : %d\n"
+		      KERN_INFO "segsize  : %d\n"
+		      KERN_INFO "remaining: %d",
+		      seg_pos, result, remaining);
+		src_buf   += seg_sz;
+		seg_pos++;
+	} while (remaining > 0);
+	TRACE_EXIT size;
+}
+
+
+/* zft_erase().  implemented compression-handling
+ *
+ * calculate the first data-segment when using/not using compression.
+ *
+ * update header-segment and compression-map-segment.
+ */
+int zft_erase(void)
+{
+	int result = 0;
+	TRACE_FUN(ft_t_flow);
+	
+	if (!zft_header_read) {
+		TRACE_CATCH(zft_vmalloc_once((void **)&zft_hseg_buf,
+					     FT_SEGMENT_SIZE),);
+		/* no need to read the vtbl and compression map */
+		TRACE_CATCH(ftape_read_header_segment(zft_hseg_buf),);
+		if ((zft_old_ftape = 
+		     zft_ftape_validate_label(&zft_hseg_buf[FT_LABEL]))) {
+			zft_ftape_extract_file_marks(zft_hseg_buf);
+		}
+		TRACE(ft_t_noise,
+		      "ft_first_data_segment: %d, ft_last_data_segment: %d", 
+		      ft_first_data_segment, ft_last_data_segment);
+		zft_qic113 = (ft_format_code != fmt_normal &&
+			      ft_format_code != fmt_1100ft &&
+			      ft_format_code != fmt_425ft);
+	}
+	if (zft_old_ftape) {
+		zft_clear_ftape_file_marks();
+		zft_old_ftape = 0; /* no longer old ftape */
+	}
+	PUT2(zft_hseg_buf, FT_CMAP_START, 0);
+	zft_volume_table_changed = 1;
+	zft_capacity = zft_get_capacity();
+	zft_init_vtbl();
+	/* the rest must be done in ftape_update_header_segments 
+	 */
+	zft_header_read = 1;
+	zft_header_changed = 1; /* force update of timestamp */
+	result = zft_update_header_segments();
+
+	ftape_abort_operation();
+
+	zft_reset_position(&zft_pos);
+	zft_set_flags (zft_unit);
+	TRACE_EXIT result;
+}
+
+unsigned int zft_get_time(void) 
+{
+	unsigned int date = FT_TIME_STAMP(2097, 11, 30, 23, 59, 59); /* fun */
+	return date;
+}

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