File:  [WindowsNT SDKs] / ntddk / src / scsi / scsitape / tandqic / tandqic.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Thu Aug 9 18:31:12 2018 UTC (7 years, 9 months ago) by root
Branches: msft, MAIN
CVS tags: ntddk-nov-1993, HEAD
Microsoft Windows NT Build 511 (DDK SDK) 11-01-1993

/*++

Copyright (c) 1992 Microsoft Corporation

Module Name:

    tandqic.c

Abstract:

    This module contains device specific routines for Tandberg QIC
    drives with SCSI-2 interfaces -- TDC 4220, TDC 4120, TDC 3820,
    and TDC 3660.

Author:

    Mike Colandreo  (Maynard)

Environment:

    kernel mode only

Revision History:

--*/

#include "ntddk.h"
#include "tape.h"
#include "physlogi.h"

//
//  Internal (module wide) defines that symbolize
//  non-QFA mode and the two QFA mode partitions.
//
#define NO_PARTITIONS        0  // non-QFA mode
#define DIRECTORY_PARTITION  1  // QFA mode, directory partition #
#define DATA_PARTITION       2  // QFA mode, data partition #

//
//  Internal (module wide) defines that symbolize
//  the Tandberg QIC drives supported by this module.
//
#define TDC_3600  (ULONG)1  // aka the TDC 3660
#define TDC_3800  (ULONG)2  // aka the TDC 3820
#define TDC_4100  (ULONG)3  // aka the TDC 4120
#define TDC_4200  (ULONG)4  // aka the TDC 4220

//
//  Function Prototype(s) for internal function(s)
//
static
NTSTATUS
GetInquiryInformationData (
    IN  PDEVICE_OBJECT  DeviceObject,
    IN  PIRP            Irp,
    OUT PINQUIRYDATA    InquiryBuffer
    );

static
NTSTATUS
RewindToBOT(
    IN PDEVICE_OBJECT  DeviceObject,
    IN PIRP            Irp
    );
    
static
ULONG
WhichIsIt(
    IN PINQUIRYDATA  InquiryData
    );


NTSTATUS
TapeCreatePartition(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )

/*++
Routine Description:

    This routine can either "create" two, fixed, QFA partitions on a QIC
    tape by selecting/enabling the drive's dual-partition, QFA mode or
    return the drive to the not partitioned mode (i.e., disable QFA mode).
    Note that this function "creates" two partitions only in a conceptual
    sense -- it does not physically affect tape.
    
Arguments:

    DeviceObject
    Irp

Return Value:

    NTSTATUS

--*/

