/* * 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: /cvs/work/hardhat2_1_ar7/linux-2.4.17_mvl21/drivers/char/ftape/zftape/zftape-rw.c,v $ * $Revision: 1.1.1.1 $ * $Date: 2003/06/23 22:18:26 $ * * This file contains some common code for the r/w code for * zftape. */ #include /* for CONFIG_ZFT_DFLT_BLK_SZ */ #include #include #include #include #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; __u8 *zft_hseg_buf; int zft_deblock_segment = -1; zft_status_enum zft_io_state = zft_idle; int zft_header_changed; int zft_bad_sector_map_changed; int zft_qic113; /* conform to old specs. and old zftape */ int zft_use_compression; 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; unsigned int zft_written_segments; int zft_label_changed; /* 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; static __s64 tape_pos; __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; }