URL
https://opencores.org/ocsvn/or1k/or1k/trunk
Subversion Repositories or1k
[/] [or1k/] [trunk/] [rc203soc/] [sw/] [uClinux/] [drivers/] [char/] [ftape/] [ftape-write.c] - Rev 1777
Go to most recent revision | Compare with Previous | Blame | View Log
/* * 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/marcus/revision_ctrl_test/oc_cvs/cvs/or1k/rc203soc/sw/uClinux/drivers/char/ftape/ftape-write.c,v $ $Author: jcastillo $ * $Revision: 1.1 $ $Date: 2005-12-20 10:16:52 $ $State: Exp $ * * 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/segment.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; } memcpy_fromfs(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; }
Go to most recent revision | Compare with Previous | Blame | View Log