{
    PDEVICE_EXTENSION      deviceExtension = DeviceObject->DeviceExtension;
    PTAPE_CREATE_PARTITION tapePartition = Irp->AssociatedIrp.SystemBuffer;
    PTAPE_DATA             tapeData = (PTAPE_DATA)(deviceExtension + 1);
    PMODE_MEDIUM_PART_PAGE buffer;
    SCSI_REQUEST_BLOCK     srb;
    PCDB                   cdb = (PCDB)srb.Cdb;
    NTSTATUS               status;

    //
    //  Only support 2 partitions, QFA mode
    //  Partition 1 = Used as directory
    //  Partition 0 = used as data
    //
    //  Note that 0 & 1 are partition numbers used
    //  by the drive -- they are not tape API partition
    //  numbers.
    //
    
    DebugPrint((3,"TapeCreatePartition: Enter routine\n"));

    switch (tapePartition->Method) {
        case TAPE_FIXED_PARTITIONS:
            DebugPrint((3,"TapeCreatePartition: fixed partitions\n"));
            break;

        case TAPE_SELECT_PARTITIONS:
        case TAPE_INITIATOR_PARTITIONS:
        default:
            DebugPrint((1,"TapeCreatePartition: "));
            DebugPrint((1,"PartitionMethod -- operation not supported\n"));
            return STATUS_NOT_IMPLEMENTED;
    }

    //
    // Must rewind to BOT before one can enable/disable QFA mode.
    // Changing the value of the FDP bit is only valid at BOT.
    // FDP bit is used to enable/disable "additional partitions"
    // (mode sense command).
    //
    
    DebugPrint((3,"TapeCreatePartition: SendSrb (rewind)\n"));

    status = RewindToBOT(DeviceObject, Irp);

    if (!NT_SUCCESS(status)) {
        DebugPrint((1,"TapeCreatePartition: rewind, SendSrb unsuccessful\n"));
        return status;
    }
    
    //
    // Performing mode select command, medium partition parameters page,
    // to enable/disable QFA mode: set the FDP bit accordingly.
    //
    
    buffer = ExAllocatePool(NonPagedPoolCacheAligned,
                            sizeof(MODE_MEDIUM_PART_PAGE));

    if (!buffer) {
        DebugPrint((1,"TapeCreatePartition: insufficient resources (buffer)\n"));
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    RtlZeroMemory(buffer, sizeof(MODE_MEDIUM_PART_PAGE));

    buffer->ParameterListHeader.DeviceSpecificParameter = 0x10;
    
    buffer->MediumPartPage.PageCode = MODE_PAGE_MEDIUM_PARTITION;
    buffer->MediumPartPage.PageLength = 0x06;
    buffer->MediumPartPage.MaximumAdditionalPartitions = 1;

    //
    // Setup FDP bit to enable/disable "additional partition".
    //

    if (tapePartition->Count == 0) {
        buffer->MediumPartPage.FDPBit = SETBITOFF;
    } else {
        buffer->MediumPartPage.FDPBit = SETBITON;
    }
    
    //
    // Zero CDB in SRB on stack.
    //
    
    RtlZeroMemory(cdb, MAXIMUM_CDB_SIZE);
    
    //
    // Prepare SCSI command (CDB)
    //

    srb.CdbLength = CDB6GENERIC_LENGTH;
    
    cdb->MODE_SELECT.OperationCode  = SCSIOP_MODE_SELECT;
    cdb->MODE_SELECT.PFBit = SETBITON;
    cdb->MODE_SELECT.ParameterListLength = sizeof(MODE_MEDIUM_PART_PAGE) - 4;
         
    //
    // Set timeout value.
    //
    
    srb.TimeOutValue = deviceExtension->TimeOutValue;

    //
    // Send SCSI command (CDB) to device
    //
    
    DebugPrint((3,"TapeCreatePartition: SendSrb (mode select)\n"));

    status = ScsiClassSendSrbSynchronous(DeviceObject,
                                         &srb,
                                         buffer,
                                         sizeof(MODE_MEDIUM_PART_PAGE) - 4,
                                         TRUE);
    
    ExFreePool(buffer);
    
    if (NT_SUCCESS(status)) {

        if (tapePartition->Count == 0) {
            tapeData->CurrentPartition = NO_PARTITIONS;
            DebugPrint((3,"TapeCreatePartition: QFA disabled\n"));
        } else {
            tapeData->CurrentPartition = DATA_PARTITION;
            DebugPrint((3,"TapeCreatePartition: QFA enabled\n"));
        }

    } else {

        DebugPrint((1,"TapeCreatePartition: mode select, SendSrb unsuccessful\n"));

    }

    return status;

} // end TapeCreatePartition()


NTSTATUS
TapeErase(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )

/*++
Routine Description:

    This routine erases the entire tape. The Tandberg QIC drives cannot
    partially erase tape. Positioning the tape at BOT is a prerequisite:
    the drive rejects the command if the tape is not at BOT.

Arguments:

    DeviceObject
    Irp

Return Value:

    NTSTATUS

--*/

{
    PDEVICE_EXTENSION  deviceExtension = DeviceObject->DeviceExtension;
    PTAPE_ERASE        tapeErase = Irp->AssociatedIrp.SystemBuffer;
    PTAPE_DATA         tapeData = (PTAPE_DATA)(deviceExtension + 1);
    SCSI_REQUEST_BLOCK srb;
    PCDB               cdb = (PCDB)srb.Cdb;
    NTSTATUS           status;

    DebugPrint((3,"TapeErase: Enter routine\n"));

    if (tapeErase->Immediate) {
        switch (tapeErase->Type) {
            case TAPE_ERASE_LONG:
                DebugPrint((3,"TapeErase: immediate\n"));
                break;

            case TAPE_ERASE_SHORT:
            default:
                DebugPrint((1,"TapeErase: EraseType, immediate -- operation not supported\n"));
                return STATUS_NOT_IMPLEMENTED;
        }
    }

    switch (tapeErase->Type) {
        case TAPE_ERASE_LONG:
            DebugPrint((3,"TapeErase: long\n"));
            break;

        case TAPE_ERASE_SHORT:
        default:
            DebugPrint((1,"TapeErase: EraseType -- operation not supported\n"));
            return STATUS_NOT_IMPLEMENTED;
    }

    //
    // Zero CDB in SRB on stack.
    //

    RtlZeroMemory(cdb, MAXIMUM_CDB_SIZE);

    //
    // Prepare SCSI command (CDB)
    //

    srb.CdbLength = CDB6GENERIC_LENGTH;

    cdb->ERASE.OperationCode = SCSIOP_ERASE;
    cdb->ERASE.Immediate = tapeErase->Immediate;
    cdb->ERASE.Long = SETBITON;

    //
    // Set timeout value.
    //

    srb.TimeOutValue = 360;

    //
    // Send SCSI command (CDB) to device
    //

    DebugPrint((3,"TapeErase: SendSrb (erase)\n"));

    status = ScsiClassSendSrbSynchronous(DeviceObject,
                                         &srb,
                                         NULL,
                                         0,
                                         FALSE);

    if (!NT_SUCCESS(status)) {
        DebugPrint((1,"TapeErase: erase, SendSrb unsuccessful\n"));
        return status;
    }

    if (tapeData->CurrentPartition) {
        tapeData->CurrentPartition = DATA_PARTITION;
    }

    return status;

} // end TapeErase()


VOID
TapeError(
    PDEVICE_OBJECT DeviceObject,
    PSCSI_REQUEST_BLOCK Srb,
    NTSTATUS *Status,
    BOOLEAN *Retry
    )

/*++

Routine Description:

    When a request completes with error, the routine InterpretSenseInfo is
    called to determine from the sense data whether the request should be
    retried and what NT status to set in the IRP. Then this routine is called
    for tape requests to handle tape-specific errors and update the nt status
    and retry boolean.

Arguments:

    DeviceObject - Supplies a pointer to the device object.

    Srb - Supplies a pointer to the failing Srb.

    Status - NT Status used to set the IRP's completion status.

    Retry - Indicates that this request should be retried.

Return Value:

    None.

--*/

{
    PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
    PSENSE_DATA       senseBuffer = Srb->SenseInfoBuffer;
    NTSTATUS          status = *Status;
    BOOLEAN           retry = *Retry;

    DebugPrint((3,"TapeError: Enter routine\n"));
    DebugPrint((1,"TapeError: Status 0x%.8X, Retry %d\n", status, retry));
    return;

} // end TapeError()


NTSTATUS
TapeGetDriveParameters(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )

/*++
Routine Description:

    This routine determines and returns the "drive parameters" of the
    Tandberg QIC tape drive associated with "DeviceObject". From time
    to time, the drive paramater set of a given drive will vary. It will
    change as drive operating characteristics change: e.g., tape media
    type loaded, recording density of the media type loaded, etc.

Arguments:

    DeviceObject
    Irp

Return Value:

    NTSTATUS

--*/

{
    PDEVICE_EXTENSION          deviceExtension = DeviceObject->DeviceExtension;
    PTAPE_GET_DRIVE_PARAMETERS tapeGetDriveParams = Irp->AssociatedIrp.SystemBuffer;
    PMODE_PARM_READ_WRITE_DATA modeParmBuffer;
    PREAD_BLOCK_LIMITS_DATA    blockLimits;
    PINQUIRYDATA               inquiryBuffer;
    UCHAR                      densityCode;
    UCHAR                      mediumType;
    ULONG                      tapeBlockLength;
    ULONG                      whichdrive;
    SCSI_REQUEST_BLOCK         srb;
    PCDB                       cdb = (PCDB)srb.Cdb;
    NTSTATUS                   status;

    DebugPrint((3,"TapeGetDriveParameters: Enter routine\n"));
    
    RtlZeroMemory(tapeGetDriveParams, sizeof(TAPE_GET_DRIVE_PARAMETERS));
    Irp->IoStatus.Information = sizeof(TAPE_GET_DRIVE_PARAMETERS);

    modeParmBuffer = ExAllocatePool(NonPagedPoolCacheAligned,
                                    sizeof(MODE_PARM_READ_WRITE_DATA));

    if (!modeParmBuffer) {
        DebugPrint((1,"TapeGetDriveParameters: insufficient resources (modeParmBuffer)\n"));
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    RtlZeroMemory(modeParmBuffer, sizeof(MODE_PARM_READ_WRITE_DATA));

    //
    // Zero CDB in SRB on stack.
    //

    RtlZeroMemory(cdb, MAXIMUM_CDB_SIZE);

    //
    // Prepare SCSI command (CDB)
    //

    srb.CdbLength = CDB6GENERIC_LENGTH;

    cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE;
    cdb->MODE_SENSE.AllocationLength = sizeof(MODE_PARM_READ_WRITE_DATA);

    //
    // Set timeout value.
    //

    srb.TimeOutValue = deviceExtension->TimeOutValue;

    //
    // Send SCSI command (CDB) to device
    //

    DebugPrint((3,"TapeGetDriveParameters: SendSrb (mode sense)\n"));

    status = ScsiClassSendSrbSynchronous(DeviceObject,
                                         &srb,
                                         modeParmBuffer,
                                         sizeof(MODE_PARM_READ_WRITE_DATA),
                                         FALSE);

    if (NT_SUCCESS(status)) {

        mediumType = modeParmBuffer->ParameterListHeader.MediumType;
        densityCode = modeParmBuffer->ParameterListBlock.DensityCode;
        tapeBlockLength  = modeParmBuffer->ParameterListBlock.BlockLength[2];
        tapeBlockLength += (modeParmBuffer->ParameterListBlock.BlockLength[1] << 8);
        tapeBlockLength += (modeParmBuffer->ParameterListBlock.BlockLength[0] << 16);

        switch (mediumType) {
            case 1:
               mediumType = DC600;
               break;

            case 2:
               mediumType = DC6150;
               break;

            case 3:
               mediumType = DC6320;
               break;

        }

    }

    ExFreePool(modeParmBuffer);

    if (!NT_SUCCESS(status)) {
        DebugPrint((1,"TapeGetDriveParameters: mode sense, SendSrb unsuccessful\n"));
        return status;
    }

    blockLimits = ExAllocatePool(NonPagedPoolCacheAligned,
                                 sizeof(READ_BLOCK_LIMITS_DATA));

    if (!blockLimits) {
        DebugPrint((1,"TapeGetDriveParameters: insufficient resources (blockLimits)\n"));
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    RtlZeroMemory(blockLimits, sizeof(READ_BLOCK_LIMITS_DATA));

    //
    // Zero CDB in SRB on stack.
    //

    RtlZeroMemory(cdb, MAXIMUM_CDB_SIZE);

    //
    // Prepare SCSI command (CDB)
    //

    srb.CdbLength = CDB6GENERIC_LENGTH;

    cdb->CDB6GENERIC.OperationCode = SCSIOP_READ_BLOCK_LIMITS;

    //
    // Set timeout value.
    //

    srb.TimeOutValue = deviceExtension->TimeOutValue;

    //
    // Send SCSI command (CDB) to device
    //

    DebugPrint((3,"TapeGetDriveParameters: SendSrb (read block limits)\n"));

    status = ScsiClassSendSrbSynchronous(DeviceObject,
                                         &srb,
                                         blockLimits,
                                         sizeof(READ_BLOCK_LIMITS_DATA),
                                         FALSE);

    if (NT_SUCCESS(status)) {
        tapeGetDriveParams->MaximumBlockSize = blockLimits->BlockMaximumSize[2];
        tapeGetDriveParams->MaximumBlockSize += (blockLimits->BlockMaximumSize[1] << 8);
        tapeGetDriveParams->MaximumBlockSize += (blockLimits->BlockMaximumSize[0] << 16);
            
        tapeGetDriveParams->MinimumBlockSize = blockLimits->BlockMinimumSize[1];
        tapeGetDriveParams->MinimumBlockSize += (blockLimits->BlockMinimumSize[0] << 8);
    }

    ExFreePool(blockLimits);

    if (!NT_SUCCESS(status)) {
        DebugPrint((1,"TapeGetDriveParameters: read block limits, SendSrb unsuccessful\n"));
        return status;
    }

    inquiryBuffer = ExAllocatePool(NonPagedPoolCacheAligned,
                                   sizeof(INQUIRYDATA));

    if (!inquiryBuffer) {
        DebugPrint((1,"TapeGetDriveParameters: insufficient resources (inquiryBuffer)\n"));
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    RtlZeroMemory(inquiryBuffer, sizeof(INQUIRYDATA));

    //
    // Send SCSI command (CDB) to device
    //

    DebugPrint((3,"TapeGetDriveParameters: SendSrb (inquiry)\n"));

    status = GetInquiryInformationData(DeviceObject, Irp, inquiryBuffer);

    if (NT_SUCCESS(status)) {

        //
        // Which drive do we have, TDC 3660, 3820, 4120 or 4220?
        //

        whichdrive = WhichIsIt(inquiryBuffer);
    }

    ExFreePool(inquiryBuffer);

    if (!NT_SUCCESS(status)) {
        DebugPrint((1,"TapeGetDriveParameters: inquiry, SendSrb unsuccessful\n"));
        return status;
    }
         
    tapeGetDriveParams->ECC = 0;
    tapeGetDriveParams->Compression = 0;
    tapeGetDriveParams->DataPadding = 0;
    tapeGetDriveParams->MaximumPartitionCount = 2;
    
    if ((whichdrive == TDC_4100) || (whichdrive == TDC_4200)) {

        tapeGetDriveParams->ReportSetmarks = TRUE;

        tapeGetDriveParams->FeaturesLow |=
            TAPE_DRIVE_REPORT_SMKS;

        tapeGetDriveParams->FeaturesHigh |=
            TAPE_DRIVE_SETMARKS |
            TAPE_DRIVE_WRITE_SETMARKS;

    } 

    if (whichdrive != TDC_3600) {
             
        tapeGetDriveParams->FeaturesLow |=
            TAPE_DRIVE_VARIABLE_BLOCK;
            
        tapeGetDriveParams->FeaturesHigh |=
            TAPE_DRIVE_SET_BLOCK_SIZE;

    }
    
    switch (densityCode) {
        case QIC_XX:
            switch (mediumType) {
                case DC6320:
                case DC6525:
                case DC9100:
                case DC9100XL:
                    tapeGetDriveParams->DefaultBlockSize = 1024;
                    break;

                default:
                    tapeGetDriveParams->DefaultBlockSize = 512;
                    break;
            }
            break;

        case QIC_525:
        case QIC_1000:
        case QIC_2GB:
            tapeGetDriveParams->DefaultBlockSize = 1024;
            break;
            
        default:
            tapeGetDriveParams->DefaultBlockSize = 512;
            break;
    }

    tapeGetDriveParams->FeaturesLow |=
        TAPE_DRIVE_FIXED |
        TAPE_DRIVE_ERASE_LONG |
        TAPE_DRIVE_ERASE_BOP_ONLY |
        TAPE_DRIVE_ERASE_IMMEDIATE |
        TAPE_DRIVE_FIXED_BLOCK |
        TAPE_DRIVE_WRITE_PROTECT |
        TAPE_DRIVE_GET_ABSOLUTE_BLK |
        TAPE_DRIVE_GET_LOGICAL_BLK;

    tapeGetDriveParams->FeaturesHigh |=
        TAPE_DRIVE_LOAD_UNLOAD |
        TAPE_DRIVE_TENSION |
        TAPE_DRIVE_LOCK_UNLOCK |
        TAPE_DRIVE_REWIND_IMMEDIATE |
        TAPE_DRIVE_LOAD_UNLD_IMMED |
        TAPE_DRIVE_TENSION_IMMED |
        TAPE_DRIVE_ABSOLUTE_BLK |
        TAPE_DRIVE_LOGICAL_BLK |
        TAPE_DRIVE_END_OF_DATA |
        TAPE_DRIVE_RELATIVE_BLKS |
        TAPE_DRIVE_FILEMARKS |
        TAPE_DRIVE_SEQUENTIAL_FMKS |
        TAPE_DRIVE_REVERSE_POSITION |
        TAPE_DRIVE_WRITE_FILEMARKS |
        TAPE_DRIVE_WRITE_MARK_IMMED;

    tapeGetDriveParams->FeaturesHigh &= ~TAPE_DRIVE_HIGH_FEATURES;

    DebugPrint((3,"TapeGetDriveParameters: FeaturesLow == 0x%.8X\n",
        tapeGetDriveParams->FeaturesLow));
    DebugPrint((3,"TapeGetDriveParameters: FeaturesHigh == 0x%.8X\n",
        tapeGetDriveParams->FeaturesHigh));

    return status;

} // end TapeGetDriveParameters()


NTSTATUS
TapeGetMediaParameters(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )

/*++
Routine Description:

    This routine determines and returns the "media parameters" of the
    Tandberg QIC tape drive associated with "DeviceObject". Tape media
    must be present (loaded) in the drive for this function to return
    "no error".

Arguments:

    DeviceObject
    Irp

Return Value:

    NTSTATUS

--*/

{
    PDEVICE_EXTENSION            deviceExtension = DeviceObject->DeviceExtension;
    PTAPE_DATA                   tapeData = (PTAPE_DATA)(deviceExtension + 1);
    PTAPE_GET_MEDIA_PARAMETERS   tapeGetMediaParams = Irp->AssociatedIrp.SystemBuffer;
    PMODE_TAPE_MEDIA_INFORMATION mediaInfoBuffer;
    PMODE_DEVICE_CONFIG_PAGE     deviceConfigBuffer; 
    ULONG                        sectorShift;
    SCSI_REQUEST_BLOCK           srb;
    PCDB                         cdb = (PCDB)srb.Cdb;
    NTSTATUS                     status;

    DebugPrint((3,"TapeGetMediaParameters: Enter routine\n"));

    RtlZeroMemory(tapeGetMediaParams, sizeof(TAPE_GET_MEDIA_PARAMETERS));
    Irp->IoStatus.Information = sizeof(TAPE_GET_MEDIA_PARAMETERS);

    //
    // Zero CDB in SRB on stack.
    //

    RtlZeroMemory(cdb, MAXIMUM_CDB_SIZE);

    //
    // Prepare SCSI command (CDB)
    //

    srb.CdbLength = CDB6GENERIC_LENGTH;

    cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;

    //
    // Set timeout value.
    //

    srb.TimeOutValue = deviceExtension->TimeOutValue;

    //
    // Send SCSI command (CDB) to device
    //

    DebugPrint((3,"TapeGetMediaParameters: SendSrb (test unit ready)\n"));

    status = ScsiClassSendSrbSynchronous(DeviceObject,
                                         &srb,
                                         NULL,
                                         0,
                                         FALSE);

    if (!NT_SUCCESS(status)) {
        DebugPrint((1,"TapeGetMediaParameters: test unit ready, SendSrb unsuccessful\n"));
        return status;
    }

    mediaInfoBuffer = ExAllocatePool(NonPagedPoolCacheAligned,
                                     sizeof(MODE_TAPE_MEDIA_INFORMATION));

    if (!mediaInfoBuffer) {
        DebugPrint((1,"TapeGetMediaParameters: insufficient resources (mediaInfoBuffer)\n"));
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    RtlZeroMemory(mediaInfoBuffer, sizeof(MODE_TAPE_MEDIA_INFORMATION));

    //
    // Zero CDB in SRB on stack.
    //

    RtlZeroMemory(cdb, MAXIMUM_CDB_SIZE);

    //
    // Prepare SCSI command (CDB)
    //

    srb.CdbLength = CDB6GENERIC_LENGTH;

    cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE;
    cdb->MODE_SENSE.PageCode = MODE_PAGE_MEDIUM_PARTITION;
    cdb->MODE_SENSE.AllocationLength = sizeof(MODE_TAPE_MEDIA_INFORMATION) - 4;

    //
    // Set timeout value.
    //

    srb.TimeOutValue = deviceExtension->TimeOutValue;

    //
    // Send SCSI command (CDB) to device
    //

    DebugPrint((3,"TapeGetMediaParameters: SendSrb (mode sense #1)\n"));

    status = ScsiClassSendSrbSynchronous(DeviceObject,
                                         &srb,
                                         mediaInfoBuffer,
                                         sizeof(MODE_TAPE_MEDIA_INFORMATION) - 4,
                                         FALSE);

    if (!NT_SUCCESS(status)) {
        DebugPrint((1,"TapeGetMediaParameters: mode sense #1, SendSrb unsuccessful\n"));
        ExFreePool(mediaInfoBuffer);
        return status;
    }

    if (mediaInfoBuffer->MediumPartPage.FDPBit) {

        deviceConfigBuffer = ExAllocatePool(NonPagedPoolCacheAligned,
                                            sizeof(MODE_DEVICE_CONFIG_PAGE));

        if (!deviceConfigBuffer) {
            DebugPrint((1,"TapeGetMediaParameters: insufficient resources (deviceConfigBuffer)\n"));
            ExFreePool(mediaInfoBuffer);
            return STATUS_INSUFFICIENT_RESOURCES;
        }

        RtlZeroMemory(deviceConfigBuffer, sizeof(MODE_DEVICE_CONFIG_PAGE));

        //
        // Zero CDB in SRB on stack.
        //

        RtlZeroMemory(cdb, MAXIMUM_CDB_SIZE);

        //
        // Prepare SCSI command (CDB)
        //

        srb.CdbLength = CDB6GENERIC_LENGTH;

        cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE;
        cdb->MODE_SENSE.Dbd = SETBITON;
        cdb->MODE_SENSE.PageCode = MODE_PAGE_DEVICE_CONFIG;
        cdb->MODE_SENSE.AllocationLength = sizeof(MODE_DEVICE_CONFIG_PAGE);

        //
        // Set timeout value.
        //

        srb.TimeOutValue = deviceExtension->TimeOutValue;

        //
        // Send SCSI command (CDB) to device
        //

        DebugPrint((3,"TapeGetMediaParameters: SendSrb (mode sense #2)\n"));

        status = ScsiClassSendSrbSynchronous(DeviceObject,
                                             &srb,
                                             deviceConfigBuffer,
                                             sizeof(MODE_DEVICE_CONFIG_PAGE),
                                             FALSE);

        if (!NT_SUCCESS(status)) {
            DebugPrint((1,"TapeGetMediaParameters: mode sense #2, SendSrb unsuccessful\n"));
            ExFreePool(deviceConfigBuffer);
            ExFreePool(mediaInfoBuffer);
            return status;
        }

        tapeData->CurrentPartition =
            deviceConfigBuffer->DeviceConfigPage.ActivePartition?
            DIRECTORY_PARTITION : DATA_PARTITION;

        tapeGetMediaParams->PartitionCount = 2;

        ExFreePool(deviceConfigBuffer);

    } else {

        tapeGetMediaParams->PartitionCount = 1 ;

        tapeData->CurrentPartition = NO_PARTITIONS;

    }

    tapeGetMediaParams->BlockSize = mediaInfoBuffer->ParameterListBlock.BlockLength[2];
    tapeGetMediaParams->BlockSize += (mediaInfoBuffer->ParameterListBlock.BlockLength[1] << 8);
    tapeGetMediaParams->BlockSize += (mediaInfoBuffer->ParameterListBlock.BlockLength[0] << 16);

    WHICH_BIT(tapeGetMediaParams->BlockSize, sectorShift);
    deviceExtension->DiskGeometry->BytesPerSector = tapeGetMediaParams->BlockSize;
    deviceExtension->SectorShift = sectorShift;

    tapeGetMediaParams->WriteProtected =
        ((mediaInfoBuffer->ParameterListHeader.DeviceSpecificParameter >> 7) & 0x01);

    ExFreePool(mediaInfoBuffer);

    return status;

} // end TapeGetMediaParameters()


NTSTATUS
TapeGetPosition(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )

/*++
Routine Description:

    This routine returns the current position of the tape.

Arguments:

    DeviceObject
    Irp

Return Value:

    NTSTATUS

--*/

{
    PDEVICE_EXTENSION            deviceExtension = DeviceObject->DeviceExtension;
    PTAPE_GET_POSITION           tapeGetPosition = Irp->AssociatedIrp.SystemBuffer;
    PTAPE_DATA                   tapeData = (PTAPE_DATA)(deviceExtension + 1);
    PMODE_TAPE_MEDIA_INFORMATION mediaInfoBuffer;
    PMODE_DEVICE_CONFIG_PAGE     deviceConfigBuffer; 
    PTAPE_POSITION_DATA          absoluteBuffer;
    UCHAR                        densityCode;
    ULONG                        tapeBlockLength;
    ULONG                        tapeBlockAddress;
    ULONG                        type;
    SCSI_REQUEST_BLOCK           srb;
    PCDB                         cdb = (PCDB)srb.Cdb;
    NTSTATUS                     status;

    DebugPrint((3,"TapeGetPosition: Enter routine\n"));

    type = tapeGetPosition->Type;
    RtlZeroMemory(tapeGetPosition, sizeof(TAPE_GET_POSITION));
    Irp->IoStatus.Information = sizeof(TAPE_GET_POSITION);
    tapeGetPosition->Type = type;

    //
    // Zero CDB in SRB on stack.
    //

    RtlZeroMemory(cdb, MAXIMUM_CDB_SIZE);

    //
    // Prepare SCSI command (CDB)
    //

    srb.CdbLength = CDB6GENERIC_LENGTH;

    cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;

    //
    // Set timeout value.
    //

    srb.TimeOutValue = deviceExtension->TimeOutValue;

    //
    // Send SCSI command (CDB) to device
    //

    DebugPrint((3,"TapeGetPosition: SendSrb (test unit ready)\n"));

    status = ScsiClassSendSrbSynchronous(DeviceObject,
                                         &srb,
                                         NULL,
                                         0,
                                         FALSE);

    if (!NT_SUCCESS(status)) {
        DebugPrint((1,"TapeGetPosition: test unit ready, SendSrb unsuccessful\n"));
        return status;
    }

    if (type == TAPE_LOGICAL_POSITION) {

        DebugPrint((3,"TapeGetPosition: pseudo logical\n"));

        type = TAPE_PSEUDO_LOGICAL_POSITION;

        mediaInfoBuffer = ExAllocatePool(NonPagedPoolCacheAligned,
                                         sizeof(MODE_TAPE_MEDIA_INFORMATION));

        if (!mediaInfoBuffer) {
            DebugPrint((1,"TapeGetPosition: insufficient resources (mediaInfoBuffer)\n"));
            return STATUS_INSUFFICIENT_RESOURCES;
        }

        RtlZeroMemory(mediaInfoBuffer, sizeof(MODE_TAPE_MEDIA_INFORMATION));

        //
        // Zero CDB in SRB on stack.
        //

        RtlZeroMemory(cdb, MAXIMUM_CDB_SIZE);

        //
        // Prepare SCSI command (CDB)
        //

        srb.CdbLength = CDB6GENERIC_LENGTH;
        
        cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE;
        cdb->MODE_SENSE.PageCode = MODE_PAGE_MEDIUM_PARTITION;
        cdb->MODE_SENSE.AllocationLength = sizeof(MODE_TAPE_MEDIA_INFORMATION) - 4;

        //
        // Set timeout value.
        //

        srb.TimeOutValue = deviceExtension->TimeOutValue;

        //
        // Send SCSI command (CDB) to device
        //

        DebugPrint((3,"TapeGetPosition: SendSrb (mode sense #1)\n"));

        status = ScsiClassSendSrbSynchronous(DeviceObject,
                                             &srb,
                                             mediaInfoBuffer,
                                             sizeof(MODE_TAPE_MEDIA_INFORMATION) - 4,
                                             FALSE);

        if (!NT_SUCCESS(status)) {
            DebugPrint((1,"TapeGetPosition: mode sense #1, SendSrb unsuccessful\n"));
            ExFreePool(mediaInfoBuffer);
            return status;
        }

        if (mediaInfoBuffer->MediumPartPage.FDPBit) {

            deviceConfigBuffer = ExAllocatePool(NonPagedPoolCacheAligned,
                                                sizeof(MODE_DEVICE_CONFIG_PAGE));

            if (!deviceConfigBuffer) {
                DebugPrint((1,"TapeGetPosition: insufficient resources (deviceConfigBuffer)\n"));
                ExFreePool(mediaInfoBuffer);
                return STATUS_INSUFFICIENT_RESOURCES;
            }

            RtlZeroMemory(deviceConfigBuffer, sizeof(MODE_DEVICE_CONFIG_PAGE));

            //
            // Zero CDB in SRB on stack.
            //

            RtlZeroMemory(cdb, MAXIMUM_CDB_SIZE);

            //
            // Prepare SCSI command (CDB)
            //

            srb.CdbLength = CDB6GENERIC_LENGTH;

            cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE;
            cdb->MODE_SENSE.Dbd = SETBITON;
            cdb->MODE_SENSE.PageCode = MODE_PAGE_DEVICE_CONFIG;
            cdb->MODE_SENSE.AllocationLength = sizeof(MODE_DEVICE_CONFIG_PAGE);

            //
            // Set timeout value.
            //

            srb.TimeOutValue = deviceExtension->TimeOutValue;

            //
            // Send SCSI command (CDB) to device
            //

            DebugPrint((3,"TapeGetPosition: SendSrb (mode sense #2)\n"));

            status = ScsiClassSendSrbSynchronous(DeviceObject,
                                                 &srb,
                                                 deviceConfigBuffer,
                                                 sizeof(MODE_DEVICE_CONFIG_PAGE),
                                                 FALSE);

            if (!NT_SUCCESS(status)) {
                DebugPrint((1,"TapeGetPosition: mode sense #2, SendSrb unsuccessful\n"));
                ExFreePool(deviceConfigBuffer);
                ExFreePool(mediaInfoBuffer);
                return status;
            }

            tapeData->CurrentPartition =
                 deviceConfigBuffer->DeviceConfigPage.ActivePartition?
                 DIRECTORY_PARTITION : DATA_PARTITION;

            ExFreePool(deviceConfigBuffer);

        } else {

            tapeData->CurrentPartition = NO_PARTITIONS;

        }

        densityCode      = mediaInfoBuffer->ParameterListBlock.DensityCode;
        tapeBlockLength  = mediaInfoBuffer->ParameterListBlock.BlockLength[2];
        tapeBlockLength += (mediaInfoBuffer->ParameterListBlock.BlockLength[1] << 8);
        tapeBlockLength += (mediaInfoBuffer->ParameterListBlock.BlockLength[0] << 16);

        ExFreePool(mediaInfoBuffer);

    }

    switch (type) {
        case TAPE_PSEUDO_LOGICAL_POSITION:
        case TAPE_ABSOLUTE_POSITION:
            absoluteBuffer = ExAllocatePool(NonPagedPoolCacheAligned,
                                            sizeof(TAPE_POSITION_DATA));
    
            if (!absoluteBuffer) {
                DebugPrint((1,"TapeGetPosition: insufficient resources (absoluteBuffer)\n"));
                return STATUS_INSUFFICIENT_RESOURCES;
            }
    
            RtlZeroMemory(absoluteBuffer, sizeof(TAPE_POSITION_DATA));

            //
            // Zero CDB in SRB on stack.
            //

            RtlZeroMemory(cdb, MAXIMUM_CDB_SIZE);

            //
            // Prepare SCSI command (CDB)
            //

            srb.CdbLength = CDB10GENERIC_LENGTH;

            cdb->READ_POSITION.Operation = SCSIOP_READ_POSITION;
            cdb->READ_POSITION.BlockType = SETBITON;

            //
            // Set timeout value.
            //
    
            srb.TimeOutValue = deviceExtension->TimeOutValue;
    
            //
            // Send SCSI command (CDB) to device
            //

            DebugPrint((3,"TapeGetPosition: SendSrb (read position)\n"));

            status = ScsiClassSendSrbSynchronous(DeviceObject,
                                                 &srb,
                                                 absoluteBuffer,
                                                 sizeof(TAPE_POSITION_DATA),
                                                 FALSE);

            if (NT_SUCCESS(status)) {

                REVERSE_BYTES((PFOUR_BYTE)&tapeBlockAddress,
                              (PFOUR_BYTE)absoluteBuffer->FirstBlock);
            }

            ExFreePool(absoluteBuffer);

            if (!NT_SUCCESS(status)) {
                DebugPrint((1,"TapeGetPosition: read position, SendSrb unsuccessful\n"));
                return status;
            }

            if (type == TAPE_ABSOLUTE_POSITION) {
                tapeGetPosition->Partition = 0;
                tapeGetPosition->Offset.HighPart = 0;
                tapeGetPosition->Offset.LowPart  = tapeBlockAddress;
                break;
            }

            tapeBlockAddress =
                TapePhysicalBlockToLogicalBlock(
                    densityCode,
                    tapeBlockAddress,
                    tapeBlockLength,
                    (BOOLEAN)(
                        (tapeData->CurrentPartition
                            == DIRECTORY_PARTITION)?
                        NOT_FROM_BOT : FROM_BOT
                    )
                );

            tapeGetPosition->Offset.HighPart = 0;
            tapeGetPosition->Offset.LowPart  = tapeBlockAddress;
            tapeGetPosition->Partition = tapeData->CurrentPartition;
            break;

        default:
            DebugPrint((1,"TapeGetPosition: PositionType -- operation not supported\n"));
            return STATUS_NOT_IMPLEMENTED;
    }

    return status;

} // end TapeGetPosition()


NTSTATUS
TapeGetStatus(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )

/*++
Routine Description:

    This routine returns the status of the device.

Arguments:

    DeviceObject
    Irp

Return Value:

    NTSTATUS

--*/

{
    PDEVICE_EXTENSION  deviceExtension = DeviceObject->DeviceExtension;
    SCSI_REQUEST_BLOCK srb;
    PCDB               cdb = (PCDB)srb.Cdb;
    NTSTATUS           status;

    DebugPrint((3,"TapeGetStatus: Enter routine\n"));

    //
    // Zero CDB in SRB on stack.
    //

    RtlZeroMemory(cdb, MAXIMUM_CDB_SIZE);

    //
    // Prepare SCSI command (CDB)
    //

    srb.CdbLength = CDB6GENERIC_LENGTH;

    cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;

    //
    // Set timeout value.
    //

    srb.TimeOutValue = deviceExtension->TimeOutValue;

    //
    // Send SCSI command (CDB) to device
    //

    DebugPrint((3,"TapeGetStatus: SendSrb (test unit ready)\n"));

    status = ScsiClassSendSrbSynchronous(DeviceObject,
                                         &srb,
                                         NULL,
                                         0,
                                         FALSE);

    if (!NT_SUCCESS(status)) {
        DebugPrint((1,"TapeGetStatus: test unit ready, SendSrb unsuccessful\n"));
    }

    return status;

} // end TapeGetStatus()


NTSTATUS
TapePrepare(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )

/*++
Routine Description:

    This routine loads, unloads, tensions, locks, or unlocks the tape.

Arguments:

    DeviceObject
    Irp

Return Value:

    NTSTATUS

--*/

{
    PDEVICE_EXTENSION  deviceExtension = DeviceObject->DeviceExtension;
    PTAPE_PREPARE      tapePrepare = Irp->AssociatedIrp.SystemBuffer;
    SCSI_REQUEST_BLOCK srb;
    PCDB               cdb = (PCDB)srb.Cdb;
    NTSTATUS           status;

    DebugPrint((3,"TapePrepare: Enter routine\n"));

    if (tapePrepare->Immediate) {
        switch (tapePrepare->Operation) {
            case TAPE_LOAD:
            case TAPE_UNLOAD:
            case TAPE_TENSION:
                DebugPrint((3,"TapePrepare: immediate\n"));
                break;

            case TAPE_LOCK:
            case TAPE_UNLOCK:
            default:
                DebugPrint((1,"TapePrepare: Operation, immediate -- operation not supported\n"));
                return STATUS_NOT_IMPLEMENTED;
        }
    }

    //
    // Zero CDB in SRB on stack.
    //

    RtlZeroMemory(cdb, MAXIMUM_CDB_SIZE);

    //
    // Prepare SCSI command (CDB)
    //

    srb.CdbLength = CDB6GENERIC_LENGTH;

    cdb->CDB6GENERIC.Immediate = tapePrepare->Immediate;

    switch (tapePrepare->Operation) {
        case TAPE_LOAD:
            DebugPrint((3,"TapePrepare: Operation == load\n"));
            cdb->CDB6GENERIC.OperationCode = SCSIOP_LOAD_UNLOAD;
            cdb->CDB6GENERIC.CommandUniqueBytes[2] = 0x01;
            srb.TimeOutValue = 180;
            break;

        case TAPE_UNLOAD:
            DebugPrint((3,"TapePrepare: Operation == unload\n"));
            cdb->CDB6GENERIC.OperationCode = SCSIOP_LOAD_UNLOAD;
            srb.TimeOutValue = 180;
            break;

        case TAPE_TENSION:
            DebugPrint((3,"TapePrepare: Operation == tension\n"));
            cdb->CDB6GENERIC.OperationCode = SCSIOP_LOAD_UNLOAD;
            cdb->CDB6GENERIC.CommandUniqueBytes[2] = 0x03;
            srb.TimeOutValue = 360;
            break;

        case TAPE_LOCK:
            DebugPrint((3,"TapePrepare: Operation == lock\n"));
            cdb->CDB6GENERIC.OperationCode = SCSIOP_MEDIUM_REMOVAL;
            cdb->CDB6GENERIC.CommandUniqueBytes[2] = 0x01;
            srb.TimeOutValue = 180;
            break;

        case TAPE_UNLOCK:
            DebugPrint((3,"TapePrepare: Operation == unlock\n"));
            cdb->CDB6GENERIC.OperationCode = SCSIOP_MEDIUM_REMOVAL;
            srb.TimeOutValue = 180;
            break;

        default:
            DebugPrint((1,"TapePrepare: Operation -- operation not supported\n"));
            return STATUS_NOT_IMPLEMENTED;
    }

    //
    // Send SCSI command (CDB) to device
    //

    DebugPrint((3,"TapePrepare: SendSrb (Operation)\n"));

    status = ScsiClassSendSrbSynchronous(DeviceObject,
                                         &srb,
                                         NULL,
                                         0,
                                         FALSE);

    if (!NT_SUCCESS(status)) {
        DebugPrint((1,"TapePrepare: Operation, SendSrb unsuccessful\n"));
    }

    return status;

} // end TapePrepare()


NTSTATUS
TapeReadWrite(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )

/*++

Routine Description:

    This routine builds SRBs and CDBs for read and write requests to
    Tandberg QIC drives.

Arguments:

    DeviceObject
    Irp

Return Value:

    Returns STATUS_PENDING.

--*/

  {
    PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
    PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
    PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(Irp);
    PSCSI_REQUEST_BLOCK srb;
    PCDB cdb;
    KIRQL currentIrql;
    ULONG transferBlocks;
    LARGE_INTEGER startingOffset =
      currentIrpStack->Parameters.Read.ByteOffset;

    DebugPrint((3,"TapeReadWrite: Enter routine\n"));

    //
    // Allocate an Srb.
    //

    if (deviceExtension->SrbZone != NULL &&
        (srb = ExInterlockedAllocateFromZone(
            deviceExtension->SrbZone,
            deviceExtension->SrbZoneSpinLock)) != NULL) {

        srb->SrbFlags = SRB_FLAGS_ALLOCATED_FROM_ZONE;

    } else {

        //
        // Allocate Srb from non-paged pool.
        // This call must succeed.
        //

        srb = ExAllocatePool(NonPagedPoolMustSucceed, SCSI_REQUEST_BLOCK_SIZE);

        srb->SrbFlags = 0;

    }

    //
    // Write length to SRB.
    //

    srb->Length = SCSI_REQUEST_BLOCK_SIZE;

    //
    // Set up IRP Address.
    //

    srb->OriginalRequest = Irp;

    //
    // Set up target id and logical unit number.
    //

    srb->PathId = deviceExtension->PathId;
    srb->TargetId = deviceExtension->TargetId;
    srb->Lun = deviceExtension->Lun;


    srb->Function = SRB_FUNCTION_EXECUTE_SCSI;

    srb->DataBuffer = MmGetMdlVirtualAddress(Irp->MdlAddress);

    //
    // Save byte count of transfer in SRB Extension.
    //

    srb->DataTransferLength = currentIrpStack->Parameters.Read.Length;

    //
    // Indicate auto request sense by specifying buffer and size.
    //

    srb->SenseInfoBuffer = deviceExtension->SenseData;

    srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;

    //
    // Initialize the queue actions field.
    //

    srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;

    //
    // Indicate auto request sense by specifying buffer and size.
    //

    srb->SenseInfoBuffer = deviceExtension->SenseData;

    srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;

    //
    // Set timeout value in seconds.
    //

    srb->TimeOutValue = deviceExtension->TimeOutValue;

    //
    // Zero statuses.
    //

    srb->SrbStatus = srb->ScsiStatus = 0;

    srb->NextSrb = 0;

    //
    // Indicate that 6-byte CDB's will be used.
    //

    srb->CdbLength = CDB6GENERIC_LENGTH;

    //
    // Fill in CDB fields.
    //

    cdb = (PCDB)srb->Cdb;

    //
    // Zero CDB in SRB.
    //

    RtlZeroMemory(cdb, MAXIMUM_CDB_SIZE);

    //
    // Since we are writing fixed block mode, normalize transfer count
    // to number of blocks.
    //

    transferBlocks =
        currentIrpStack->Parameters.Read.Length >> deviceExtension->SectorShift;

    //
    // Set up transfer length
    //

    cdb->CDB6READWRITETAPE.TransferLenMSB = (UCHAR)((transferBlocks >> 16) & 0xff);
    cdb->CDB6READWRITETAPE.TransferLen    = (UCHAR)((transferBlocks >> 8) & 0xff);
    cdb->CDB6READWRITETAPE.TransferLenLSB = (UCHAR)(transferBlocks & 0xff);

    //
    // Tell the python we are in fixed block mode
    //

    cdb->CDB6READWRITETAPE.VendorSpecific = 1;

    //
    // Set transfer direction flag and Cdb command.
    //

    if (currentIrpStack->MajorFunction == IRP_MJ_READ) {

         DebugPrint((3, "TapeRequest: Read Command\n"));

         srb->SrbFlags = SRB_FLAGS_DATA_IN;
         cdb->CDB6READWRITETAPE.OperationCode = SCSIOP_READ6;

    } else {

         DebugPrint((3, "TapeRequest: Write Command\n"));

         srb->SrbFlags = SRB_FLAGS_DATA_OUT;
         cdb->CDB6READWRITETAPE.OperationCode = SCSIOP_WRITE6;
    }

    //
    // Or in the default flags from the device object.
    //

    srb->SrbFlags |= deviceExtension->SrbFlags;

    //
    // Set up major SCSI function.
    //

    nextIrpStack->MajorFunction = IRP_MJ_SCSI;

    //
    // Save SRB address in next stack for port driver.
    //

    nextIrpStack->Parameters.Scsi.Srb = srb;

    //
    // Save retry count in current IRP stack.
    //

    currentIrpStack->Parameters.Others.Argument4 = (PVOID)MAXIMUM_RETRIES;

    //
    // Set up IoCompletion routine address.
    //

    IoSetCompletionRoutine(Irp,
                           ScsiClassIoComplete,
                           srb,
                           TRUE,
                           TRUE,
                           FALSE);

    return STATUS_PENDING;

} // end TapeReadWrite()


NTSTATUS
TapeSetDriveParameters(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )

/*++
Routine Description:

    This routine would "set" the "drive parameters" of the Tandberg QIC
    tape drive associated with "DeviceObject" if any could be set, but
    none can! Hence, this routine always returns a STATUS_NOT_IMPLEMENTED
    status.

Arguments:

    DeviceObject
    Irp

Return Value:

    NTSTATUS

--*/

{
    DebugPrint((3,"TapeSetDriveParameters: Enter routine\n"));

    DebugPrint((1,"TapeSetDriveParameters: Operation -- operation not supported\n"));

    return STATUS_NOT_IMPLEMENTED;

} // end TapeSetDriveParameters()



NTSTATUS
TapeSetMediaParameters(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )

/*++
Routine Description:

    This routine "sets" the "media parameters" of the Tandberg QIC tape
    drive associated with "DeviceObject". Tape media must be present
    (loaded) in the drive for this function to return "no error".

Arguments:

    DeviceObject
    Irp

Return Value:

    NTSTATUS

--*/

{
    PDEVICE_EXTENSION          deviceExtension = DeviceObject->DeviceExtension;
    PTAPE_SET_MEDIA_PARAMETERS tapeSetMediaParams = Irp->AssociatedIrp.SystemBuffer;
    PMODE_PARM_READ_WRITE_DATA modeBuffer;
    PINQUIRYDATA               inquiryBuffer;
    ULONG                      whichdrive;
    SCSI_REQUEST_BLOCK         srb;
    PCDB                       cdb = (PCDB)srb.Cdb;
    NTSTATUS                   status;

    DebugPrint((3,"TapeSetMediaParameters: Enter routine\n"));

    inquiryBuffer = ExAllocatePool(NonPagedPoolCacheAligned,
                                   sizeof(INQUIRYDATA));

    if (!inquiryBuffer) {
        DebugPrint((1,"TapeSetMediaParameters: insufficient resources (inquiryBuffer)\n"));
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    RtlZeroMemory(inquiryBuffer, sizeof(INQUIRYDATA));

    //
    // Send SCSI command (CDB) to device
    //

    DebugPrint((3,"TapeSetMediaParameters: SendSrb (inquiry)\n"));

    status = GetInquiryInformationData(DeviceObject, Irp, inquiryBuffer);

    if (NT_SUCCESS(status)) {

        //
        // Which drive do we have, TDC 3660, 3820, 4120 or 4220?
        //

        whichdrive = WhichIsIt(inquiryBuffer);
    }
    
    ExFreePool(inquiryBuffer);

    if (!NT_SUCCESS(status)) {
        DebugPrint((1,"TapeSetMediaParameters: inquiry, SendSrb unsuccessful\n"));
        return status;
    }
         
    if (whichdrive == TDC_3600) {
        DebugPrint((1,"TapeSetMediaParameters: whichdrive -- operation not supported\n"));
        return STATUS_NOT_IMPLEMENTED;
    }

    //
    // Zero CDB in SRB on stack.
    //

    RtlZeroMemory(cdb, MAXIMUM_CDB_SIZE);

    //
    // Prepare SCSI command (CDB)
    //

    srb.CdbLength = CDB6GENERIC_LENGTH;

    cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;

    //
    // Set timeout value.
    //

    srb.TimeOutValue = deviceExtension->TimeOutValue;

    //
    // Send SCSI command (CDB) to device
    //

    DebugPrint((3,"TapeSetMediaParameters: SendSrb (test unit ready)\n"));

    status = ScsiClassSendSrbSynchronous(DeviceObject,
                                         &srb,
                                         NULL,
                                         0,
                                         FALSE);

    if (!NT_SUCCESS(status)) {
        DebugPrint((1,"TapeSetMediaParameters: test unit ready, SendSrb unsuccessful\n"));
        return status;
    }

    modeBuffer = ExAllocatePool(NonPagedPoolCacheAligned,
                                sizeof(MODE_PARM_READ_WRITE_DATA));

    if (!modeBuffer) {
        DebugPrint((1,"TapeSetMediaParameters: insufficient resources (modeBuffer)\n"));
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    RtlZeroMemory(modeBuffer, sizeof(MODE_PARM_READ_WRITE_DATA));

    modeBuffer->ParameterListHeader.DeviceSpecificParameter = 0x10;
    modeBuffer->ParameterListHeader.BlockDescriptorLength = MODE_BLOCK_DESC_LENGTH;
        
    modeBuffer->ParameterListBlock.DensityCode = 0x7F;
    modeBuffer->ParameterListBlock.BlockLength[0] =
        ((tapeSetMediaParams->BlockSize >> 16) & 0xFF);
    modeBuffer->ParameterListBlock.BlockLength[1] =
        ((tapeSetMediaParams->BlockSize >> 8) & 0xFF);
    modeBuffer->ParameterListBlock.BlockLength[2] =
        (tapeSetMediaParams->BlockSize & 0xFF);

    //
    // Zero CDB in SRB on stack.
    //

    RtlZeroMemory(cdb, MAXIMUM_CDB_SIZE);

    //
    // Prepare SCSI command (CDB)
    //

    srb.CdbLength = CDB6GENERIC_LENGTH;

    cdb->MODE_SELECT.OperationCode = SCSIOP_MODE_SELECT;
    cdb->MODE_SELECT.ParameterListLength = sizeof(MODE_PARM_READ_WRITE_DATA);

    //
    // Set timeout value.
    //

    srb.TimeOutValue = deviceExtension->TimeOutValue;

    //
    // Send SCSI command (CDB) to device
    //
    
    DebugPrint((3,"TapeSetMediaParameters: SendSrb (mode select)\n"));

    status = ScsiClassSendSrbSynchronous(DeviceObject,
                                         &srb,
                                         modeBuffer,
                                         sizeof(MODE_PARM_READ_WRITE_DATA),
                                         TRUE);

    ExFreePool(modeBuffer);

    if (!NT_SUCCESS(status)) {
        DebugPrint((1,"TapeSetMediaParameters: mode select, SendSrb unsuccessful\n"));
    }

    return status;

} // end TapeSetMediaParameters()


NTSTATUS
TapeSetPosition(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )

/*++
Routine Description:

    This routine sets the position of the tape.

Arguments:

    DeviceObject
    Irp

Return Value:

    NTSTATUS

--*/

{
    PDEVICE_EXTENSION            deviceExtension = DeviceObject->DeviceExtension;
    PTAPE_SET_POSITION           tapeSetPosition = Irp->AssociatedIrp.SystemBuffer;
    PTAPE_DATA                   tapeData = (PTAPE_DATA)(deviceExtension + 1);
    BOOLEAN                      changePartition = FALSE;
    PMODE_TAPE_MEDIA_INFORMATION mediaInfoBuffer;
    PMODE_DEVICE_CONFIG_PAGE     deviceConfigBuffer; 
    TAPE_PHYS_POSITION           physPosition;
    UCHAR                        densityCode;
    ULONG                        tapePositionVector;
    ULONG                        tapeBlockLength;
    ULONG                        method;
    SCSI_REQUEST_BLOCK           srb;
    PCDB                         cdb = (PCDB)srb.Cdb;
    NTSTATUS                     status;

    DebugPrint((3,"TapeSetPosition: Enter routine\n"));

    if (tapeSetPosition->Immediate) {
        switch (tapeSetPosition->Method) {
            case TAPE_REWIND:
                DebugPrint((3,"TapeSetPosition: immediate\n"));
                break;

            case TAPE_LOGICAL_BLOCK:
            case TAPE_ABSOLUTE_BLOCK:
            case TAPE_SPACE_END_OF_DATA:
            case TAPE_SPACE_RELATIVE_BLOCKS:
            case TAPE_SPACE_FILEMARKS:
            case TAPE_SPACE_SEQUENTIAL_FMKS:
            case TAPE_SPACE_SETMARKS:
            case TAPE_SPACE_SEQUENTIAL_SMKS:
            default:
                DebugPrint((1,"TapeSetPosition: PositionMethod, immediate -- operation not supported\n"));
                return STATUS_NOT_IMPLEMENTED;
        }
    }

    method = tapeSetPosition->Method;
    tapePositionVector = tapeSetPosition->Offset.LowPart;
    
    if (method == TAPE_LOGICAL_BLOCK) {

        DebugPrint((3,"TapeSetPosition: pseudo logical\n"));

        method = TAPE_PSEUDO_LOGICAL_BLOCK;

        mediaInfoBuffer = ExAllocatePool(NonPagedPoolCacheAligned,
                                         sizeof(MODE_TAPE_MEDIA_INFORMATION));

        if (!mediaInfoBuffer) {
            DebugPrint((1,"TapeSetPosition: insufficient resources (mediaInfoBuffer)\n"));
            return STATUS_INSUFFICIENT_RESOURCES;
        }

        RtlZeroMemory(mediaInfoBuffer, sizeof(MODE_TAPE_MEDIA_INFORMATION));

        //
        // Zero CDB in SRB on stack.
        //

        RtlZeroMemory(cdb, MAXIMUM_CDB_SIZE);

        //
        // Prepare SCSI command (CDB)
        //

        srb.CdbLength = CDB6GENERIC_LENGTH;
        
        cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE;
        cdb->MODE_SENSE.PageCode = MODE_PAGE_MEDIUM_PARTITION;
        cdb->MODE_SENSE.AllocationLength = sizeof(MODE_TAPE_MEDIA_INFORMATION) - 4;

        //
        // Set timeout value.
        //

        srb.TimeOutValue = deviceExtension->TimeOutValue;

        //
        // Send SCSI command (CDB) to device
        //
    
        DebugPrint((3,"TapeSetPosition: SendSrb (mode sense #1)\n"));

        status = ScsiClassSendSrbSynchronous(DeviceObject,
                                             &srb,
                                             mediaInfoBuffer,
                                             sizeof(MODE_TAPE_MEDIA_INFORMATION) - 4,
                                             FALSE);

        if (!NT_SUCCESS(status)) {
            DebugPrint((1,"TapeSetPosition: mode sense #1, SendSrb unsuccessful\n"));
            ExFreePool(mediaInfoBuffer);
            return status;
        }

        if (mediaInfoBuffer->MediumPartPage.FDPBit) {

            deviceConfigBuffer = ExAllocatePool(NonPagedPoolCacheAligned,
                                                sizeof(MODE_DEVICE_CONFIG_PAGE));
            
            if (!deviceConfigBuffer) {
                DebugPrint((1,"TapeSetPosition: insufficient resources (deviceConfigBuffer)\n"));
                ExFreePool(mediaInfoBuffer);
                return STATUS_INSUFFICIENT_RESOURCES;
            }
            
            RtlZeroMemory(deviceConfigBuffer, sizeof(MODE_DEVICE_CONFIG_PAGE));

            //
            // Zero CDB in SRB on stack.
            //

            RtlZeroMemory(cdb, MAXIMUM_CDB_SIZE);
            
            //
            // Prepare SCSI command (CDB)
            //

            srb.CdbLength = CDB6GENERIC_LENGTH;
            
            cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE;
            cdb->MODE_SENSE.Dbd = SETBITON;
            cdb->MODE_SENSE.PageCode = MODE_PAGE_DEVICE_CONFIG;
            cdb->MODE_SENSE.AllocationLength = sizeof(MODE_DEVICE_CONFIG_PAGE);
            
            //
            // Set timeout value.
            //
            
            srb.TimeOutValue = deviceExtension->TimeOutValue;
            
            //
            // Send SCSI command (CDB) to device
            //
    
            DebugPrint((3,"TapeSetPosition: SendSrb (mode sense #2)\n"));

            status = ScsiClassSendSrbSynchronous(DeviceObject,
                                                 &srb,
                                                 deviceConfigBuffer,
                                                 sizeof(MODE_DEVICE_CONFIG_PAGE),
                                                 FALSE);
            
            if (!NT_SUCCESS(status)) {
                DebugPrint((1,"TapeSetPosition: mode sense #2, SendSrb unsuccessful\n"));
                ExFreePool(deviceConfigBuffer);
                ExFreePool(mediaInfoBuffer);
                return status;
            }

            tapeData->CurrentPartition =
                deviceConfigBuffer->DeviceConfigPage.ActivePartition?
                DIRECTORY_PARTITION : DATA_PARTITION;

            ExFreePool(deviceConfigBuffer);

        } else {

            tapeData->CurrentPartition = NO_PARTITIONS;

        }

        densityCode      = mediaInfoBuffer->ParameterListBlock.DensityCode;
        tapeBlockLength  = mediaInfoBuffer->ParameterListBlock.BlockLength[2];
        tapeBlockLength += (mediaInfoBuffer->ParameterListBlock.BlockLength[1] << 8);
        tapeBlockLength += (mediaInfoBuffer->ParameterListBlock.BlockLength[0] << 16);

        ExFreePool(mediaInfoBuffer);

        switch (tapeSetPosition->Partition) {
            case 0:
                break;

            case DIRECTORY_PARTITION:
            case DATA_PARTITION:
                if (tapeData->CurrentPartition != NO_PARTITIONS) {
                    if (tapeSetPosition->Partition
                        != tapeData->CurrentPartition) {
                        changePartition = TRUE;
                    }
                    break;
                }
                // else: fall through to next case

            default:
                DebugPrint((1,"TapeSetPosition: Partition -- invalid parameter\n"));
                return STATUS_INVALID_PARAMETER;
        }

        physPosition =
            TapeLogicalBlockToPhysicalBlock(
                densityCode,
                tapePositionVector,
                tapeBlockLength,
                (BOOLEAN)(
                    (tapeData->CurrentPartition
                        == DIRECTORY_PARTITION)?
                    NOT_FROM_BOT : FROM_BOT
                )
            );

        tapePositionVector = physPosition.SeekBlockAddress;

    }

    //
    // Zero CDB in SRB on stack.
    //

    RtlZeroMemory(cdb, MAXIMUM_CDB_SIZE);

    //
    // Prepare SCSI command (CDB)
    //

    srb.CdbLength = CDB6GENERIC_LENGTH;

    cdb->CDB6GENERIC.Immediate = tapeSetPosition->Immediate;

    switch (method) {
        case TAPE_REWIND:
            DebugPrint((3,"TapeSetPosition: method == rewind\n"));
            cdb->CDB6GENERIC.OperationCode = SCSIOP_REWIND;
            srb.TimeOutValue = 180;
            break;

        case TAPE_PSEUDO_LOGICAL_BLOCK:
        case TAPE_ABSOLUTE_BLOCK:
            DebugPrint((3,"TapeSetPosition: method == locate (absolute)\n"));
            srb.CdbLength = CDB10GENERIC_LENGTH;
            cdb->LOCATE.OperationCode = SCSIOP_LOCATE;
            cdb->LOCATE.CPBit = changePartition? SETBITON : SETBITOFF;
            cdb->LOCATE.BTBit = SETBITON;
            cdb->LOCATE.LogicalBlockAddress[1] =
                ((tapePositionVector >> 16) & 0xFF);
            cdb->LOCATE.LogicalBlockAddress[2] =
                ((tapePositionVector >> 8) & 0xFF);
            cdb->LOCATE.LogicalBlockAddress[3] =
                (tapePositionVector & 0xFF);
            if (changePartition &&
                (tapeSetPosition->Partition == DIRECTORY_PARTITION)) {
                cdb->LOCATE.Partition = 1;
            }
            srb.TimeOutValue = 480;
            if ((physPosition.SpaceBlockCount != 0) &&
                (method == TAPE_PSEUDO_LOGICAL_BLOCK)) {

                //
                // Send SCSI command (CDB) to device
                //

                DebugPrint((3,"TapeSetPosition: SendSrb (locate)\n"));

                status = ScsiClassSendSrbSynchronous(DeviceObject,
                                                     &srb,
                                                     NULL,
                                                     0,
                                                     FALSE);

                if (!NT_SUCCESS(status)) {
                    DebugPrint((1,"TapeSetPosition: locate, SendSrb unsuccessful\n"));
                    return status;
                }

                //
                // Zero CDB in SRB on stack.
                //

                RtlZeroMemory(cdb, MAXIMUM_CDB_SIZE);

                //
                // Prepare SCSI command (CDB)
                //

                srb.CdbLength = CDB6GENERIC_LENGTH;

                DebugPrint((3,"TapeSetPosition: method == space block(s)\n"));
                cdb->SPACE_TAPE_MARKS.OperationCode = SCSIOP_SPACE;
                cdb->SPACE_TAPE_MARKS.Code = 0;
                cdb->SPACE_TAPE_MARKS.NumMarksMSB =
                     ((physPosition.SpaceBlockCount >> 16) & 0xFF);
                cdb->SPACE_TAPE_MARKS.NumMarks =
                     ((physPosition.SpaceBlockCount >> 8) & 0xFF);
                cdb->SPACE_TAPE_MARKS.NumMarksLSB =
                     (physPosition.SpaceBlockCount & 0xFF);
                srb.TimeOutValue = deviceExtension->TimeOutValue;

            }
            break;

        case TAPE_SPACE_END_OF_DATA:
            DebugPrint((3,"TapeSetPosition: method == space to end-of-data\n"));
            cdb->SPACE_TAPE_MARKS.OperationCode = SCSIOP_SPACE;
            cdb->SPACE_TAPE_MARKS.Code = 3;
            srb.TimeOutValue = 480;
            break;

        case TAPE_SPACE_RELATIVE_BLOCKS:
            DebugPrint((3,"TapeSetPosition: method == space blocks\n"));
            cdb->SPACE_TAPE_MARKS.OperationCode = SCSIOP_SPACE;
            cdb->SPACE_TAPE_MARKS.Code = 0;
            cdb->SPACE_TAPE_MARKS.NumMarksMSB =
                ((tapePositionVector >> 16) & 0xFF);
            cdb->SPACE_TAPE_MARKS.NumMarks =
                ((tapePositionVector >> 8) & 0xFF);
            cdb->SPACE_TAPE_MARKS.NumMarksLSB =
                (tapePositionVector & 0xFF);
            srb.TimeOutValue = 4100;
            break;

        case TAPE_SPACE_FILEMARKS:
            DebugPrint((3,"TapeSetPosition: method == space filemarks\n"));
            cdb->SPACE_TAPE_MARKS.OperationCode = SCSIOP_SPACE;
            cdb->SPACE_TAPE_MARKS.Code = 1;
            cdb->SPACE_TAPE_MARKS.NumMarksMSB =
                ((tapePositionVector >> 16) & 0xFF);
            cdb->SPACE_TAPE_MARKS.NumMarks =
                ((tapePositionVector >> 8) & 0xFF);
            cdb->SPACE_TAPE_MARKS.NumMarksLSB =
                (tapePositionVector & 0xFF);
            srb.TimeOutValue = 4100;
            break;

        case TAPE_SPACE_SEQUENTIAL_FMKS:
            DebugPrint((3,"TapeSetPosition: method == space sequential filemarks\n"));
            cdb->SPACE_TAPE_MARKS.OperationCode = SCSIOP_SPACE;
            cdb->SPACE_TAPE_MARKS.Code = 2;
            cdb->SPACE_TAPE_MARKS.NumMarksMSB =
                ((tapePositionVector >> 16) & 0xFF);
            cdb->SPACE_TAPE_MARKS.NumMarks =
                ((tapePositionVector >> 8) & 0xFF);
            cdb->SPACE_TAPE_MARKS.NumMarksLSB =
                (tapePositionVector & 0xFF);
            srb.TimeOutValue = 4100;
            break;

        case TAPE_SPACE_SETMARKS:
            DebugPrint((3,"TapeSetPosition: method == space setmarks\n"));
            cdb->SPACE_TAPE_MARKS.OperationCode = SCSIOP_SPACE;
            cdb->SPACE_TAPE_MARKS.Code = 4;
            cdb->SPACE_TAPE_MARKS.NumMarksMSB =
                ((tapePositionVector >> 16) & 0xFF);
            cdb->SPACE_TAPE_MARKS.NumMarks =
                ((tapePositionVector >> 8) & 0xFF);
            cdb->SPACE_TAPE_MARKS.NumMarksLSB =
                (tapePositionVector & 0xFF);
            srb.TimeOutValue = 4100;
            break;

        default:
            DebugPrint((1,"TapeSetPosition: PositionMethod -- operation not supported\n"));
            return STATUS_NOT_IMPLEMENTED;
    }

    //
    // Send SCSI command (CDB) to device
    //

    DebugPrint((3,"TapeSetPosition: SendSrb (method)\n"));

    status = ScsiClassSendSrbSynchronous(DeviceObject,
                                         &srb,
                                         NULL,
                                         0,
                                         FALSE);

    if (NT_SUCCESS(status)) {
        if (changePartition) {
            tapeData->CurrentPartition = tapeSetPosition->Partition;
        }
    } else {
        DebugPrint((1,"TapeSetPosition: method, SendSrb unsuccessful\n"));
    }

    return status;

} // end TapeSetPosition()


BOOLEAN
TapeVerifyInquiry(
    IN PSCSI_INQUIRY_DATA LunInfo
    )

/*++
Routine Description:

    This routine determines if this driver should claim this device.

Arguments:

    LunInfo

Return Value:

    TRUE  - driver should claim this device.
    FALSE - driver should not claim this device.

--*/

{
    PINQUIRYDATA inquiryData;

    DebugPrint((3,"TapeVerifyInquiry: Enter routine\n"));

    inquiryData = (PVOID)LunInfo->InquiryData;

    //
    //  Determine, from the Product ID field in the
    //  inquiry data, whether or not to "claim" this drive.
    //

    return WhichIsIt(inquiryData)? TRUE : FALSE;

} // end TapeVerifyInquiry()


NTSTATUS
TapeWriteMarks(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )

/*++
Routine Description:

    This routine writes tapemarks on the tape.

Arguments:

    DeviceObject
    Irp

Return Value:

    NTSTATUS

--*/

{
    PDEVICE_EXTENSION  deviceExtension = DeviceObject->DeviceExtension;
    PTAPE_WRITE_MARKS  tapeWriteMarks = Irp->AssociatedIrp.SystemBuffer;
    SCSI_REQUEST_BLOCK srb;
    PCDB               cdb = (PCDB)srb.Cdb;
    NTSTATUS           status;

    DebugPrint((3,"TapeWriteMarks: Enter routine\n"));

    if (tapeWriteMarks->Immediate) {
        switch (tapeWriteMarks->Type) {
            case TAPE_SETMARKS:
            case TAPE_FILEMARKS:
                DebugPrint((3,"TapeWriteMarks: immediate\n"));
                break;

            case TAPE_SHORT_FILEMARKS:
            case TAPE_LONG_FILEMARKS:
            default:
                DebugPrint((1,"TapeWriteMarks: TapemarkType, immediate -- operation not supported\n"));
                return STATUS_NOT_IMPLEMENTED;
        }
    }

    //
    // Zero CDB in SRB on stack.
    //

    RtlZeroMemory(cdb, MAXIMUM_CDB_SIZE);

    //
    // Prepare SCSI command (CDB)
    //

    srb.CdbLength = CDB6GENERIC_LENGTH;

    cdb->WRITE_TAPE_MARKS.OperationCode = SCSIOP_WRITE_FILEMARKS;
    cdb->WRITE_TAPE_MARKS.Immediate = tapeWriteMarks->Immediate;

    switch (tapeWriteMarks->Type) {
        case TAPE_SETMARKS:
            DebugPrint((3,"TapeWriteMarks: TapemarkType == setmarks\n"));
            cdb->WRITE_TAPE_MARKS.WriteSetMarks = SETBITON;
            break;

        case TAPE_FILEMARKS:
            DebugPrint((3,"TapeWriteMarks: TapemarkType == filemarks\n"));
            break;

        case TAPE_SHORT_FILEMARKS:
        case TAPE_LONG_FILEMARKS:
        default:
            DebugPrint((1,"TapeWriteMarks: TapemarkType -- operation not supported\n"));
            return STATUS_NOT_IMPLEMENTED;
    }

    cdb->WRITE_TAPE_MARKS.TransferLength[0] =
        ((tapeWriteMarks->Count >> 16) & 0xFF);
    cdb->WRITE_TAPE_MARKS.TransferLength[1] =
        ((tapeWriteMarks->Count >> 8) & 0xFF);
    cdb->WRITE_TAPE_MARKS.TransferLength[2] =
        (tapeWriteMarks->Count & 0xFF);

    //
    // Set timeout value.
    //

    srb.TimeOutValue = deviceExtension->TimeOutValue;

    //
    // Send SCSI command (CDB) to device
    //

    DebugPrint((3,"TapeWriteMarks: SendSrb (TapemarkType)\n"));

    status = ScsiClassSendSrbSynchronous(DeviceObject,
                                         &srb,
                                         NULL,
                                         0,
                                         FALSE);

    if (!NT_SUCCESS(status)) {
        DebugPrint((1,"TapeWriteMarks: TapemarkType, SendSrb unsuccessful\n"));
    }

    return status;

} // end TapeWriteMarks()


static
NTSTATUS
GetInquiryInformationData (
    IN  PDEVICE_OBJECT  DeviceObject,
    IN  PIRP            Irp,
    OUT PINQUIRYDATA    InquiryBuffer
    )

/*++
Routine Description:

     Get the inquiry parameter list buffer
     
Arguments:

    DeviceObject
    Irp
    InquiryBuffer

Return Value:

    NTSTATUS

--*/

{
    PDEVICE_EXTENSION  deviceExtension = DeviceObject->DeviceExtension;
    SCSI_REQUEST_BLOCK srb;
    PCDB               cdb = (PCDB)srb.Cdb;
    NTSTATUS           status;
     
    RtlZeroMemory(InquiryBuffer, sizeof(INQUIRYDATA));

    //
    // Zero CDB in SRB on stack.
    //

    RtlZeroMemory(cdb, MAXIMUM_CDB_SIZE);

    //
    // Prepare SCSI command (CDB)
    //

    srb.CdbLength = CDB6GENERIC_LENGTH;

    cdb->CDB6INQUIRY.OperationCode = SCSIOP_INQUIRY;
    cdb->CDB6INQUIRY.AllocationLength = INQUIRYDATABUFFERSIZE;

    //
    // Set timeout value.
    //

    srb.TimeOutValue = deviceExtension->TimeOutValue;

    //
    // Send SCSI command (CDB) to device
    //

    status = ScsiClassSendSrbSynchronous(DeviceObject,
                                         &srb,
                                         InquiryBuffer,
                                         INQUIRYDATABUFFERSIZE,
                                         FALSE);
                                
    return status;
}


static
NTSTATUS
RewindToBOT(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )

/*++
Routine Description:

     This routine will rewind to BOT.  Note: BOP is BOT for both the
     data partition and the directory partition; thus, if QFA is enabled,
     a rewind to BOT is a rewind to BOP of the active partition.
    
Arguments:

    DeviceObject
    Irp

Return Value:

    NTSTATUS

--*/

{
    PDEVICE_EXTENSION  deviceExtension = DeviceObject->DeviceExtension;
    SCSI_REQUEST_BLOCK srb;
    PCDB               cdb = (PCDB)srb.Cdb;
    NTSTATUS           status;

    //
    // Zero CDB in SRB on stack.
    //

    RtlZeroMemory(cdb, MAXIMUM_CDB_SIZE);
    
    //
    // Prepare SCSI command (CDB)
    //

    srb.CdbLength = CDB6GENERIC_LENGTH;
    
    cdb->CDB6INQUIRY.OperationCode = SCSIOP_REWIND;
    
    //
    // Set timeout value.
    //
    
    srb.TimeOutValue = 180;
    
    //
    // Send SCSI command (CDB) to device
    //

    status = ScsiClassSendSrbSynchronous(DeviceObject,
                                         &srb,
                                         NULL,
                                         0,
                                         FALSE);
    return status;
}


static
ULONG
WhichIsIt(
    IN PINQUIRYDATA InquiryData
    )

/*++
Routine Description:

    This routine determines a drive's identity from the Product ID field
    in its inquiry data.

Arguments:

    InquiryData (from an Inquiry command)

Return Value:

    driveID

--*/

{
    if (RtlCompareMemory(InquiryData->VendorId,"TANDBERG",8) == 8) {

        if (RtlCompareMemory(InquiryData->ProductId," TDC 3600",9) == 9) {
            return TDC_3600;
        }

        if (RtlCompareMemory(InquiryData->ProductId," TDC 3800",9) == 9) {
            return TDC_3800;
        }

        if (RtlCompareMemory(InquiryData->ProductId," TDC 4100",9) == 9) {
            return TDC_4100;
        }

        if (RtlCompareMemory(InquiryData->ProductId," IBM 4100",9) == 9) {
            return TDC_4100;
        }

        if (RtlCompareMemory(InquiryData->ProductId," TDC 4200",9) == 9) {
            return TDC_4200;
        }

    }

    if (RtlCompareMemory(InquiryData->VendorId,"DEC     ",8) == 8) {

        if (RtlCompareMemory(InquiryData->ProductId,"TZK10",5) == 5) {
            return TDC_3800;
        }

    }

    return (ULONG)0;
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.