NT4/private/ntos/dd/qic117/init.c
2020-09-30 17:12:29 +02:00

906 lines
23 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
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;
}