Example #1
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;
}
Example #2
0
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;
}
/*      Write to tape device
 */
static int ftape_write(struct inode *ino, struct file *fp, const char *buff, int req_len)
{
	TRACE_FUN(8, "ftape_write");
	int result = -EIO;
	int old_sigmask;

	TRACEi(5, "called with count:", req_len);
	if (!busy_flag || MINOR(ino->i_rdev) != ftape_unit || ftape_failure) {
		TRACE(1, "failed: not busy, failure or wrong unit");
		TRACE_EXIT;
		return -EIO;
	}
	old_sigmask = current->blocked;		/* save mask */
	current->blocked = _BLOCK_ALL;
	result = _ftape_write(buff, req_len);
	TRACEi(7, "return with count:", result);
	current->blocked = old_sigmask;		/* restore mask */
	TRACE_EXIT;
	return result;
}
/*      Open ftape device
 */
static int ftape_open(struct inode *ino, struct file *filep)
{
	TRACE_FUN(4, "ftape_open");
	int result;
	MOD_INC_USE_COUNT;	/* lock module in memory */

	TRACEi(5, "called for minor", MINOR(ino->i_rdev));
	if (busy_flag) {
		TRACE(1, "failed: already busy");
		MOD_DEC_USE_COUNT;	/* unlock module in memory */
		TRACE_EXIT;
		return -EBUSY;
	}
	if ((MINOR(ino->i_rdev) & ~FTAPE_NO_REWIND) > 3) {
		TRACE(1, "failed: illegal unit nr");
		MOD_DEC_USE_COUNT;	/* unlock module in memory */
		TRACE_EXIT;
		return -ENXIO;
	}
	if (ftape_unit == -1 || FTAPE_UNIT != (MINOR(ino->i_rdev) & 3)) {
		/*  Other selection than last time
		 */
		ftape_init_driver();
	}
	ftape_unit = MINOR(ino->i_rdev);
	ftape_failure = 0;	/* allow tape operations */
	old_sigmask = current->blocked;
	current->blocked = _BLOCK_ALL;
	fdc_save_drive_specs();	/* save Drive Specification regs on i82078-1's */
	result = _ftape_open();
	if (result < 0) {
		TRACE(1, "_ftape_open failed");
		current->blocked = old_sigmask;		/* restore mask */
		MOD_DEC_USE_COUNT;	/* unlock module in memory */
		TRACE_EXIT;
		return result;
	} else {
		busy_flag = 1;
		/*  Mask signals that will disturb proper operation of the
		 *  program that is calling.
		 */
		current->blocked = old_sigmask | _DO_BLOCK;
		TRACE_EXIT;
		return 0;
	}
}
Example #5
0
/*  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;
}
Example #6
0
/*      IOCTL routine called by kernel-interface code
 */
