906 lines
23 KiB
C
906 lines
23 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1993 - Colorado Memory Systems, Inc.
|
|||
|
All Rights Reserved
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
init.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This section reads the QIC40 Header and initalizes some
|
|||
|
of the Context for the rest of the system.
|
|||
|
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
//
|
|||
|
// Includes
|
|||
|
//
|
|||
|
|
|||
|
#include <ntddk.h>
|
|||
|
#include <ntddtape.h>
|
|||
|
#include "common.h"
|
|||
|
#include "q117.h"
|
|||
|
#include "protos.h"
|
|||
|
|
|||
|
#define FCT_ID 0x010b
|
|||
|
|
|||
|
dStatus
|
|||
|
q117FixBadSectorMapPtr(
|
|||
|
IN OUT PQ117_CONTEXT Context,
|
|||
|
IN OUT PTAPE_HEADER header
|
|||
|
);
|
|||
|
|
|||
|
dBoolean
|
|||
|
ValidateBadSectorMap(
|
|||
|
IN PTAPE_HEADER HeaderPointer,
|
|||
|
IN int StartOffset,
|
|||
|
IN dBoolean allow_null_list
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
dStatus
|
|||
|
q117LoadTape (
|
|||
|
IN OUT PTAPE_HEADER *HeaderPointer,
|
|||
|
IN OUT PQ117_CONTEXT Context,
|
|||
|
IN dUByte *driver_format_code
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Initialize the tape interface for read or write
|
|||
|
This routine reads the bad sector map into memory.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
HeaderPointer -
|
|||
|
|
|||
|
Context - Context of the driver
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
dStatus ret; // Return value from other routines called or
|
|||
|
// the status of block 1 of tracks 1-5 when read in.
|
|||
|
PTAPE_HEADER hdr;
|
|||
|
unsigned badSize;
|
|||
|
IO_REQUEST ioreq;
|
|||
|
|
|||
|
|
|||
|
q117ClearQueue(Context);
|
|||
|
|
|||
|
//
|
|||
|
// Is defined later at ReadVolumeEntry().
|
|||
|
//
|
|||
|
|
|||
|
Context->CurrentOperation.EndOfUsedTape=0;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// this memset allows IssIOReq to find the first two
|
|||
|
// good segments on the tape
|
|||
|
//
|
|||
|
|
|||
|
RtlZeroMemory(
|
|||
|
Context->CurrentTape.BadMapPtr,
|
|||
|
Context->CurrentTape.BadSectorMapSize);
|
|||
|
|
|||
|
Context->CurrentTape.CurBadListIndex = 0;
|
|||
|
|
|||
|
|
|||
|
if (!(ret = q117ReadHeaderSegment(&hdr, Context))) {
|
|||
|
|
|||
|
if (HeaderPointer) {
|
|||
|
|
|||
|
*HeaderPointer = (PVOID)hdr;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
if (Context->CurrentTape.BadSectorMapFormat == BadMap4ByteArray) {
|
|||
|
|
|||
|
badSize = sizeof(LONG) * (hdr->LastSegment+1);
|
|||
|
|
|||
|
if (badSize > Context->CurrentTape.BadSectorMapSize) {
|
|||
|
|
|||
|
ret = ERROR_ENCODE(ERR_BAD_TAPE, FCT_ID, 1);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Now, if the driver needs to know how big the tape is,
|
|||
|
// pass the information from the tape header.
|
|||
|
//
|
|||
|
switch (*driver_format_code) {
|
|||
|
case QIC_XLFORMAT:
|
|||
|
case QICFLX_FORMAT:
|
|||
|
// If no problems, then change the tape length to the proper
|
|||
|
// size. (this is needed because the drive can not distinguish
|
|||
|
// between a standard 205 ft. tape and a 425 ft. tape. Since
|
|||
|
// we read a valid header
|
|||
|
// Also needed for flex formats
|
|||
|
|
|||
|
if (hdr->VendorUnique.correct_name.unused2 == 0 &&
|
|||
|
hdr->VendorUnique.correct_name.TrackSeg != 0) {
|
|||
|
|
|||
|
// now, set the number of segments that was recorded when
|
|||
|
// the tape was formatted.
|
|||
|
ioreq.x.ioTapeParms.segments_per_track = hdr->VendorUnique.correct_name.TrackSeg;
|
|||
|
|
|||
|
} else {
|
|||
|
// This is an old tape that does not have the
|
|||
|
// number of segments in the header. Therefore, it must
|
|||
|
// be a 205ft cart.
|
|||
|
ioreq.x.ioTapeParms.segments_per_track = 68;
|
|||
|
CheckedDump(QIC117INFO,("NOTE: this is an old tape without segment info - Assuming 68 segments\n"));
|
|||
|
}
|
|||
|
CheckedDump(QIC117INFO,("Informing the driver that the tape has 0x%x segments\n",ioreq.x.ioTapeParms.segments_per_track));
|
|||
|
ret = q117DoCmd(&ioreq, CMD_SET_TAPE_PARMS, NULL, Context);
|
|||
|
|
|||
|
// Now, change the format code from the old value, to the
|
|||
|
// correct (new) value based on the tape header information.
|
|||
|
CheckedDump(QIC117INFO,("Changed format from %d to %d\n",*driver_format_code, ioreq.x.ioTapeParms.tape_cfg.tape_format_code));
|
|||
|
*driver_format_code = ioreq.x.ioTapeParms.tape_cfg.tape_format_code;
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
if (ret == ERR_NO_ERR) {
|
|||
|
|
|||
|
|
|||
|
// Get the pointer to the bad sector map
|
|||
|
ret = q117FixBadSectorMapPtr(Context, hdr);
|
|||
|
|
|||
|
//
|
|||
|
// Copy over the header (and bad sector map, etc.)
|
|||
|
//
|
|||
|
RtlMoveMemory(
|
|||
|
Context->CurrentTape.TapeHeader,
|
|||
|
hdr,
|
|||
|
sizeof(*Context->CurrentTape.TapeHeader) );
|
|||
|
|
|||
|
|
|||
|
#if DBG
|
|||
|
{
|
|||
|
ULONG bits,tmp,i,seg;
|
|||
|
ULONG *ptr, *ptrbase, *newbase, ptrsize,ptrused;
|
|||
|
|
|||
|
ptrsize = 2048;
|
|||
|
ptrused = 0;
|
|||
|
ptrbase = ptr = ExAllocatePool(PagedPool, ptrsize*sizeof(*ptr));
|
|||
|
|
|||
|
CheckedDump(QIC117SHOWBSM,("Bad Sector map:\n"));
|
|||
|
for (seg=0;seg<hdr->LastSegment;++seg) {
|
|||
|
bits = q117ReadBadSectorList(Context, (SEGMENT)seg);
|
|||
|
if (bits) {
|
|||
|
tmp = 1;
|
|||
|
|
|||
|
//
|
|||
|
// Loop through checking all the bits
|
|||
|
//
|
|||
|
for (i = 0; i < BLOCKS_PER_SEGMENT; ++i) {
|
|||
|
|
|||
|
if ( bits & tmp ) {
|
|||
|
if (ptrused == ptrsize) {
|
|||
|
newbase = ExAllocatePool(PagedPool, (ptrsize+512)*sizeof(*ptr));
|
|||
|
RtlMoveMemory(newbase, ptrbase, ptrsize*sizeof(*ptr));
|
|||
|
ExFreePool(ptrbase);
|
|||
|
ptr = newbase + (ptr-ptrbase);
|
|||
|
ptrbase = newbase;
|
|||
|
ptrsize += 512;
|
|||
|
}
|
|||
|
++ptrused;
|
|||
|
*ptr++=i+(seg*BLOCKS_PER_SEGMENT);
|
|||
|
CheckedDump(QIC117SHOWBSM,("%x ",i+(seg*BLOCKS_PER_SEGMENT)));
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// shift left one (tmp *= 2 optimized)
|
|||
|
//
|
|||
|
tmp += tmp;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
CheckedDump(QIC117SHOWBSM,("\n *** End of Bad Sector map\n"));
|
|||
|
|
|||
|
RtlWriteRegistryValue(
|
|||
|
RTL_REGISTRY_DEVICEMAP,
|
|||
|
L"Tape\\Unit 0",
|
|||
|
L"BadSectorMap",
|
|||
|
REG_BINARY,
|
|||
|
ptrbase,
|
|||
|
ptrused*sizeof(*ptrbase));
|
|||
|
|
|||
|
RtlWriteRegistryValue(
|
|||
|
RTL_REGISTRY_DEVICEMAP,
|
|||
|
L"Tape\\Unit 0",
|
|||
|
L"BadSectorMapFormat",
|
|||
|
REG_DWORD,
|
|||
|
&Context->CurrentTape.BadSectorMapFormat,
|
|||
|
4);
|
|||
|
|
|||
|
RtlWriteRegistryValue(
|
|||
|
RTL_REGISTRY_DEVICEMAP,
|
|||
|
L"Tape\\Unit 0",
|
|||
|
L"TapeHeader",
|
|||
|
REG_BINARY,
|
|||
|
Context->CurrentTape.TapeHeader,
|
|||
|
3*1024);
|
|||
|
|
|||
|
{
|
|||
|
int offset;
|
|||
|
|
|||
|
offset = (int)Context->CurrentTape.BadMapPtr - (int)Context->CurrentTape.TapeHeader;
|
|||
|
|
|||
|
RtlWriteRegistryValue(
|
|||
|
RTL_REGISTRY_DEVICEMAP,
|
|||
|
L"Tape\\Unit 0",
|
|||
|
L"BadMapOffset",
|
|||
|
REG_DWORD,
|
|||
|
&offset,
|
|||
|
4);
|
|||
|
}
|
|||
|
|
|||
|
ExFreePool(ptrbase);
|
|||
|
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
//
|
|||
|
// if any bad sectors in the tape directory then don't
|
|||
|
// read ahead
|
|||
|
//
|
|||
|
|
|||
|
if (q117CountBits(Context, Context->CurrentTape.VolumeSegment, 0l)) {
|
|||
|
|
|||
|
Context->tapedir = (PIO_REQUEST)NULL;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// set global variables
|
|||
|
//
|
|||
|
|
|||
|
Context->CurrentTape.LastUsedSegment = Context->CurrentTape.VolumeSegment;
|
|||
|
|
|||
|
//
|
|||
|
// Last data block that can be written to on tape
|
|||
|
//
|
|||
|
|
|||
|
Context->CurrentTape.LastSegment = hdr->LastSegment;
|
|||
|
Context->CurrentTape.MaximumVolumes = (USHORT) (
|
|||
|
q117GoodDataBytes(hdr->FirstSegment,Context) /
|
|||
|
sizeof(VOLUME_TABLE_ENTRY));
|
|||
|
|
|||
|
//
|
|||
|
// get number of bad sectors on tape and set CurrentTape.LastSegment to
|
|||
|
// last good block
|
|||
|
//
|
|||
|
|
|||
|
q117GetBadSectors(Context);
|
|||
|
}
|
|||
|
}
|
|||
|
return(ret);
|
|||
|
}
|
|||
|
|
|||
|
dStatus q117FixBadSectorMapPtr(
|
|||
|
IN OUT PQ117_CONTEXT Context,
|
|||
|
IN OUT PTAPE_HEADER header
|
|||
|
)
|
|||
|
|
|||
|
{
|
|||
|
int badmap;
|
|||
|
dStatus ret;
|
|||
|
|
|||
|
//
|
|||
|
// Point to extra area. This information (union of several structurs)
|
|||
|
// determines what the bad sector map is in several versions of QIC
|
|||
|
// tape formats
|
|||
|
//
|
|||
|
Context->CurrentTape.BadSectorMapFormat = BadMapFormatUnknown;
|
|||
|
ret = ERR_NO_ERR;
|
|||
|
|
|||
|
// This better be correct for the next call to work
|
|||
|
ASSERT(header->FormatCode == Context->CurrentTape.TapeFormatCode);
|
|||
|
|
|||
|
badmap = q117SelectBSMLocation(Context);
|
|||
|
ASSERT(badmap != 0);
|
|||
|
|
|||
|
if (badmap == 0) {
|
|||
|
|
|||
|
ret = ERROR_ENCODE(ERR_BAD_TAPE, FCT_ID, 4);
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// For a 3 byte list format, make sure we have an assending
|
|||
|
// list of bad sectors.
|
|||
|
//
|
|||
|
if (Context->CurrentTape.BadSectorMapFormat == BadMap3ByteList) {
|
|||
|
|
|||
|
if (ValidateBadSectorMap(header,badmap,TRUE) &&
|
|||
|
Context->CurrentTape.BadSectorMapFormat != BadMapFormatUnknown) {
|
|||
|
|
|||
|
ret = ERR_NO_ERR;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
ret = ERROR_ENCODE(ERR_BAD_TAPE, FCT_ID, 3);
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
#if DBG
|
|||
|
{
|
|||
|
static char *btype[] = { "BadMap3ByteList",
|
|||
|
"BadMap8ByteList",
|
|||
|
"BadMap4ByteArray",
|
|||
|
"BadMapFormatUnknown" };
|
|||
|
|
|||
|
CheckedDump(QIC117INFO,("Found a bad sector map at offset %x of type %s\n",badmap,btype[Context->CurrentTape.BadSectorMapFormat]));
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
return ret;
|
|||
|
}
|
|||
|
|
|||
|
int
|
|||
|
q117SelectBSMLocation(
|
|||
|
IN OUT PQ117_CONTEXT Context
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
For pre-format screwup tapes, and all tapes created by NT,
|
|||
|
find the correct location for the bad sector map based
|
|||
|
on the drive type, and the format code being used
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Context - Context of the driver
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
int badmap;
|
|||
|
|
|||
|
Context->CurrentTape.BadSectorMapFormat = BadMapFormatUnknown;
|
|||
|
badmap = 0; //default to a detectable error condition
|
|||
|
|
|||
|
//
|
|||
|
// OK, this tape does not have a rev level (format sub code) so we must
|
|||
|
// determine what format the bad sector map is in.
|
|||
|
//
|
|||
|
// History:
|
|||
|
//
|
|||
|
// QIC80 rev K. Spec format code, and BSM location/format
|
|||
|
//
|
|||
|
// QIC_FORMAT (2) = QIC-80 Rev B & later, 205 foot, and 307.5 foot tape.
|
|||
|
// BSM = 1024, BadMap4ByteArray
|
|||
|
//
|
|||
|
// QICEST_FORMAT(3) = QIC-80 Rev B & later, 1,100 foot tape.
|
|||
|
// BSM = 1024, BadMap3ByteList
|
|||
|
//
|
|||
|
// QICFLX_FORMAT(4) = QIC-80 Rev B & later variable format
|
|||
|
// on any tape .250 inch or .315 inch.
|
|||
|
// BSM = 256, BadMap3ByteList
|
|||
|
//
|
|||
|
// QIC_XLFORMAT (5) = QIC-80 Rev B & later, 425 foot
|
|||
|
// BSM = 1024, BadMap4ByteArray
|
|||
|
//
|
|||
|
// QIC80 rev M. Spec format code, and BSM location/format
|
|||
|
|
|||
|
switch (Context->CurrentTape.TapeFormatCode) {
|
|||
|
case QIC_FORMAT:
|
|||
|
case QIC_XLFORMAT:
|
|||
|
badmap = 2048;
|
|||
|
Context->CurrentTape.BadSectorMapFormat = BadMap4ByteArray;
|
|||
|
break;
|
|||
|
|
|||
|
|
|||
|
case QICEST_FORMAT:
|
|||
|
// This is a Pegasus cart (not manufactured)
|
|||
|
badmap = 2048;
|
|||
|
Context->CurrentTape.BadSectorMapFormat = BadMap3ByteList;
|
|||
|
break;
|
|||
|
|
|||
|
case QICFLX_FORMAT:
|
|||
|
// This is a Travan or QIC Wide media, so the bad
|
|||
|
// sector map is at 0x100, and it is
|
|||
|
badmap = 256;
|
|||
|
Context->CurrentTape.BadSectorMapFormat = BadMap3ByteList;
|
|||
|
break;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
// Now, fix up the pointer and size of the bad sector map
|
|||
|
Context->CurrentTape.BadMapPtr = (void *)(((char *)(Context->CurrentTape.TapeHeader))+badmap);
|
|||
|
Context->CurrentTape.BadSectorMapSize = sizeof(struct _TAPE_HEADER)-badmap;
|
|||
|
|
|||
|
return badmap;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
void
|
|||
|
q117GetBadSectors (
|
|||
|
IN OUT PQ117_CONTEXT Context
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Gets the number of bad sectors on the whole tape.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Context - Context of the driver
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
ULONG badBits;
|
|||
|
SEGMENT segment,lastGood;
|
|||
|
|
|||
|
Context->CurrentTape.BadSectors = 0;
|
|||
|
|
|||
|
//
|
|||
|
// count up the bad blocks for status information
|
|||
|
//
|
|||
|
|
|||
|
for ( segment = 0; segment <= Context->CurrentTape.LastSegment; ++segment ) {
|
|||
|
|
|||
|
badBits = q117CountBits(Context, segment, 0l);
|
|||
|
|
|||
|
if (badBits >= BLOCKS_PER_SEGMENT-ECC_BLOCKS_PER_SEGMENT) {
|
|||
|
|
|||
|
badBits = BLOCKS_PER_SEGMENT;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
lastGood = segment;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
Context->CurrentTape.BadSectors += badBits;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// set CurrentTape.LastSegment to last good segment
|
|||
|
//
|
|||
|
|
|||
|
Context->CurrentTape.LastSegment = lastGood;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
dStatus
|
|||
|
q117ReadHeaderSegment (
|
|||
|
OUT PTAPE_HEADER *HeaderPointer,
|
|||
|
IN OUT PQ117_CONTEXT Context
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Reads a QIC40 tape header. This includes reconstructing the header
|
|||
|
using 100% redundancy and Reed-Solomon Error correction.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
HeaderPointer -
|
|||
|
|
|||
|
Context - Context of the driver
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
dStatus ret;
|
|||
|
int i,j;
|
|||
|
BLOCK headerBlock[2];
|
|||
|
LONG headerBlockCount;
|
|||
|
PVOID data;
|
|||
|
PIO_REQUEST ioreq;
|
|||
|
PTAPE_HEADER hdr;
|
|||
|
|
|||
|
//
|
|||
|
// default volume directory not loaded
|
|||
|
//
|
|||
|
|
|||
|
Context->tapedir = NULL;
|
|||
|
|
|||
|
//
|
|||
|
// read first segment from tape
|
|||
|
//
|
|||
|
|
|||
|
Context->CurrentOperation.CurrentSegment=0;
|
|||
|
|
|||
|
Context->CurrentTape.BadSectors = headerBlockCount = 0;
|
|||
|
|
|||
|
//
|
|||
|
// Read the first block of the bad sector map into memory to get the
|
|||
|
// size of the bad sector map.
|
|||
|
//
|
|||
|
|
|||
|
do {
|
|||
|
|
|||
|
while (Context->CurrentOperation.CurrentSegment <=
|
|||
|
Context->CurrentTape.LastSegment &&
|
|||
|
!q117QueueFull(Context)) {
|
|||
|
|
|||
|
if (ret = q117IssIOReq(
|
|||
|
(PVOID)NULL,
|
|||
|
CMD_READ_RAW,
|
|||
|
SEGMENT_TO_BLOCK(Context->CurrentOperation.CurrentSegment),
|
|||
|
NULL,
|
|||
|
Context)) {
|
|||
|
|
|||
|
return(ret);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
++Context->CurrentOperation.CurrentSegment;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
ioreq = q117Dequeue(WaitForItem, Context);
|
|||
|
|
|||
|
ret = ioreq->x.adi_hdr.status;
|
|||
|
|
|||
|
//
|
|||
|
// Allow bad blocks (but not bad marks)
|
|||
|
// as good header segments
|
|||
|
//
|
|||
|
if (ERROR_DECODE(ret) == ERR_BAD_BLOCK_DETECTED || ret == ERR_NO_ERR) {
|
|||
|
|
|||
|
headerBlock[headerBlockCount++] = ioreq->x.ioDeviceIO.starting_sector;
|
|||
|
|
|||
|
if (q117DoCorrect(ioreq->x.adi_hdr.cmd_buffer_ptr,0l,ioreq->x.ioDeviceIO.crc)) {
|
|||
|
|
|||
|
ret = ERROR_ENCODE(ERR_CORRECTION_FAILED, FCT_ID, 1);
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
ret = ERR_NO_ERR;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Re-try (get a new segment) if we failed to correct, got a bad
|
|||
|
// segment, or found a bad mark. All other errors will abort
|
|||
|
// the load.
|
|||
|
//
|
|||
|
if (ret != ERR_NO_ERR && ERROR_DECODE(ret) != ERR_CORRECTION_FAILED
|
|||
|
&& ERROR_DECODE(ret) != ERR_BAD_BLOCK_DETECTED
|
|||
|
&& ERROR_DECODE(ret) != ERR_BAD_MARK_DETECTED) {
|
|||
|
|
|||
|
return(ret);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
} while (ret && headerBlockCount < 2 && (!q117QueueEmpty(Context) ||
|
|||
|
Context->CurrentOperation.CurrentSegment <=
|
|||
|
Context->CurrentTape.LastSegment));
|
|||
|
|
|||
|
//
|
|||
|
// if we did not find both tape header blocks and we got a bad block
|
|||
|
// (or other driver error) return to caller with BadTape tape
|
|||
|
//
|
|||
|
|
|||
|
if (headerBlockCount < 2 && ret) {
|
|||
|
|
|||
|
//
|
|||
|
// All copies of the tape header are bad.
|
|||
|
//
|
|||
|
return ERROR_ENCODE(ERR_BAD_TAPE, FCT_ID, 2);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// if we got a bad block then we need to do 100% redundancy
|
|||
|
// reconstruction
|
|||
|
//
|
|||
|
|
|||
|
if (ERROR_DECODE(ret) == ERR_CORRECTION_FAILED) {
|
|||
|
|
|||
|
ULONG badBits,curentBit;
|
|||
|
|
|||
|
//
|
|||
|
// clear out any pending requests
|
|||
|
//
|
|||
|
|
|||
|
q117ClearQueue(Context);
|
|||
|
|
|||
|
//
|
|||
|
// re-read the first segment (error correction
|
|||
|
// routines corrupt data if they fail)
|
|||
|
//
|
|||
|
|
|||
|
ret = q117IssIOReq((PVOID)NULL, CMD_READ_RAW, headerBlock[0],NULL,Context);
|
|||
|
ioreq = q117Dequeue(WaitForItem,Context);
|
|||
|
badBits = ioreq->x.ioDeviceIO.crc;
|
|||
|
data = ioreq->x.adi_hdr.cmd_buffer_ptr;
|
|||
|
curentBit = 1;
|
|||
|
|
|||
|
for (i = 0; i < BLOCKS_PER_SEGMENT; ++i) {
|
|||
|
|
|||
|
if (badBits & curentBit) {
|
|||
|
|
|||
|
//
|
|||
|
// try to read bad sector out of either header segment
|
|||
|
//
|
|||
|
|
|||
|
for ( j = 0; j < 2; ++j ) {
|
|||
|
|
|||
|
if (ret = q117IssIOReq(
|
|||
|
(UCHAR *)data+(BYTES_PER_SECTOR * i),
|
|||
|
CMD_READ_HEROIC,
|
|||
|
headerBlock[j]+i,
|
|||
|
ioreq->BufferInfo,
|
|||
|
Context)) {
|
|||
|
|
|||
|
return(ret);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
if (q117Dequeue(WaitForItem, Context)->x.adi_hdr.status == ERR_NO_ERR) {
|
|||
|
|
|||
|
//
|
|||
|
// turn off the bit (we just got a good copy
|
|||
|
// of this sector)
|
|||
|
//
|
|||
|
|
|||
|
badBits &= ~curentBit;
|
|||
|
|
|||
|
//
|
|||
|
// don't try the duplicate header segment
|
|||
|
//
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// shift bit left once (optimized)
|
|||
|
//
|
|||
|
|
|||
|
curentBit += curentBit;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// re-try the error correction after 100% correction
|
|||
|
//
|
|||
|
|
|||
|
if (q117DoCorrect(data,0l,badBits)) {
|
|||
|
|
|||
|
return ERROR_ENCODE(ERR_CORRECTION_FAILED, FCT_ID, 2);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
data = ioreq->x.adi_hdr.cmd_buffer_ptr;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// go on and read the volume directory if we have
|
|||
|
// already queued it up
|
|||
|
//
|
|||
|
|
|||
|
Context->CurrentTape.VolumeSegment =
|
|||
|
((PTAPE_HEADER)ioreq->x.adi_hdr.cmd_buffer_ptr)->FirstSegment;
|
|||
|
|
|||
|
if (Context->CurrentTape.VolumeSegment <
|
|||
|
Context->CurrentOperation.CurrentSegment) {
|
|||
|
|
|||
|
do {
|
|||
|
|
|||
|
ioreq = q117Dequeue(WaitForItem, Context);
|
|||
|
|
|||
|
} while (ioreq->x.ioDeviceIO.starting_sector !=
|
|||
|
SEGMENT_TO_BLOCK( Context->CurrentTape.VolumeSegment ) );
|
|||
|
|
|||
|
Context->tapedir = ioreq;
|
|||
|
}
|
|||
|
|
|||
|
q117ClearQueue(Context);
|
|||
|
}
|
|||
|
|
|||
|
*HeaderPointer = hdr = (PTAPE_HEADER)data;
|
|||
|
|
|||
|
//
|
|||
|
// make sure this is a valid QIC40 tape
|
|||
|
//
|
|||
|
|
|||
|
if (hdr->Signature != TapeHeaderSig) {
|
|||
|
|
|||
|
return ERROR_ENCODE(ERR_BAD_SIGNATURE, FCT_ID, 1);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
if ((hdr->FormatCode != QIC_FORMAT) &&
|
|||
|
(hdr->FormatCode != QICEST_FORMAT) &&
|
|||
|
(hdr->FormatCode != QIC_XLFORMAT) &&
|
|||
|
(hdr->FormatCode != QICFLX_FORMAT)) {
|
|||
|
|
|||
|
return ERROR_ENCODE(ERR_UNKNOWN_FORMAT_CODE, FCT_ID, 1);
|
|||
|
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
Context->CurrentTape.TapeFormatCode = hdr->FormatCode;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
if (hdr->HeaderSegment != BLOCK_TO_SEGMENT( headerBlock[0] ) ) {
|
|||
|
|
|||
|
//
|
|||
|
// segment number of header
|
|||
|
//
|
|||
|
|
|||
|
return ERROR_ENCODE(ERR_UNUSABLE_TAPE, FCT_ID, 1);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
if (headerBlockCount > 1 &&
|
|||
|
hdr->DupHeaderSegment != BLOCK_TO_SEGMENT( headerBlock[1] ) ) {
|
|||
|
|
|||
|
//
|
|||
|
// segment number of duplicate header
|
|||
|
//
|
|||
|
|
|||
|
return ERROR_ENCODE(ERR_UNUSABLE_TAPE, FCT_ID, 2);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
Context->CurrentTape.VolumeSegment = hdr->FirstSegment;
|
|||
|
|
|||
|
return(ERR_NO_ERR);
|
|||
|
}
|
|||
|
dBoolean
|
|||
|
ValidateBadSectorMap(
|
|||
|
IN PTAPE_HEADER HeaderPointer,
|
|||
|
IN int StartOffset,
|
|||
|
IN dBoolean allow_null_list
|
|||
|
)
|
|||
|
{
|
|||
|
IN int offset;
|
|||
|
int end;
|
|||
|
int good;
|
|||
|
int previous_value,next_value;
|
|||
|
unsigned char *ptr;
|
|||
|
int start_sector, end_sector;
|
|||
|
|
|||
|
|
|||
|
ptr = (void *)HeaderPointer;
|
|||
|
ptr += StartOffset;
|
|||
|
offset = StartOffset;
|
|||
|
|
|||
|
// Calculate last byte offset (within the header segment)
|
|||
|
end = sizeof(struct _TAPE_HEADER);
|
|||
|
good = TRUE; // Default to OK
|
|||
|
|
|||
|
// Read in one 3byte value
|
|||
|
previous_value = (*(ptr+2) << 16) + *(dUWord *)ptr;
|
|||
|
|
|||
|
// Skip past current entry
|
|||
|
ptr += 3;
|
|||
|
offset += 3;
|
|||
|
|
|||
|
// Calculate 1 based start and ending sectors (inclusive)
|
|||
|
// Start is just after the duplicate header segment
|
|||
|
// and end is the last segment
|
|||
|
|
|||
|
//start_sector = ((HeaderPointer->DupHeaderSegment+1)*BLOCKS_PER_SEGMENT)+1;
|
|||
|
start_sector = 1; // pre-formatted tapes sectors mapped out in the
|
|||
|
// header area, so we can't use above commented-
|
|||
|
// out line
|
|||
|
|
|||
|
end_sector = ((HeaderPointer->LastSegment+1)*BLOCKS_PER_SEGMENT);
|
|||
|
|
|||
|
// if it's not null, then we have a bad sector, so make sure
|
|||
|
// the rest of the bad sectors are assending, and within the
|
|||
|
// boundaries of the tape
|
|||
|
if (previous_value) {
|
|||
|
// Get rid of hi-bit
|
|||
|
previous_value &= ~0x800000;
|
|||
|
|
|||
|
// It is illegal for a zero with the hi-bit set
|
|||
|
if (previous_value == 0) {
|
|||
|
good = FALSE;
|
|||
|
}
|
|||
|
|
|||
|
while (previous_value && offset + 3 <= end && good) {
|
|||
|
|
|||
|
// Check to make sure the value is on the tape
|
|||
|
if (previous_value >= start_sector && previous_value <= end_sector) {
|
|||
|
|
|||
|
// Read in next 3byte value
|
|||
|
next_value = (*(ptr+2) << 16) + *(dUWord *)ptr;
|
|||
|
|
|||
|
// If we aren't at the null terminator
|
|||
|
if (next_value) {
|
|||
|
|
|||
|
if (next_value & 0x800000) {
|
|||
|
if ((next_value - 0x800001)%32 != 0) {
|
|||
|
CheckedDump(QIC117DBGP,("qic117: ERROR: Tape contains an invalid bad sector map\n"));
|
|||
|
good = FALSE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Mask off hi-bit (full segment bit)
|
|||
|
next_value &= ~0x800000;
|
|||
|
|
|||
|
// If less than previous value, then fail the list
|
|||
|
// NOTE: It is illegal for a zero with the hi-bit set
|
|||
|
// but we should not need a specific test for this case
|
|||
|
// as a zero value would fail this test (good=false).
|
|||
|
if (next_value <= previous_value)
|
|||
|
good = FALSE;
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
// promote this to the previous
|
|||
|
previous_value = next_value;
|
|||
|
|
|||
|
// Skip past current entry
|
|||
|
ptr += 3;
|
|||
|
offset += 3;
|
|||
|
}
|
|||
|
} else {
|
|||
|
|
|||
|
// If we allow a null list, then good will be true
|
|||
|
good = allow_null_list;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
CheckedDump(QIC117INFO,("Validate Header at %x %s\n",StartOffset,good?"OK":"FAILED"));
|
|||
|
|
|||
|
// good is true if we found an assending list
|
|||
|
return good;
|
|||
|
}
|