summaryrefslogtreecommitdiffstats
path: root/drivers/char/ftape/ftape-write.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/char/ftape/ftape-write.c')
-rw-r--r--drivers/char/ftape/ftape-write.c723
1 files changed, 0 insertions, 723 deletions
diff --git a/drivers/char/ftape/ftape-write.c b/drivers/char/ftape/ftape-write.c
deleted file mode 100644
index 0ba24c978..000000000
--- a/drivers/char/ftape/ftape-write.c
+++ /dev/null
@@ -1,723 +0,0 @@
-
-
-
-/*
- * Copyright (C) 1993-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-write.c,v $
- $Author: bas $
- *
- $Revision: 1.26 $
- $Date: 1995/05/27 08:55:27 $
- $State: Beta $
- *
- * This file contains the writing code
- * for the QIC-117 floppy-tape driver for Linux.
- */
-
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/mm.h>
-#include <linux/ftape.h>
-#include <asm/uaccess.h>
-
-#include "tracing.h"
-#include "ftape-write.h"
-#include "ftape-read.h"
-#include "qic117.h"
-#include "ftape-io.h"
-#include "ftape-ctl.h"
-#include "ftape-rw.h"
-#include "ftape-eof.h"
-#include "ecc.h"
-#include "ftape-bsm.h"
-
-
-/* Global vars.
- */
-
-/* Local vars.
- */
-static int buf_pos_wr = 0;
-static int last_write_failed = 0;
-static int need_flush = 0;
-
-#define WRITE_MULTI 0
-#define WRITE_SINGLE 1
-
-void ftape_zap_write_buffers(void)
-{
- int i;
-
- for (i = 0; i < NR_BUFFERS; ++i) {
- buffer[i].status = done;
- }
- need_flush = 0;
-}
-
-int copy_and_gen_ecc(char *destination, byte * source,
- unsigned int bad_sector_map)
-{
- TRACE_FUN(8, "copy_and_gen_ecc");
- int result;
- struct memory_segment mseg;
- int bads = count_ones(bad_sector_map);
-
- if (bads > 0) {
- TRACEi(4, "bad sectors in map:", bads);
- }
- if (bads + 3 >= SECTORS_PER_SEGMENT) {
- TRACE(4, "empty segment");
- mseg.blocks = 0; /* skip entire segment */
- result = 0; /* nothing written */
- } else {
- mseg.blocks = SECTORS_PER_SEGMENT - bads;
- mseg.data = destination;
- memcpy(mseg.data, source, (mseg.blocks - 3) * SECTOR_SIZE);
- result = ecc_set_segment_parity(&mseg);
- if (result < 0) {
- TRACE(1, "ecc_set_segment_parity failed");
- } else {
- result = (mseg.blocks - 3) * SECTOR_SIZE;
- }
- }
- TRACE_EXIT;
- return result;
-}
-
-void prevent_flush(void)
-{
- need_flush = 0;
- ftape_state = idle;
-}
-
-int start_writing(int mode)
-{
- TRACE_FUN(5, "start_writing");
- int result = 0;
- buffer_struct *buff = &buffer[head];
- int segment_id = buff->segment_id;
-
- if (ftape_state == writing && buff->status == waiting) {
- setup_new_segment(buff, segment_id, 1);
- if (mode == WRITE_SINGLE) {
- buffer[head].next_segment = 0; /* stop tape instead of pause */
- }
- calc_next_cluster(buff); /* prepare */
- buff->status = writing;
- if (runner_status == idle) {
- TRACEi(5, "starting runner for segment", segment_id);
- result = ftape_start_tape(segment_id, buff->sector_offset);
- if (result >= 0) {
- runner_status = running;
- }
- }
- if (result >= 0) {
- result = setup_fdc_and_dma(buff, FDC_WRITE); /* go */
- }
- ftape_state = writing;
- }
- TRACE_EXIT;
- return result;
-}
-
-int loop_until_writes_done(void)
-{
- TRACE_FUN(5, "loop_until_writes_done");
- int i;
- int result = 0;
-
- /*
- * Wait until all data is actually written to tape.
- */
- while (ftape_state == writing && buffer[head].status != done) {
- TRACEx2(7, "tail: %d, head: %d", tail, head);
- for (i = 0; i < NR_BUFFERS; ++i) {
- TRACEx3(8, "buffer[ %d] segment_id: %d, status: %d",
- i, buffer[i].segment_id, buffer[i].status);
- }
- result = fdc_interrupt_wait(5 * SECOND);
- if (result < 0) {
- TRACE(1, "fdc_interrupt_wait failed");
- last_write_failed = 1;
- break;
- }
- if (buffer[head].status == error) {
- /* Allow escape from loop when signaled !
- */
- if (current->signal & _DONT_BLOCK) {
- TRACE(2, "interrupted by signal");
- TRACE_EXIT;
- result = -EINTR; /* is this the right return value ? */
- break;
- }
- if (buffer[head].hard_error_map != 0) {
- /* Implement hard write error recovery here
- */
- }
- buffer[head].status = waiting; /* retry this one */
- if (runner_status == aborting) {
- ftape_dumb_stop();
- runner_status = idle;
- }
- if (runner_status != idle) {
- TRACE(1, "unexpected state: runner_status != idle");
- result = -EIO;
- break;
- }
- start_writing(WRITE_MULTI);
- }
- TRACE(5, "looping until writes done");
- result = 0; /* normal exit status */
- }
- TRACE_EXIT;
- return result;
-}
-
-/* Write given segment from buffer at address onto tape.
- */
-int write_segment(unsigned segment_id, byte * address, int flushing)
-{
- TRACE_FUN(5, "write_segment");
- int result = 0;
- int bytes_written = 0;
-
- TRACEi(5, "segment_id =", segment_id);
- if (ftape_state != writing) {
- if (ftape_state == reading) {
- TRACE(5, "calling ftape_abort_operation");
- result = ftape_abort_operation();
- if (result < 0) {
- TRACE(1, "ftape_abort_operation failed");
- }
- }
- ftape_zap_read_buffers();
- ftape_zap_write_buffers();
- ftape_state = writing;
- }
- /* if all buffers full we'll have to wait...
- */
- wait_segment(writing);
- if (buffer[tail].status == error) {
- /* setup for a retry
- */
- buffer[tail].status = waiting;
- bytes_written = -EAGAIN; /* force retry */
- if (buffer[tail].hard_error_map != 0) {
- TRACEx1(1, "warning: %d hard error(s) in written segment",
- count_ones(buffer[tail].hard_error_map));
- TRACEx1(4, "hard_error_map = 0x%08lx", buffer[tail].hard_error_map);
- /* Implement hard write error recovery here
- */
- }
- } else if (buffer[tail].status == done) {
- history.defects += count_ones(buffer[tail].hard_error_map);
- } else {
- TRACE(1, "wait for empty segment failed");
- result = -EIO;
- }
- /* If just passed last segment on tape: wait for BOT or EOT mark.
- */
- if (result >= 0 && runner_status == logical_eot) {
- int status;
-
- result = ftape_ready_wait(timeout.seek, &status);
- if (result < 0 || (status & (QIC_STATUS_AT_BOT | QIC_STATUS_AT_EOT)) == 0) {
- TRACE(1, "eot/bot not reached");
- } else {
- runner_status = end_of_tape;
- }
- }
- /* should runner stop ?
- */
- if (result >= 0 &&
- (runner_status == aborting || runner_status == buffer_underrun ||
- runner_status == end_of_tape)) {
- if (runner_status != end_of_tape) {
- result = ftape_dumb_stop();
- }
- if (result >= 0) {
- if (runner_status == aborting) {
- if (buffer[head].status == writing) {
- buffer[head].status = done; /* ????? */
- }
- }
- runner_status = idle; /* aborted ? */
- }
- }
- /* Don't start tape if runner idle and segment empty.
- */
- if (result >= 0 && !(runner_status == idle &&
- get_bad_sector_entry(segment_id) == EMPTY_SEGMENT)) {
- if (buffer[tail].status == done) {
- /* now at least one buffer is empty, fill it with our data.
- * skip bad sectors and generate ecc.
- * copy_and_gen_ecc return nr of bytes written,
- * range 0..29 Kb inclusive !
- */
- result = copy_and_gen_ecc(buffer[tail].address, address,
- get_bad_sector_entry(segment_id));
- if (result >= 0) {
- bytes_written = result;
- buffer[tail].segment_id = segment_id;
- buffer[tail].status = waiting;
- next_buffer(&tail);
- }
- }
- /* Start tape only if all buffers full or flush mode.
- * This will give higher probability of streaming.
- */
- if (result >= 0 && runner_status != running &&
- ((head == tail && buffer[tail].status == waiting) || flushing)) {
- result = start_writing(WRITE_MULTI);
- }
- }
- TRACE_EXIT;
- return (result < 0) ? result : bytes_written;
-}
-
-/* Write as much as fits from buffer to the given segment on tape
- * and handle retries.
- * Return the number of bytes written (>= 0), or:
- * -EIO write failed
- * -EINTR interrupted by signal
- * -ENOSPC device full
- */
-int _write_segment(unsigned int segment_id, byte * buffer, int flush)
-{
- TRACE_FUN(5, "_write_segment");
- int retry = 0;
- int result;
-
- history.used |= 2;
- for (;;) {
- if (segment_id > ftape_last_segment.id && !flush) {
- result = -ENOSPC; /* tape full */
- break;
- }
- result = write_segment(segment_id, buffer, flush);
- if (result < 0) {
- if (result == -EAGAIN) {
- if (++retry > 100) {
- TRACE(1, "write failed, >100 retries in segment");
- result = -EIO; /* give up */
- break;
- } else {
- TRACEx1(2, "write error, retry %d", retry);
- }
- } else {
- TRACEi(1, "write_segment failed, error:", -result);
- break;
- }
- } else { /* success */
- if (result == 0) { /* empty segment */
- TRACE(4, "empty segment, nothing written");
- }
- break;
- }
- /* Allow escape from loop when signaled !
- */
- if (current->signal & _DONT_BLOCK) {
- TRACE(2, "interrupted by signal");
- TRACE_EXIT;
- result = -EINTR; /* is this the right return value ? */
- break;
- }
- }
- TRACE_EXIT;
- return result;
-}
-
-int update_header_segment(unsigned segment, byte * buffer)
-{
- TRACE_FUN(5, "update_header_segment");
- int result = 0;
- int status;
-
- if (buffer == NULL) {
- TRACE(5, "no input buffer specified");
- buffer = deblock_buffer;
- result = read_segment(used_header_segment, buffer, &status, 0);
- if (bad_sector_map_changed) {
- store_bad_sector_map(buffer);
- }
- if (failed_sector_log_changed) {
- update_failed_sector_log(buffer);
- }
- }
- if (result >= 0 && GET4(buffer, 0) != 0xaa55aa55) {
- TRACE(1, "wrong header signature found, aborting");
- result = -EIO;
- }
- if (result >= 0) {
- result = _write_segment(segment, buffer, 0);
- if (result >= 0 && runner_status == idle) {
- /* Force flush for single segment instead of relying on
- * flush in read_segment for multiple segments.
- */
- result = start_writing(WRITE_SINGLE);
- if (result >= 0 && ftape_state == writing) {
- result = loop_until_writes_done();
- prevent_flush();
- }
- }
-#ifdef VERIFY_HEADERS
- if (result >= 0) { /* read back and verify */
- result = read_segment(segment, scratch_buffer, &status, 0);
- /* Should retry if soft error during read !
- * TO BE IMPLEMENTED
- */
- if (result >= 0) {
- if (memcmp(buffer, scratch_buffer, sizeof(buffer)) == 0) {
- result = 0; /* verified */
- TRACE(5, "verified");
- } else {
- result = -EIO; /* verify failed */
- TRACE(5, "verify failed");
- }
- }
- }
-#endif
- }
- TRACE_EXIT;
- return result;
-}
-
-int ftape_write_header_segments(byte * buffer)
-{
- TRACE_FUN(5, "ftape_write_header_segments");
- int result = 0;
- int retry = 0;
- int header_1_ok = 0;
- int header_2_ok = 0;
-
- do {
- if (!header_1_ok) {
- result = update_header_segment(header_segment_1, buffer);
- if (result < 0) {
- continue;
- }
- header_1_ok = 1;
- }
- if (!header_2_ok) {
- result = update_header_segment(header_segment_2, buffer);
- if (result < 0) {
- continue;
- }
- header_2_ok = 1;
- }
- } while (result < 0 && retry++ < 3);
- if (result < 0) {
- if (!header_1_ok) {
- TRACE(1, "update of first header segment failed");
- }
- if (!header_2_ok) {
- TRACE(1, "update of second header segment failed");
- }
- result = -EIO;
- }
- TRACE_EXIT;
- return result;
-}
-
-int ftape_update_header_segments(byte * buffer, int update)
-{
- TRACE_FUN(5, "ftape_update_header_segments");
- int result = 0;
- int dummy;
- int header_changed = 1;
-
- if (ftape_state == writing) {
- result = loop_until_writes_done();
- }
- if (read_only) {
- result = 0; /* exit and fake success */
- TRACE(4, "Tape set read-only: no update");
- } else if (result >= 0) {
- result = ftape_abort_operation();
- if (result >= 0) {
- if (buffer == NULL) {
- if (bad_sector_map_changed || failed_sector_log_changed) {
- ftape_seek_to_bot(); /* prevents extra rewind */
- buffer = deblock_buffer;
- result = read_segment(used_header_segment, buffer, &dummy, 0);
- if (result < 0) {
- TRACE_EXIT;
- return result;
- }
- }
- header_changed = 0;
- }
- if (update) {
- if (bad_sector_map_changed) {
- store_bad_sector_map(buffer);
- header_changed = 1;
- }
- if (failed_sector_log_changed) {
- update_failed_sector_log(buffer);
- header_changed = 1;
- }
- }
- if (header_changed) {
- ftape_seek_to_bot(); /* prevents extra rewind */
- result = ftape_write_header_segments(buffer);
- }
- }
- }
- TRACE_EXIT;
- return result;
-}
-
-int ftape_flush_buffers(void)
-{
- TRACE_FUN(5, "ftape_flush_buffers");
- int result;
- int pad_count;
- int data_remaining;
- static int active = 0;
-
- if (active) {
- TRACE(5, "nested call, abort");
- TRACE_EXIT;
- return 0;
- }
- active = 1;
- TRACEi(5, "entered, ftape_state =", ftape_state);
- if (ftape_state != writing && !need_flush) {
- active = 0;
- TRACE(5, "no need for flush");
- TRACE_EXIT;
- return 0;
- }
- data_remaining = buf_pos_wr;
- buf_pos_wr = 0; /* prevent further writes if this fails */
- TRACE(5, "flushing write buffers");
- if (last_write_failed) {
- ftape_zap_write_buffers();
- active = 0;
- TRACE_EXIT;
- return write_protected ? -EROFS : -EIO;
- }
- /*
- * If there is any data not written to tape yet, append zero's
- * up to the end of the sector. Then write the segment(s) to tape.
- */
- if (data_remaining > 0) {
- int written;
-
- do {
- TRACEi(4, "remaining in buffer:", data_remaining);
- pad_count = sizeof(deblock_buffer) - data_remaining;
- TRACEi(7, "flush, padding count:", pad_count);
- memset(deblock_buffer + data_remaining, 0, pad_count); /* pad buffer */
- result = _write_segment(ftape_seg_pos, deblock_buffer, 1);
- if (result < 0) {
- if (result != -ENOSPC) {
- last_write_failed = 1;
- }
- active = 0;
- TRACE_EXIT;
- return result;
- }
- written = result;
- clear_eof_mark_if_set(ftape_seg_pos, written);
- TRACEi(7, "flush, moved out buffer:", written);
- if (written > 0) {
- data_remaining -= written;
- if (data_remaining > 0) {
- /* Need another segment for remaining data, move the remainder
- * to the beginning of the buffer
- */
- memmove(deblock_buffer, deblock_buffer + written, data_remaining);
- }
- }
- ++ftape_seg_pos;
- } while (data_remaining > 0);
- /* Data written to last segment == data_remaining + written
- * value is in range [1..29K].
- */
- TRACEx2(4, "last write: %d, netto pad-count: %d",
- data_remaining + written, -data_remaining);
- if (-1024 < data_remaining && data_remaining <= 0) {
- /* Last sector of segment was used for data, so put eof mark
- * in next segment and position at second file mark.
- */
- if (ftape_weof(2, ftape_seg_pos, 1) >= 0) {
- ++ftape_seg_pos; /* position between file marks */
- }
- } else {
- /* Put eof mark in previous segment after data and position
- * at second file mark.
- */
- ftape_weof(2, ftape_seg_pos - 1, 1 +
- ((SECTOR_SIZE - 1 + result + data_remaining) / SECTOR_SIZE));
- }
- } else {
- TRACE(7, "deblock_buffer empty");
- if (ftape_weof(2, ftape_seg_pos, 1) >= 0) {
- ++ftape_seg_pos; /* position between file marks */
- }
- start_writing(WRITE_MULTI);
- }
- TRACE(7, "waiting");
- result = loop_until_writes_done();
- if (result < 0) {
- TRACE(1, "flush buffers failed");
- }
- ftape_state = idle;
- last_write_failed = 0;
- need_flush = 0;
- active = 0;
- TRACE_EXIT;
- return result;
-}
-
-int _ftape_write(const char *buff, int req_len)
-{
- TRACE_FUN(5, "_ftape_write");
- int result = 0;
- int cnt;
- int written = 0;
-
- if (write_protected) {
- TRACE(1, "error: cartridge write protected");
- last_write_failed = 1;
- result = -EROFS;
- } else if (ftape_offline || !formatted || no_tape) {
- result = -EIO;
- } else if (first_data_segment == -1) {
- /*
- * If we haven't read the header segment yet, do it now.
- * This will verify the configuration, get the eof markers
- * and the bad sector table.
- * We'll use the deblock buffer for scratch.
- */
- result = read_header_segment(deblock_buffer);
- if (result >= 0 && ftape_seg_pos > ftape_last_segment.id) {
- result = -ENOSPC; /* full is full */
- }
- }
- if (result < 0) {
- TRACE_EXIT;
- return result;
- }
- /*
- * This part writes data blocks to tape until the
- * requested amount is written.
- * The data will go in a buffer until it's enough
- * for a segment without bad sectors. Then we'll write
- * that segment to tape.
- * The bytes written will be removed from the buffer
- * and the process is repeated until there is less
- * than one segment to write left in the buffer.
- */
- while (req_len > 0) {
- int space_left = sizeof(deblock_buffer) - buf_pos_wr;
-
- TRACEi(7, "remaining req_len:", req_len);
- TRACEi(7, " buf_pos:", buf_pos_wr);
- cnt = (req_len < space_left) ? req_len : space_left;
- if (cnt > 0) {
- result = verify_area(VERIFY_READ, buff, cnt);
- if (result) {
- TRACE(1, "verify_area failed");
- last_write_failed = 1;
- TRACE_EXIT;
- return result;
- }
- copy_from_user(deblock_buffer + buf_pos_wr, buff, cnt);
- buff += cnt;
- req_len -= cnt;
- buf_pos_wr += cnt;
- }
- TRACEi(7, "moved into blocking buffer:", cnt);
- while (buf_pos_wr >= sizeof(deblock_buffer)) {
- /* If this is the last buffer to be written, let flush handle it.
- */
- if (ftape_seg_pos >= ftape_last_segment.id) {
- TRACEi(7, "remaining in blocking buffer:", buf_pos_wr);
- TRACEi(7, "just written bytes:", written + cnt);
- TRACE_EXIT;
- return written + cnt;
- }
- /* Got one full buffer, write it to disk
- */
- result = _write_segment(ftape_seg_pos, deblock_buffer, 0);
- TRACEi(5, "_write_segment result =", result);
- if (result < 0) {
- if (result == -EAGAIN) {
- TRACE(5, "retry...");
- continue; /* failed, retry same segment */
- }
- last_write_failed = 1;
- TRACE_EXIT;
- return result;
- } else {
- clear_eof_mark_if_set(ftape_seg_pos, result);
- }
- if (result > 0 && result < buf_pos_wr) {
- /* Partial write: move remainder in lower part of buffer
- */
- memmove(deblock_buffer, deblock_buffer + result, buf_pos_wr - result);
- }
- TRACEi(7, "moved out of blocking buffer:", result);
- buf_pos_wr -= result; /* remainder */
- ++ftape_seg_pos;
- /* Allow us to escape from this loop with a signal !
- */
- if (current->signal & _DONT_BLOCK) {
- TRACE(2, "interrupted by signal");
- last_write_failed = 1;
- TRACE_EXIT;
- return -EINTR; /* is this the right return value ? */
- }
- }
- written += cnt;
- }
- TRACEi(7, "remaining in blocking buffer:", buf_pos_wr);
- TRACEi(7, "just written bytes:", written);
- last_write_failed = 0;
- if (!need_flush && written > 0) {
- need_flush = 1;
- }
- TRACE_EXIT;
- return written; /* bytes written */
-}
-
-int ftape_fix(void)
-{
- TRACE_FUN(5, "ftape_fix");
- int result = 0;
- int dummy;
- int status;
-
- if (write_protected) {
- result = -EROFS;
- } else {
- /* This will copy header segment 2 to header segment 1
- * Spares us a tape format operation if header 2 is still good.
- */
- header_segment_1 = 0;
- header_segment_2 = 1;
- first_data_segment = 2;
- result = read_segment(header_segment_2, scratch_buffer, &dummy, 0);
- result = ftape_ready_wait(timeout.pause, &status);
- result = ftape_write_header_segments(scratch_buffer);
- }
- TRACE_EXIT;
- return result;
-}