int _ftape_ioctl(unsigned int command, void *arg)
{
	TRACE_FUN(8, "ftape_ioctl");
	int result = EINVAL;
	union {
		struct mtop mtop;
		struct mtget mtget;
	} krnl_arg;
	int arg_size = (command & IOCSIZE_MASK) >> IOCSIZE_SHIFT;

	/* This check will only catch arguments that are too large !
	 */
	if ((command & IOC_INOUT) && arg_size > sizeof(krnl_arg)) {
		TRACEi(1, "bad argument size:", arg_size);
		TRACE_EXIT;
		return -EINVAL;
	}
	if (command & IOC_IN) {
		int error = verify_area(VERIFY_READ, arg, arg_size);
		if (error) {
			TRACE_EXIT;
			return error;
		}
		memcpy_fromfs(&krnl_arg.mtop, arg, arg_size);
	}
	TRACEx1(5, "called with ioctl command: 0x%08x", command);
	switch (command) {
		/* cpio compatibility
		 * mtrasx and mtreset are mt extension by Hennus Bergman
		 * mtseek and mttell are mt extension by eddy olk
		 */
	case MTIOCTOP:
		TRACEx1(5, "calling MTIOCTOP command: 0x%08x", krnl_arg.mtop.mt_op);
		switch (krnl_arg.mtop.mt_op) {
		case MTNOP:
			/* gnu mt calls MTNOP before MTIOCGET to set status */
			result = 0;
			break;
		case MTRESET:
			result = ftape_reset_drive();
			init_drive_needed = 1;
			if (result < 0 || ftape_offline) {
				break;
			}
			result = ftape_seek_to_bot();
			ftape_reset_position();
			break;
		case MTREW:
		case MTOFFL:
			if (ftape_offline) {
				result = -EIO;
				break;
			}
			ftape_flush_buffers();
			ftape_update_header_segments(NULL, 1);
			result = ftape_seek_to_bot();
			ftape_reset_position();
			if (krnl_arg.mtop.mt_op == MTOFFL) {
				going_offline = 1;
				TRACE(4, "Putting tape drive offline");
			}
			result = 0;
			break;
		case MTRETEN:
			if (ftape_offline) {
				result = -EIO;
				break;
			}
			result = ftape_seek_to_eot();
			if (result >= 0) {
				result = ftape_seek_to_bot();
			}
			ftape_reset_position();
			break;
		case MTERASE:
			if (ftape_offline) {
				result = -EIO;
				break;
			}
			result = ftape_erase();
			break;
		case MTEOM:
			if (ftape_offline) {
				result = -EIO;
				break;
			}
			result = ftape_seek_eom();
			break;
		case MTFSFM:
			if (ftape_offline) {
				result = -EIO;
				break;
			}
			eof_mark = 1;	/* position ready to extend */
		case MTFSF:
			if (ftape_offline) {
				result = -EIO;
				break;
			}
			result = ftape_seek_eof(krnl_arg.mtop.mt_count);
			break;
		case MTBSFM:
			if (ftape_offline) {
				result = -EIO;
				break;
			}
			eof_mark = 1;	/* position ready to extend */
		case MTBSF:
			if (ftape_offline) {
				result = -EIO;
				break;
			}
			result = ftape_seek_eof(-krnl_arg.mtop.mt_count);
			break;
		case MTFSR:
			if (ftape_offline) {
				result = -EIO;
				break;
			}
			tracing = krnl_arg.mtop.mt_count;
			TRACEx1(2, "tracing set to %d", tracing);
			result = 0;
			break;
		case MTBSR:
			if (ftape_offline) {
				result = -EIO;
				break;
			}
#if 0
			result = ftape_fix();
#else
			result = 0;
#endif
			break;
		case MTWEOF:
			if (ftape_offline) {
				result = -EIO;
				break;
			}
			result = ftape_weof(krnl_arg.mtop.mt_count, ftape_seg_pos, 1);
			if (result >= 0) {
				ftape_seg_pos += krnl_arg.mtop.mt_count - 1;
			}
			break;
			/* MTRASx and MTRESET are mt extension by Hennus Bergman
			 */
		case MTRAS1:
		case MTRAS2:
		case MTRAS3:
		case MTSEEK:
		case MTTELL:
		default:
			TRACEi(1, "MTIOCTOP sub-command not implemented:", krnl_arg.mtop.mt_op);
			result = -EIO;
			break;
		}
		break;
	case MTIOCGET:
		krnl_arg.mtget.mt_type = drive_type.vendor_id + 0x800000;
		krnl_arg.mtget.mt_resid = 0;	/* not implemented */
		krnl_arg.mtget.mt_dsreg = 0;	/* status register */
		krnl_arg.mtget.mt_gstat =	/* device independent status */
		    ((ftape_offline) ? 0 : GMT_ONLINE(-1L)) |
		    ((write_protected) ? GMT_WR_PROT(-1L) : 0) |
		    ((no_tape) ? GMT_DR_OPEN(-1L) : 0);
		krnl_arg.mtget.mt_erreg = ftape_last_error;	/* error register */
		result = ftape_file_no(&krnl_arg.mtget.mt_fileno,
				       &krnl_arg.mtget.mt_blkno);
		break;
	case MTIOCPOS:
		TRACE(5, "Mag tape ioctl command: MTIOCPOS");
		TRACE(1, "MTIOCPOS command not implemented");
		break;
	default:
		result = -EINVAL;
		break;
	}
	if (command & IOC_OUT) {
		int error = verify_area(VERIFY_WRITE, arg, arg_size);
		if (error) {
			TRACE_EXIT;
			return error;
		}
		memcpy_tofs(arg, &krnl_arg, arg_size);
	}
	TRACE_EXIT;
	return result;
}
Example #7
0
/*      RELEASE routine called by kernel-interface code
 */
