|  | /* | 
|  | *      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/config.h> /* for CONFIG_ZFT_DFLT_BLK_SZ */ | 
|  | #include <linux/errno.h> | 
|  | #include <linux/mm.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; | 
|  | __u8 *zft_hseg_buf; | 
|  | int zft_deblock_segment = -1; | 
|  | zft_status_enum zft_io_state = zft_idle; | 
|  | int zft_header_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; | 
|  | } |