int _ftape_close(void)
{
	TRACE_FUN(8, "_ftape_close");
	int result = 0;
	int last_segment = 0;

	if (!ftape_offline) {
		result = ftape_flush_buffers();
		last_segment = ftape_seg_pos - 1;
		if (!(ftape_unit & FTAPE_NO_REWIND)) {
			if (result >= 0) {
				result = ftape_update_header_segments(NULL, 1);
				if (result < 0) {
					TRACE(1, "error: update of header segments failed");
				}
			} else {
				TRACE(1, "error: unable to update header segments");
			}
		}
		ftape_abort_operation();
		if (!(ftape_unit & FTAPE_NO_REWIND)) {
			if (!no_tape) {
				TRACE(5, "rewinding tape");
				result = ftape_seek_to_bot();
			}
			ftape_reset_position();
			ftape_zap_read_buffers();
			ftape_zap_write_buffers();
		}
	}
	ftape_detach_drive();
	fdc_uninit();
	if (history.used) {
		TRACE(3, "== Non-fatal errors this run: ==");
		TRACE(3, "fdc isr statistics:");
		TRACEi(3, " id_am_errors     :", history.id_am_errors);
		TRACEi(3, " id_crc_errors    :", history.id_crc_errors);
		TRACEi(3, " data_am_errors   :", history.data_am_errors);
		TRACEi(3, " data_crc_errors  :", history.data_crc_errors);
		TRACEi(3, " overrun_errors   :", history.overrun_errors);
		TRACEi(3, " no_data_errors   :", history.no_data_errors);
		TRACEi(3, " retries          :", history.retries);
		if (history.used & 1) {
			TRACE(3, "ecc statistics:");
			TRACEi(3, " crc_errors       :", history.crc_errors);
			TRACEi(3, " crc_failures     :", history.crc_failures);
			TRACEi(3, " ecc_failures     :", history.ecc_failures);
			TRACEi(3, " sectors corrected:", history.corrected);
		}
		TRACEx2(3, "media defects     : %d%s", history.defects,
			history.defects ? " !!!" : "");
		TRACEi(3, "repositions       :", history.rewinds);
		TRACEi(3, "last segment      :", last_segment);
	}
	if (going_offline) {
		going_offline = 0;
		ftape_offline = 1;
	}
	TRACE_EXIT;
	return result;
}
Example #8
0
int ftape_get_drive_status(int *new_tape, int *no_tape, int *wp_tape)
{
	TRACE_FUN(5, "ftape_get_drive_status");
	int result;
	int status;

	*no_tape =
	    *wp_tape = 0;
	/*    Tape drive is activated now.
	 *    First clear error status if present.
	 */
	do {
		result = ftape_ready_wait(timeout.reset, &status);
		if (result < 0) {
			if (result == -ETIME) {
				TRACE(1, "ftape_ready_wait timeout");
			} else if (result == -EINTR) {
				TRACE(1, "ftape_ready_wait aborted");
			} else {
				TRACE(1, "ftape_ready_wait failed");
			}
			result = -EIO;
			break;
		}
		/*  Clear error condition (drive is ready !)
		 */
		if (status & QIC_STATUS_ERROR) {
			int error;
			int command;

			TRACE(1, "error status set");
			result = ftape_report_error(&error, &command, 1);
			if (result < 0) {
				TRACEi(1, "report_error_code failed:", result);
				ftape_reset_drive();	/* hope it's working next time */
				init_drive_needed = 1;
				result = -EIO;
				break;
			} else if (error != 0) {
				TRACEi(4, "error code   :", error);
				TRACEi(4, "error command:", command);
			}
		}
		if (status & QIC_STATUS_NEW_CARTRIDGE) {
			int error;
			int command;
			int old_tracing = tracing;

			/*  Undocumented feature: Must clear (not present!) error 
			 *  here or we'll fail later.
			 */
			tracing = 0;
			ftape_report_error(&error, &command, 1);
			tracing = old_tracing;
			TRACE(3, "status: new cartridge");
			*new_tape = 1;
		}
	} while (status & QIC_STATUS_ERROR);

	*no_tape = !(status & QIC_STATUS_CARTRIDGE_PRESENT);
	*wp_tape = (status & QIC_STATUS_WRITE_PROTECT);
	if (*no_tape) {
		TRACE(1, "no cartridge present");
	} else {
		if (*wp_tape) {
			TRACE(2, "Write protected cartridge");
		}
	}
	TRACE_EXIT;
	return result;
}
Example #9
0
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 */
}
Example #10
0
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;
}
Example #11
0
/*      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;
}