File:  [WindowsNT SDKs] / ntddk / src / scsi / scsicdrm / cdrom.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) 1991  Microsoft Corporation

Module Name:

    cdrom.c

Abstract:

    The CDROM class driver tranlates IRPs to SRBs with embedded CDBs
    and sends them to its devices through the port driver.

Author:

    Mike Glass (mglass)

Environment:

    kernel mode only

Notes:

    SCSI Tape, CDRom and Disk class drivers share common routines
    that can be found in the CLASS directory (..\ntos\dd\class).

Revision History:

--*/

#include "ntddk.h"
#include "scsi.h"
#include "class.h"
#include "string.h"

#define DEVICE_EXTENSION_SIZE sizeof(DEVICE_EXTENSION) + sizeof(BOOLEAN)
#define SCSI_CDROM_TIMEOUT  10
#define HITACHI_MODE_DATA_SIZE 12
#define MODE_DATA_SIZE 64

#define PLAY_ACTIVE(DeviceExtension) *((PBOOLEAN) (DeviceExtension+1))


NTSTATUS
DriverEntry(
    IN PDRIVER_OBJECT DriverObject,
    IN PUNICODE_STRING RegistryPath
    );

BOOLEAN
FindScsiCdRoms(
    IN PDRIVER_OBJECT DriveObject,
    IN PDEVICE_OBJECT PortDeviceObject,
    IN UCHAR PortNumber
    );

NTSTATUS
ScsiCdRomOpenClose(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    );

NTSTATUS
ScsiCdRomRead(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    );

NTSTATUS
ScsiCdRomDeviceControl(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    );

NTSTATUS
CreateCdRomDeviceObject(
    IN PDRIVER_OBJECT DriverObject,
    IN PDEVICE_OBJECT PortDeviceObject,
    IN UCHAR PortNumber,
    IN PULONG DeviceCount,
    PIO_SCSI_CAPABILITIES PortCapabilities,
    IN PSCSI_INQUIRY_DATA LunInfo
    );

NTSTATUS
GetTableOfContents(
    IN PDEVICE_OBJECT DeviceObject
    );

VOID
ScanForSpecial(
    PDEVICE_OBJECT DeviceObject,
    PINQUIRYDATA InquiryData,
    PIO_SCSI_CAPABILITIES PortCapabilities
    );

BOOLEAN
CdRomIsPlayActive(
    IN PDEVICE_OBJECT DeviceObject
    );

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

#ifdef ALLOC_PRAGMA
#pragma alloc_text(init, DriverEntry)
#pragma alloc_text(init, FindScsiCdRoms)
#pragma alloc_text(init, CreateCdRomDeviceObject)
#pragma alloc_text(init, ScanForSpecial)
#endif


NTSTATUS
DriverEntry(
    IN PDRIVER_OBJECT DriverObject,
    IN PUNICODE_STRING RegistryPath
    )

/*++

Routine Description:

    This routine initializes the CD-Rom class driver. The class
    driver opens the port driver by name and then receives
    configuration information used to attach to the CDROM devices.

Arguments:

    DriverObject

Return Value:

    NT Status

--*/

{
    UCHAR portNumber = 0;
    NTSTATUS status;
    PFILE_OBJECT fileObject;
    PDEVICE_OBJECT portDeviceObject;
    STRING deviceNameString;
    CCHAR deviceNameBuffer[256];
    UNICODE_STRING unicodeDeviceName;
    BOOLEAN foundOne = FALSE;

    DebugPrint((1,"\n\nSCSI CdRom Class Driver\n"));

    //
    // Set up the device driver entry points.
    //

    DriverObject->MajorFunction[IRP_MJ_CREATE] = ScsiCdRomOpenClose;
    DriverObject->MajorFunction[IRP_MJ_CLOSE] = ScsiCdRomOpenClose;
    DriverObject->MajorFunction[IRP_MJ_READ] = ScsiCdRomRead;
    DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ScsiCdRomDeviceControl;
    DriverObject->MajorFunction[IRP_MJ_SCSI] = ScsiClassInternalIoControl;


    //
    // Open port driver device objects by name.
    //

    do {

        //
        // Create port driver name.
        //

        sprintf(deviceNameBuffer,
                "\\Device\\ScsiPort%d",
                portNumber);

        DebugPrint((2,"ScsiCdRomInitialize: Open %s\n",
                    deviceNameBuffer));

        RtlInitString(&deviceNameString,
                      deviceNameBuffer);

        status = RtlAnsiStringToUnicodeString(&unicodeDeviceName,
                                              &deviceNameString,
                                              TRUE);

        if (!NT_SUCCESS(status)) {
            break;
        }

        status = IoGetDeviceObjectPointer(&unicodeDeviceName,
                                          FILE_READ_ATTRIBUTES,
                                          &fileObject,
                                          &portDeviceObject);

        RtlFreeUnicodeString(&unicodeDeviceName);

        if (NT_SUCCESS(status)) {

            //
            // SCSI port driver exists.
            //

            if (FindScsiCdRoms(DriverObject,
                               portDeviceObject,
                               portNumber++)) {

                foundOne = TRUE;
            }

            //
            // Dereference the file object since the port device pointer is no
            // longer needed.  The claim device code references the port driver
            // pointer that is actually being used.
            //

            ObDereferenceObject(fileObject);
        }

    } while (NT_SUCCESS(status));

    if (foundOne) {
        return STATUS_SUCCESS;
    } else {
        return STATUS_NO_SUCH_DEVICE;
    }

} // end DriverEntry()


BOOLEAN
FindScsiCdRoms(
    IN PDRIVER_OBJECT DriverObject,
    IN PDEVICE_OBJECT PortDeviceObject,
    IN UCHAR PortNumber
    )

/*++

Routine Description:

    Connect to SCSI port driver. Get adapter capabilities and
    SCSI bus configuration information. Search inquiry data
    for CDROM devices to process.

Arguments:

    DriverObject - CDROM class driver object.
    PortDeviceObject - SCSI port driver device object.
    PortNumber - The system ordinal for this scsi adapter.

Return Value:

    TRUE if CDROM device present on this SCSI adapter.

--*/

{
    PIO_SCSI_CAPABILITIES portCapabilities;
    PULONG cdRomCount;
    PCHAR buffer;
    PSCSI_INQUIRY_DATA lunInfo;
    PSCSI_ADAPTER_BUS_INFO  adapterInfo;
    PINQUIRYDATA inquiryData;
    ULONG scsiBus;
    NTSTATUS status;
    BOOLEAN foundDevice = FALSE;

    //
    // Call port driver to get adapter capabilities.
    //

    status = ScsiClassGetCapabilities(PortDeviceObject, &portCapabilities);

    if (!NT_SUCCESS(status)) {
        DebugPrint((1,"FindScsiDevices: ScsiClassGetCapabilities failed\n"));
        return foundDevice;
    }

    //
    // Call port driver to get inquiry information to find cdroms.
    //

    status = ScsiClassGetInquiryData(PortDeviceObject, (PSCSI_ADAPTER_BUS_INFO *) &buffer);

    if (!NT_SUCCESS(status)) {
        DebugPrint((1,"FindScsiDevices: ScsiClassGetInquiryData failed\n"));
        return foundDevice;
    }

    //
    // Get the address of the count of the number of cdroms already initialized.
    //

    cdRomCount = &IoGetConfigurationInformation()->CdRomCount;
    adapterInfo = (PVOID) buffer;

    //
    // For each SCSI bus this adapter supports ...
    //

    for (scsiBus=0; scsiBus < adapterInfo->NumberOfBuses; scsiBus++) {

        //
        // Get the SCSI bus scan data for this bus.
        //

        lunInfo = (PVOID) (buffer + adapterInfo->BusData[scsiBus].InquiryDataOffset);

        //
        // Search list for unclaimed disk devices.
        //

        while (adapterInfo->BusData[scsiBus].InquiryDataOffset) {

            inquiryData = (PVOID)lunInfo->InquiryData;

            if ((inquiryData->DeviceType == READ_ONLY_DIRECT_ACCESS_DEVICE) &&
                (!lunInfo->DeviceClaimed)) {

                DebugPrint((1,"FindScsiDevices: Vendor string is %.24s\n",
                            inquiryData->VendorId));

                //
                // Create device objects for cdrom
                //

                status = CreateCdRomDeviceObject(DriverObject,
                                                 PortDeviceObject,
                                                 PortNumber,
                                                 cdRomCount,
                                                 portCapabilities,
                                                 lunInfo);

                if (NT_SUCCESS(status)) {

                    //
                    // Increment system cdrom device count.
                    //

                    (*cdRomCount)++;

                    //
                    // Indicate that a cdrom device was found.
                    //

                    foundDevice = TRUE;
                }
            }

            //
            // Get next LunInfo.
            //

            if (lunInfo->NextInquiryDataOffset == 0) {
                break;
            }

            lunInfo = (PVOID) (buffer + lunInfo->NextInquiryDataOffset);
        }
    }

    ExFreePool(buffer);

    return foundDevice;

} // end FindScsiCdRoms()


NTSTATUS
CreateCdRomDeviceObject(
    IN PDRIVER_OBJECT DriverObject,
    IN PDEVICE_OBJECT PortDeviceObject,
    IN UCHAR PortNumber,
    IN PULONG DeviceCount,
    IN PIO_SCSI_CAPABILITIES PortCapabilities,
    IN PSCSI_INQUIRY_DATA LunInfo
    )

/*++

Routine Description:

    This routine creates an object for the device and then calls the
    SCSI port driver for media capacity and sector size.

Arguments:

    DriverObject - Pointer to driver object created by system.
    PortDeviceObject - to connect to SCSI port driver.
    DeviceCount - Number of previously installed CDROMs.
    PortCapabilities - Pointer to structure returned by SCSI port
        driver describing adapter capabilites (and limitations).
    LunInfo - Pointer to configuration information for this device.

Return Value:

    NTSTATUS

--*/
{
    UCHAR ntNameBuffer[64];
    UCHAR arcNameBuffer[64];
    STRING ntNameString;
    STRING arcNameString;
    UNICODE_STRING ntUnicodeString;
    UNICODE_STRING arcUnicodeString;
    NTSTATUS status;
    PDEVICE_OBJECT deviceObject = NULL;
    PDEVICE_EXTENSION deviceExtension;
    PVOID senseData = NULL;

    //
    // Claim the device.
    //

    status = ScsiClassClaimDevice(
        PortDeviceObject,
        LunInfo,
        FALSE,
        &PortDeviceObject
        );

    if (!NT_SUCCESS(status)) {
        return(status);
    }


    //
    // Create device object for this device.
    //

    sprintf(ntNameBuffer,
            "\\Device\\CdRom%d",
            *DeviceCount);

    RtlInitString(&ntNameString,
                  ntNameBuffer);

    DebugPrint((2,"CreateCdRomDeviceObjects: Create device object %s\n",
                ntNameBuffer));

    //
    // Convert ANSI string to Unicode.
    //

    status = RtlAnsiStringToUnicodeString(&ntUnicodeString,
                                          &ntNameString,
                                          TRUE);

    if (!NT_SUCCESS(status)) {

        DebugPrint((1,
                    "CreateDiskDeviceObjects: Cannot convert string %s\n",
                    ntNameBuffer));

        //
        // Release the device since an error occured.
        //

        ScsiClassClaimDevice(
            PortDeviceObject,
            LunInfo,
            TRUE,
            NULL
            );

            return(status);
    }

    //
    // Create device object for this CDROM.
    //

    status = IoCreateDevice(DriverObject,
                            DEVICE_EXTENSION_SIZE,
                            &ntUnicodeString,
                            FILE_DEVICE_CD_ROM,
                            FILE_REMOVABLE_MEDIA | FILE_READ_ONLY_DEVICE,
                            FALSE,
                            &deviceObject);


    if (!NT_SUCCESS(status)) {
        DebugPrint((1,"CreateCdRomDeviceObjects: Can not create device %s\n",
                    ntNameBuffer));

        RtlFreeUnicodeString(&ntUnicodeString);
        deviceObject = NULL;
        goto CreateCdRomDeviceObjectExit;
    }

    //
    // Indicate that IRPs should include MDLs.
    //

    deviceObject->Flags |= DO_DIRECT_IO;

    //
    // Set up required stack size in device object.
    //

    deviceObject->StackSize = PortDeviceObject->StackSize + 1;

    deviceExtension = deviceObject->DeviceExtension;

    //
    // Allocate spinlock for split request completion.
    //

    KeInitializeSpinLock(&deviceExtension->SplitRequestSpinLock);

    //
    // This is the physical device.
    //

    deviceExtension->PhysicalDevice = deviceObject;

    //
    // Copy port device object to device extension.
    //

    deviceExtension->PortDeviceObject = PortDeviceObject;

    //
    // Set the alignment requirements for the device based on the
    // host adapter requirements
    //

    if (PortDeviceObject->AlignmentRequirement > deviceObject->AlignmentRequirement) {
        deviceObject->AlignmentRequirement = PortDeviceObject->AlignmentRequirement;
    }

    //
    // Save address of port driver capabilities.
    //

    deviceExtension->PortCapabilities = PortCapabilities;

    //
    // Disable synchronous transfer for CDROM requests.
    //

    deviceExtension->SrbFlags = SRB_FLAGS_DISABLE_SYNCH_TRANSFER;

    //
    // Allocate request sense buffer.
    //

    senseData = ExAllocatePool(NonPagedPoolCacheAligned, SENSE_BUFFER_SIZE);

    if (senseData == NULL) {

        //
        // The buffer cannot be allocated.
        //

        status = STATUS_INSUFFICIENT_RESOURCES;
        goto CreateCdRomDeviceObjectExit;
    }

    //
    // Set the sense data pointer in the device extension.
    //

    deviceExtension->SenseData = senseData;

    //
    // CDROMs are not partitionable so starting offset is 0.
    //

    deviceExtension->StartingOffset.LowPart = 0;
    deviceExtension->StartingOffset.HighPart = 0;

    //
    // Path/TargetId/LUN describes a device location on the SCSI bus.
    // This information comes from the LunInfo buffer.
    //

    deviceExtension->PortNumber = PortNumber;
    deviceExtension->PathId = LunInfo->PathId;
    deviceExtension->TargetId = LunInfo->TargetId;
    deviceExtension->Lun = LunInfo->Lun;

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

    deviceExtension->TimeOutValue = SCSI_CDROM_TIMEOUT;

    //
    // Back pointer to device object.
    //

    deviceExtension->DeviceObject = deviceObject;

    //
    // Create a symbolic link from the cdrom name to the corresponding
    // ARC name, to be used if we're booting off the cdrom.  This will
    // fail if it's not system initialization time; that's fine.  The
    // ARC name looks something like \ArcName\scsi(0)cdrom(0)fdisk(0).
    //

    sprintf(arcNameBuffer,
            "\\ArcName\\scsi(%d)cdrom(%d)fdisk(%d)",
            PortNumber,
            LunInfo->TargetId + LunInfo->PathId * SCSI_MAXIMUM_TARGETS_PER_BUS,
            LunInfo->Lun);

    RtlInitString(&arcNameString, arcNameBuffer);

    status = RtlAnsiStringToUnicodeString(&arcUnicodeString,
                                          &arcNameString,
                                          TRUE);

    if (!NT_SUCCESS(status)) {

        DebugPrint((1,
                    "CreateCdRomDeviceObjects: Cannot convert string %s\n",
                    arcNameBuffer));

        RtlFreeUnicodeString(&ntUnicodeString);

        goto CreateCdRomDeviceObjectExit;
    }

    IoAssignArcName(&arcUnicodeString, &ntUnicodeString);

    RtlFreeUnicodeString(&ntUnicodeString);
    RtlFreeUnicodeString(&arcUnicodeString);

    //
    // Allocate buffer for drive geometry.
    //

    deviceExtension->DiskGeometry =
        ExAllocatePool(NonPagedPool, sizeof(DISK_GEOMETRY));

    if (deviceExtension->DiskGeometry == NULL) {

        status = STATUS_INSUFFICIENT_RESOURCES;
        goto CreateCdRomDeviceObjectExit;
    }

    //
    // Scan for Scsi controllers that require special processing.
    //

    ScanForSpecial(deviceObject,
                   (PINQUIRYDATA) LunInfo->InquiryData,
                   PortCapabilities);

    //
    // Do READ CAPACITY. This SCSI command
    // returns the last sector address on the device
    // and the bytes per sector.
    // These are used to calculate the drive capacity
    // in bytes.
    //

    status = ScsiClassReadDriveCapacity(deviceObject);

    if ((!NT_SUCCESS(status)) ||
        (deviceExtension->DiskGeometry->BytesPerSector == 0)) {

        DebugPrint((1,
                "CreateCdRomDeviceObjects: Can't read capacity for device %s\n",
                ntNameBuffer));

        //
        // Set disk geometry to default values (per ISO 9660).
        //

        deviceExtension->DiskGeometry->BytesPerSector = 2048;
        deviceExtension->SectorShift = 11;

    }

    return(STATUS_SUCCESS);

CreateCdRomDeviceObjectExit:

    //
    // Release the device since an error occured.
    //

    ScsiClassClaimDevice(
        PortDeviceObject,
        LunInfo,
        TRUE,
        NULL
        );

    if (senseData != NULL) {
        ExFreePool(senseData);
    }

    if (deviceExtension->DiskGeometry != NULL) {
        ExFreePool(deviceExtension->DiskGeometry);
    }

    if (deviceObject != NULL) {
        IoDeleteDevice(deviceObject);
    }


    return status;

} // end CreateCdromDeviceObject()


NTSTATUS
ScsiCdRomOpenClose(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )

/*++

Routine Description:

    This routine is called to establish a connection to the CDROM
    class driver. It does no more than return STATUS_SUCCESS.

Arguments:

    DeviceObject - Device object for CDROM drive
    Irp - Open or Close request packet

Return Value:

    NT Status - STATUS_SUCCESS

--*/

{
    //
    // Set status in Irp.
    //

    Irp->IoStatus.Status = STATUS_SUCCESS;

    //
    // Complete request at raised IRQ.
    //

    IoCompleteRequest(Irp, IO_NO_INCREMENT);
    return STATUS_SUCCESS;

} // end ScsiCdRomOpenClose()


NTSTATUS
ScsiCdRomRead(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )

/*++

Routine Description:

    This is the entry called by the I/O system for read requests.
    It builds the SRB and sends it to the port driver.

Arguments:

    DeviceObject - the system object for the device.
    Irp - IRP involved.

Return Value:

    NT Status

--*/

{
    PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
    PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
    ULONG transferByteCount = currentIrpStack->Parameters.Read.Length;
    LARGE_INTEGER startingOffset;
    ULONG maximumTransferLength =
        deviceExtension->PortCapabilities->MaximumTransferLength;
    ULONG transferPages;

    //
    // If the cd is playing music then reject this request.
    //

    if (PLAY_ACTIVE(deviceExtension)) {
        Irp->IoStatus.Status = STATUS_DEVICE_BUSY;
        IoCompleteRequest(Irp, IO_NO_INCREMENT);
        return STATUS_DEVICE_BUSY;
    }

    //
    // Check if volume needs verification.
    //

    if ((DeviceObject->Flags & DO_VERIFY_VOLUME) &&
        !(currentIrpStack->Flags & SL_OVERRIDE_VERIFY_VOLUME)) {

        //
        // if DO_VERIFY_VOLUME bit is set
        // in device object flags, fail request.
        //

        DebugPrint((2,"ScsiCdRomRead: Volume verfication needed\n"));

        IoSetHardErrorOrVerifyDevice(Irp, DeviceObject);

        Irp->IoStatus.Status = STATUS_VERIFY_REQUIRED;
        Irp->IoStatus.Information = 0;

        IoCompleteRequest(Irp, IO_NO_INCREMENT);
        return STATUS_VERIFY_REQUIRED;
    }

    //
    // Verify parameters of this request.
    // Check that ending sector is on disc and
    // that number of bytes to transfer is a multiple of
    // the sector size.
    //

    startingOffset = LiFromUlong(transferByteCount);
    startingOffset = LiAdd(startingOffset,
                           currentIrpStack->Parameters.Read.ByteOffset);

    if (LiGtr( startingOffset, deviceExtension->PartitionLength) ||
        (transferByteCount & deviceExtension->DiskGeometry->BytesPerSector - 1)) {

        DebugPrint((1,"ScsiCdRomRead: Invalid I/O parameters\n"));

        //
        // Fail request with status of invalid parameters.
        //

        Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;

        IoCompleteRequest(Irp, IO_NO_INCREMENT);
        return STATUS_INVALID_PARAMETER;
    }

    //
    // Mark IRP with status pending.
    //

    IoMarkIrpPending(Irp);

    //
    // Calculate number of pages in this transfer.
    //

    transferPages = ADDRESS_AND_SIZE_TO_SPAN_PAGES(
        MmGetMdlVirtualAddress(Irp->MdlAddress),
        currentIrpStack->Parameters.Read.Length);

    //
    // Check if request length is greater than the maximum number of
    // bytes that the hardware can transfer.
    //

    if (currentIrpStack->Parameters.Read.Length > maximumTransferLength ||
        transferPages > deviceExtension->PortCapabilities->MaximumPhysicalPages) {

         DebugPrint((2,"ScsiCdromRead: Request greater than maximum\n"));
         DebugPrint((2,"ScsiCdromRead: Maximum is %lx\n",
                     maximumTransferLength));
         DebugPrint((2,"ScsiCdromRead: Byte count is %lx\n",
                     currentIrpStack->Parameters.Read.Length));

         transferPages =
            deviceExtension->PortCapabilities->MaximumPhysicalPages - 1;

         if (maximumTransferLength > transferPages << PAGE_SHIFT ) {
             maximumTransferLength = transferPages << PAGE_SHIFT;
         }

        //
        // Check that maximum transfer size is not zero.
        //

        if (maximumTransferLength == 0) {
            maximumTransferLength = PAGE_SIZE;
        }

        //
        // Mark IRP with status pending.
        //

        IoMarkIrpPending(Irp);

        //
        // Request greater than port driver maximum.
        // Break up into smaller routines.
        //

        ScsiClassSplitRequest(DeviceObject, Irp, maximumTransferLength);


        return STATUS_PENDING;
    }

    //
    // Build SRB and CDB for this IRP.
    //

    ScsiClassBuildRequest(DeviceObject, Irp);

    //
    // Return the results of the call to the port driver.
    //

    return IoCallDriver(deviceExtension->PortDeviceObject, Irp);

} // end ScsiCdRomRead()


NTSTATUS
ScsiCdRomDeviceControl(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )

/*++

Routine Description:

    This is the NT device control handler for CDROMs.

Arguments:

    DeviceObject - for this CDROM

    Irp - IO Request packet

Return Value:

    NTSTATUS

--*/

{
    PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
    PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
    SCSI_REQUEST_BLOCK srb;
    PCDB cdb = (PCDB)srb.Cdb;
    PVOID outputBuffer;
    ULONG bytesTransferred = 0;
    NTSTATUS status;
    NTSTATUS status2;

RetryControl:

    //
    // Zero the SRB on stack.
    //

    RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));

    Irp->IoStatus.Information = 0;

    switch (irpStack->Parameters.DeviceIoControl.IoControlCode) {

    case IOCTL_CDROM_GET_DRIVE_GEOMETRY:

        DebugPrint((2,"ScsiCdRomDeviceControl: Get drive geometry\n"));

        if ( irpStack->Parameters.DeviceIoControl.OutputBufferLength <
            sizeof( DISK_GEOMETRY ) ) {

            status = STATUS_INFO_LENGTH_MISMATCH;
            break;
        }

        //
        // Issue ReadCapacity to update device extension
        // with information for current media.
        //

        status = ScsiClassReadDriveCapacity(DeviceObject);

        if ((!NT_SUCCESS(status)) ||
            (deviceExtension->DiskGeometry->BytesPerSector == 0)) {
    
            //
            // Set disk geometry to default values (per ISO 9660).
            //
    
            deviceExtension->DiskGeometry->BytesPerSector = 2048;
            deviceExtension->SectorShift = 11;
        }
    
        //
        // Copy drive geometry information from device extension.
        //

        RtlMoveMemory(Irp->AssociatedIrp.SystemBuffer,
                      deviceExtension->DiskGeometry,
                      sizeof(DISK_GEOMETRY));

        status = STATUS_SUCCESS;
        Irp->IoStatus.Information = sizeof(DISK_GEOMETRY);

        break;

    case IOCTL_CDROM_GET_LAST_SESSION:

        //
        // Set format to return first and last session numbers.
        //

        cdb->READ_TOC.Format = GET_LAST_SESSION;

        //
        // Fall through to READ TOC code.
        //

    case IOCTL_CDROM_READ_TOC:

        {
            PCDROM_TOC toc = Irp->AssociatedIrp.SystemBuffer;
            ULONG transferBytes;

            DebugPrint((2,"CdRomDeviceControl: Read TOC\n"));

            //
            // If the cd is playing music then reject this request.
            //

            if (CdRomIsPlayActive(DeviceObject)) {
                Irp->IoStatus.Status = STATUS_DEVICE_BUSY;
                IoCompleteRequest(Irp, IO_NO_INCREMENT);
                return STATUS_DEVICE_BUSY;
            }

            //
            // Read TOC is 10 byte CDB.
            //

            srb.CdbLength = 10;

            cdb->READ_TOC.OperationCode = SCSIOP_READ_TOC;

            if (irpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_READ_TOC) {

                //
                // Use MSF addressing if not request for session information.
                //

                cdb->READ_TOC.Msf = CDB_USE_MSF;
            }

            //
            // Start at beginning of disc.
            //

            cdb->READ_TOC.StartingTrack = 0;

            //
            // Set size of TOC structure.
            //

            transferBytes =
                irpStack->Parameters.Read.Length >
                    sizeof(CDROM_TOC) ? sizeof(CDROM_TOC):
                    irpStack->Parameters.Read.Length;

            cdb->READ_TOC.AllocationLength[0] = (UCHAR) (transferBytes >> 8);
            cdb->READ_TOC.AllocationLength[1] = (UCHAR) (transferBytes & 0xFF);

            cdb->READ_TOC.Control = 0;

            //
            // Set timeout value.
            //

            srb.TimeOutValue = deviceExtension->TimeOutValue;

            status = ScsiClassSendSrbSynchronous(DeviceObject,
                                        &srb,
                                        toc,
                                        transferBytes,
                                        FALSE);

            //
            // Check for data underrun.
            //

            if (status==STATUS_DATA_OVERRUN) {
                status = STATUS_SUCCESS;
            }

            Irp->IoStatus.Information  = srb.DataTransferLength;
        }

        break;

    case IOCTL_CDROM_PLAY_AUDIO_MSF:

        {
            PCDROM_PLAY_AUDIO_MSF inputBuffer = Irp->AssociatedIrp.SystemBuffer;

            //
            // Play Audio MSF
            //

            DebugPrint((2,"ScsiCdRomDeviceControl: Play audio MSF\n"));

            if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
                sizeof(CDROM_PLAY_AUDIO_MSF)) {

                //
                // Indicate unsuccessful status.
                //

                status = STATUS_BUFFER_TOO_SMALL;
                break;
            }

            cdb->PLAY_AUDIO_MSF.OperationCode = SCSIOP_PLAY_AUDIO_MSF;

            cdb->PLAY_AUDIO_MSF.StartingM = inputBuffer->StartingM;
            cdb->PLAY_AUDIO_MSF.StartingS = inputBuffer->StartingS;
            cdb->PLAY_AUDIO_MSF.StartingF = inputBuffer->StartingF;

            cdb->PLAY_AUDIO_MSF.EndingM = inputBuffer->EndingM;
            cdb->PLAY_AUDIO_MSF.EndingS = inputBuffer->EndingS;
            cdb->PLAY_AUDIO_MSF.EndingF = inputBuffer->EndingF;

            srb.CdbLength = 10;

            //
            // Set timeout value.
            //

            srb.TimeOutValue = deviceExtension->TimeOutValue;

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

            if (NT_SUCCESS(status)) {
                PLAY_ACTIVE(deviceExtension) = TRUE;
            }
        }

        break;

    case IOCTL_CDROM_SEEK_AUDIO_MSF:

        {
            PCDROM_SEEK_AUDIO_MSF inputBuffer = Irp->AssociatedIrp.SystemBuffer;

            //
            // Seek Audio MSF
            //

            DebugPrint((2,"ScsiCdRomDeviceControl: Seek audio MSF\n"));

            if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
                sizeof(CDROM_SEEK_AUDIO_MSF)) {

                //
                // Indicate unsuccessful status.
                //

                status = STATUS_BUFFER_TOO_SMALL;
                break;
            }

            cdb->PLAY_AUDIO_MSF.OperationCode = SCSIOP_PLAY_AUDIO_MSF;

            cdb->PLAY_AUDIO_MSF.StartingM = inputBuffer->M;
            cdb->PLAY_AUDIO_MSF.StartingS = inputBuffer->S;
            cdb->PLAY_AUDIO_MSF.StartingF = inputBuffer->F;

            cdb->PLAY_AUDIO_MSF.EndingM = inputBuffer->M;
            cdb->PLAY_AUDIO_MSF.EndingS = inputBuffer->S;
            cdb->PLAY_AUDIO_MSF.EndingF = inputBuffer->F;

            srb.CdbLength = 10;

            //
            // Set timeout value.
            //

            srb.TimeOutValue = deviceExtension->TimeOutValue;

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

        }

        break;

    case IOCTL_CDROM_PAUSE_AUDIO:

        //
        // Pause audio
        //

        DebugPrint((2, "ScsiCdRomDeviceControl: Pause audio\n"));

        PLAY_ACTIVE(deviceExtension) = FALSE;

        cdb->PAUSE_RESUME.OperationCode = SCSIOP_PAUSE_RESUME;

        cdb->PAUSE_RESUME.Action = CDB_AUDIO_PAUSE;

        srb.CdbLength = 10;

        //
        // Set timeout value.
        //

        srb.TimeOutValue = deviceExtension->TimeOutValue;

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

        break;

    case IOCTL_CDROM_RESUME_AUDIO:

        //
        // Resume audio
        //

        DebugPrint((2, "ScsiCdRomDeviceControl: Resume audio\n"));

        cdb->PAUSE_RESUME.OperationCode = SCSIOP_PAUSE_RESUME;

        cdb->PAUSE_RESUME.Action = CDB_AUDIO_RESUME;

        srb.CdbLength = 10;

        //
        // Set timeout value.
        //

        srb.TimeOutValue = deviceExtension->TimeOutValue;

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

        break;

    case IOCTL_CDROM_READ_Q_CHANNEL:

        {

        PSUB_Q_CHANNEL_DATA userChannelData =
                         Irp->AssociatedIrp.SystemBuffer;
        PCDROM_SUB_Q_DATA_FORMAT inputBuffer =
                         Irp->AssociatedIrp.SystemBuffer;
        PSUB_Q_CHANNEL_DATA subQPtr;

        //
        // Allocate buffer for subq channel information.
        //

        subQPtr = ExAllocatePool(NonPagedPoolCacheAligned,
                                 sizeof(SUB_Q_CHANNEL_DATA));

        if (!subQPtr) {
            status = STATUS_INSUFFICIENT_RESOURCES;
            Irp->IoStatus.Information = 0;
            break;
        }

        //
        // Read Subchannel Q
        //

        DebugPrint((2,
            "ScsiCdRomDeviceControl: Read channel Q Format %u\n", inputBuffer->Format ));

        cdb->SUBCHANNEL.OperationCode = SCSIOP_READ_SUB_CHANNEL;

        //
        // Always logical unit 0, but only use MSF addressing
        // for IOCTL_CDROM_CURRENT_POSITION
        //

        if (inputBuffer->Format==IOCTL_CDROM_CURRENT_POSITION)
            cdb->SUBCHANNEL.Msf = CDB_USE_MSF;

        //
        // Return subchannel data
        //

        cdb->SUBCHANNEL.SubQ = CDB_SUBCHANNEL_BLOCK;

        //
        // Specify format of informatin to return
        //

        cdb->SUBCHANNEL.Format = inputBuffer->Format;

        //
        // Specify which track to access (only used by Track ISRC reads)
        //

        if (inputBuffer->Format==IOCTL_CDROM_TRACK_ISRC)
            cdb->SUBCHANNEL.TrackNumber = inputBuffer->Track;

        //
        // Set size of channel data -- however, this is dependent on
        // what information we are requesting (which Format)
        //

        switch( inputBuffer->Format ) {

            case IOCTL_CDROM_CURRENT_POSITION:
                bytesTransferred = sizeof(SUB_Q_CURRENT_POSITION);
                break;

            case IOCTL_CDROM_MEDIA_CATALOG:
                bytesTransferred = sizeof(SUB_Q_MEDIA_CATALOG_NUMBER);
                break;

            case IOCTL_CDROM_TRACK_ISRC:
                bytesTransferred = sizeof(SUB_Q_TRACK_ISRC);
                break;
        }

        cdb->SUBCHANNEL.AllocationLength[0] = (UCHAR) (bytesTransferred >> 8);
        cdb->SUBCHANNEL.AllocationLength[1] = (UCHAR) (bytesTransferred &  0xFF);

        srb.CdbLength = 10;

        //
        // Set timeout value.
        //

        srb.TimeOutValue = deviceExtension->TimeOutValue;

        status = ScsiClassSendSrbSynchronous(DeviceObject,
                                    &srb,
                                    subQPtr,
                                    bytesTransferred,
                                    FALSE);

        if (NT_SUCCESS(status)) {
#if DBG
            switch( inputBuffer->Format ) {

            case IOCTL_CDROM_CURRENT_POSITION:
                DebugPrint((3,"ScsiCdromDeviceControl: Audio Status is %u\n", subQPtr->CurrentPosition.Header.AudioStatus ));
                DebugPrint((3,"ScsiCdromDeviceControl: ADR = 0x%x\n", subQPtr->CurrentPosition.ADR ));
                DebugPrint((3,"ScsiCdromDeviceControl: Control = 0x%x\n", subQPtr->CurrentPosition.Control ));
                DebugPrint((3,"ScsiCdromDeviceControl: Track = %u\n", subQPtr->CurrentPosition.TrackNumber ));
                DebugPrint((3,"ScsiCdromDeviceControl: Index = %u\n", subQPtr->CurrentPosition.IndexNumber ));
                break;

            case IOCTL_CDROM_MEDIA_CATALOG:
                DebugPrint((3,"ScsiCdromDeviceControl: Audio Status is %u\n", subQPtr->MediaCatalog.Header.AudioStatus ));
                DebugPrint((3,"ScsiCdromDeviceControl: Mcval is %u\n", subQPtr->MediaCatalog.Mcval ));
                break;

            case IOCTL_CDROM_TRACK_ISRC:
                DebugPrint((3,"ScsiCdromDeviceControl: Audio Status is %u\n", subQPtr->TrackIsrc.Header.AudioStatus ));
                DebugPrint((3,"ScsiCdromDeviceControl: Tcval is %u\n", subQPtr->TrackIsrc.Tcval ));
                break;

            }
#endif

            //
            // Update the play active status.
            //

            if (subQPtr->CurrentPosition.Header.AudioStatus == AUDIO_STATUS_IN_PROGRESS) {

                PLAY_ACTIVE(deviceExtension) = TRUE;

            } else {

                PLAY_ACTIVE(deviceExtension) = FALSE;

            }

            //
            // Check if output buffer is large enough to contain
            // the data.
            //

            if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
                bytesTransferred) {

                bytesTransferred =
                    irpStack->Parameters.DeviceIoControl.OutputBufferLength;
            }

            RtlMoveMemory(userChannelData,
                          subQPtr,
                          bytesTransferred);

            Irp->IoStatus.Information = bytesTransferred;
        } else {

            PLAY_ACTIVE(deviceExtension) = FALSE;

        }

        ExFreePool(subQPtr);

        break;

        }

    case IOCTL_CDROM_GET_CONTROL:

        DebugPrint((2, "ScsiCdRomDeviceControl: Get audio control\n"));

        //
        // Verify user buffer is large enough for the data.
        //

        if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
            sizeof(CDROM_AUDIO_CONTROL)) {

            //
            // Indicate unsuccessful status and no data transferred.
            //

            status = STATUS_BUFFER_TOO_SMALL;
            Irp->IoStatus.Information = 0;

        } else {

            PAUDIO_OUTPUT audioOutput;
            PCDROM_AUDIO_CONTROL audioControl = Irp->AssociatedIrp.SystemBuffer;

            //
            // Allocate buffer for volume control information.
            //

            outputBuffer = ExAllocatePool(NonPagedPoolCacheAligned,
                                         MODE_DATA_SIZE);

            if (!outputBuffer) {
                status = STATUS_INSUFFICIENT_RESOURCES;
                Irp->IoStatus.Information = 0;
                break;
            }

            //
            // Get audio control information
            //

            cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE;
            cdb->MODE_SENSE.PageCode = CDROM_AUDIO_CONTROL_PAGE;
            cdb->MODE_SENSE.AllocationLength = MODE_DATA_SIZE;

            //
            // Disable block descriptors.
            //

            cdb->MODE_SENSE.Dbd = TRUE;

            srb.CdbLength = 6;

            //
            // Set timeout value.
            //

            srb.TimeOutValue = deviceExtension->TimeOutValue;

            status = ScsiClassSendSrbSynchronous(DeviceObject,
                                        &srb,
                                        outputBuffer,
                                        MODE_DATA_SIZE,
                                        FALSE);

            if (SRB_STATUS(srb.SrbStatus) == SRB_STATUS_DATA_OVERRUN) {
                status = STATUS_SUCCESS;
            }

            if (NT_SUCCESS(status)) {

                audioOutput = ScsiClassFindModePage( outputBuffer,
                                                     srb.DataTransferLength,
                                                     CDROM_AUDIO_CONTROL_PAGE);
                //
                // Verify the page is as big as expected.
                //

                bytesTransferred = (PCHAR) audioOutput - (PCHAR) outputBuffer +
                    sizeof(AUDIO_OUTPUT);

                if (audioOutput != NULL &&
                    srb.DataTransferLength >= bytesTransferred) {

                    audioControl->LbaFormat = audioOutput->LbaFormat;

                    audioControl->LogicalBlocksPerSecond =
                        (audioOutput->LogicalBlocksPerSecond[0] << (UCHAR)8) |
                        audioOutput->LogicalBlocksPerSecond[1];

                    Irp->IoStatus.Information = sizeof(CDROM_AUDIO_CONTROL);

                } else {
                    status = STATUS_INVALID_DEVICE_REQUEST;
                }

            }

            ExFreePool(outputBuffer);
        }

        break;

    case IOCTL_CDROM_GET_VOLUME:

        DebugPrint((2, "ScsiCdRomDeviceControl: Get volume control\n"));

        //
        // Verify user buffer is large enough for data.
        //

        if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
            sizeof(VOLUME_CONTROL)) {

            //
            // Indicate unsuccessful status and no data transferred.
            //

            status = STATUS_BUFFER_TOO_SMALL;
            Irp->IoStatus.Information = 0;

        } else {

            PAUDIO_OUTPUT audioOutput;
            PVOLUME_CONTROL volumeControl = Irp->AssociatedIrp.SystemBuffer;
            ULONG i;

            //
            // Allocate buffer for volume control information.
            //

            outputBuffer = ExAllocatePool(NonPagedPoolCacheAligned,
                                         MODE_DATA_SIZE);

            if (!outputBuffer) {
                status = STATUS_INSUFFICIENT_RESOURCES;
                Irp->IoStatus.Information = 0;
                break;
            }

            //
            // In case not as much as expected is returned zero
            // all of this.
            //

            RtlZeroMemory(outputBuffer, MODE_DATA_SIZE);

            //
            // Get volume control information
            //

            cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE;
            cdb->MODE_SENSE.PageCode = CDROM_AUDIO_CONTROL_PAGE;
            cdb->MODE_SENSE.AllocationLength = MODE_DATA_SIZE;

            srb.CdbLength = 6;

            //
            // Set timeout value.
            //

            srb.TimeOutValue = deviceExtension->TimeOutValue;

            status = ScsiClassSendSrbSynchronous(DeviceObject,
                                        &srb,
                                        outputBuffer,
                                        MODE_DATA_SIZE,
                                        FALSE);

            if (SRB_STATUS(srb.SrbStatus) == SRB_STATUS_DATA_OVERRUN) {
                status = STATUS_SUCCESS;
            }

            if (NT_SUCCESS(status)) {

                audioOutput = ScsiClassFindModePage( outputBuffer,
                                                     srb.DataTransferLength,
                                                     CDROM_AUDIO_CONTROL_PAGE);

                //
                // Verify the page is as big as expected.
                //

                bytesTransferred = (PCHAR) audioOutput - (PCHAR) outputBuffer +
                    sizeof(AUDIO_OUTPUT);

                if (audioOutput != NULL &&
                    srb.DataTransferLength >= bytesTransferred) {

                    for (i=0; i<4; i++) {
                        volumeControl->PortVolume[i] =
                            audioOutput->PortOutput[i].Volume;
                    }

                    //
                    // Set bytes transferred in IRP.
                    //

                    Irp->IoStatus.Information = sizeof(VOLUME_CONTROL);

                } else {
                    status = STATUS_INVALID_DEVICE_REQUEST;
                }

            }

            //
            // Free buffer.
            //

            ExFreePool(outputBuffer);
        }

        break;

    case IOCTL_CDROM_SET_VOLUME:

    DebugPrint((2, "ScsiCdRomDeviceControl: Set volume control\n"));

    {

        PAUDIO_OUTPUT audioOutput;
        PAUDIO_OUTPUT audioInput;
        PVOID inputBuffer;
        PVOLUME_CONTROL volumeControl = Irp->AssociatedIrp.SystemBuffer;
        ULONG i;

        if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
            sizeof(VOLUME_CONTROL)) {

            //
            // Indicate unsuccessful status.
            //

            status = STATUS_BUFFER_TOO_SMALL;
            break;
        }

        //
        // Get the current audio contorl information so that the
        // port control information can be filled in.
        // Allocate buffer for volume control information.
        //

        inputBuffer = ExAllocatePool(NonPagedPoolCacheAligned,
                                     MODE_DATA_SIZE);

        if (!inputBuffer) {
            status = STATUS_INSUFFICIENT_RESOURCES;
            Irp->IoStatus.Information = 0;
            break;
        }

        //
        // In case not as much as expected is returned zero
        // all of this.
        //

        RtlZeroMemory(inputBuffer, MODE_DATA_SIZE);

        //
        // Get volume control information
        //

        cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE;
        cdb->MODE_SENSE.PageCode = CDROM_AUDIO_CONTROL_PAGE;
        cdb->MODE_SENSE.AllocationLength = MODE_DATA_SIZE;

        srb.CdbLength = 6;

        //
        // Set timeout value.
        //

        srb.TimeOutValue = deviceExtension->TimeOutValue;

        status = ScsiClassSendSrbSynchronous(DeviceObject,
                                    &srb,
                                    inputBuffer,
                                    MODE_DATA_SIZE,
                                    FALSE);

        if (SRB_STATUS(srb.SrbStatus) == SRB_STATUS_DATA_OVERRUN) {
            status = STATUS_SUCCESS;
        }

        if (NT_SUCCESS(status)) {

            audioInput = ScsiClassFindModePage( inputBuffer,
                                                 srb.DataTransferLength,
                                                 CDROM_AUDIO_CONTROL_PAGE);

            if (audioInput == NULL) {
                status = STATUS_INVALID_DEVICE_REQUEST;
            }

            //
            // Verify the page is as big as expected.
            //

            i = (PCHAR) audioInput - (PCHAR) inputBuffer + sizeof(AUDIO_OUTPUT);

            if (srb.DataTransferLength < i) {
                status = STATUS_INVALID_DEVICE_REQUEST;
            }

        }

        if (!NT_SUCCESS(status)) {

            //
            // Free buffer.
            //

            ExFreePool(inputBuffer);

            break;
        }

        //
        // Mode select buffer is the size of the audio page plus a
        // mode parameter header.
        //

        bytesTransferred = sizeof(AUDIO_OUTPUT) + sizeof(MODE_PARAMETER_HEADER);

        //
        // Allocate buffer for volume control information.
        //

        outputBuffer = ExAllocatePool(NonPagedPoolCacheAligned,
                                     bytesTransferred);

        if (!outputBuffer) {
            status = STATUS_INSUFFICIENT_RESOURCES;
            Irp->IoStatus.Information = 0;
            break;
        }

        //
        // Zero the buffer.  The mode parameter header will be left as zeros.
        // Also clear the srb again.
        //

        RtlZeroMemory(outputBuffer, bytesTransferred);
        RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));

        //
        // Set volume control information
        //

        cdb->MODE_SELECT.OperationCode = SCSIOP_MODE_SELECT;
        cdb->MODE_SELECT.ParameterListLength = (UCHAR) bytesTransferred;
        cdb->MODE_SELECT.PFBit = 1;

        srb.CdbLength = 6;

        audioOutput = (PAUDIO_OUTPUT) ((PCHAR) outputBuffer
                        + sizeof(MODE_PARAMETER_HEADER));

        //
        // Fill in the volume setting and the port control.
        //

        for (i=0; i<4; i++) {
            audioOutput->PortOutput[i].Volume =
                volumeControl->PortVolume[i];
            audioOutput->PortOutput[i].ChannelSelection =
                audioInput->PortOutput[i].ChannelSelection;
        }

        audioOutput->CodePage = CDROM_AUDIO_CONTROL_PAGE;
        audioOutput->ParameterLength = sizeof(AUDIO_OUTPUT) - 2;
        audioOutput->Immediate = MODE_SELECT_IMMEDIATE;

        //
        // Set timeout value.
        //

        srb.TimeOutValue = deviceExtension->TimeOutValue;

        status = ScsiClassSendSrbSynchronous(DeviceObject,
                                    &srb,
                                    outputBuffer,
                                    bytesTransferred,
                                    TRUE);

        //
        // Set bytes transferred in IRP.
        //

        Irp->IoStatus.Information = sizeof(VOLUME_CONTROL);

        //
        // Free buffers.
        //

        ExFreePool(inputBuffer);
        ExFreePool(outputBuffer);

        break;
    }

    case IOCTL_CDROM_STOP_AUDIO:

        //
        // Stop play.
        //

        DebugPrint((2, "ScsiCdRomDeviceControl: Stop audio\n"));

        PLAY_ACTIVE(deviceExtension) = FALSE;

        cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;

        cdb->START_STOP.Immediate = 1;
        cdb->START_STOP.Start = 0;
        cdb->START_STOP.LoadEject = 0;

        srb.CdbLength = 6;

        //
        // Set timeout value.
        //

        srb.TimeOutValue = deviceExtension->TimeOutValue;

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

    case IOCTL_CDROM_CHECK_VERIFY:

        srb.CdbLength = 6;

        cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;

        //
        // Set timeout value.
        //

        srb.TimeOutValue = deviceExtension->TimeOutValue * 2;

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

        //
        // If the status is verify required, redo the test unit ready to
        // verify that the cd-rom is done spinning up.
        // ScsiClassSendSrbSynchronous will wait for a while for the device to
        // spin up.
        //
        
        if (status == STATUS_VERIFY_REQUIRED) {

            srb.CdbLength = 6;
    
            cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;
    
            //
            // Set timeout value.
            //
    
            srb.TimeOutValue = deviceExtension->TimeOutValue * 2;
    
            status2 = ScsiClassSendSrbSynchronous(DeviceObject,
                                        &srb,
                                        NULL,
                                        0,
                                        FALSE);

            DebugPrint((1, "ScsiCdromDeviceControl: Status from second test unit ready is: %lx\n", status2));
        }

        //
        // Update the play active flag.
        //

        if (!CdRomIsPlayActive(DeviceObject) &&
             LiEqlZero(deviceExtension->PartitionLength)) {

            //
            // If play is not active and the volume needs verification then
            // get read capicity again.
            //

            status2 = ScsiClassReadDriveCapacity(DeviceObject);

            if ((!NT_SUCCESS(status2)) ||
                (deviceExtension->DiskGeometry->BytesPerSector == 0)) {
        
                //
                // Set disk geometry to default values (per ISO 9660).
                //
        
                deviceExtension->DiskGeometry->BytesPerSector = 2048;
                deviceExtension->SectorShift = 11;
            }
        }

        break;
        
    default:

        //
        // Pass the request to the common device control routine.
        //

        return(ScsiClassDeviceControl(DeviceObject, Irp));

        break;

    } // end switch()

    if (status == STATUS_VERIFY_REQUIRED) {

        //
        // If the status is verified required and the this request
        // should bypass verify required then retry the request.
        //

        if (irpStack->Flags & SL_OVERRIDE_VERIFY_VOLUME) {

            status = STATUS_IO_DEVICE_ERROR;
            goto RetryControl;

        }

    }

    if (IoIsErrorUserInduced(status)) {

        IoSetHardErrorOrVerifyDevice(Irp, DeviceObject);

    }

    //
    // Update IRP with completion status.
    //

    Irp->IoStatus.Status = status;

    //
    // Complete the request.
    //

    IoCompleteRequest(Irp, IO_DISK_INCREMENT);
    DebugPrint((2, "ScsiCdromDeviceControl: Status is %lx\n", status));
    return status;

} // end ScsiCdromDeviceControl()


VOID
ScanForSpecial(
    PDEVICE_OBJECT DeviceObject,
    PINQUIRYDATA InquiryData,
    PIO_SCSI_CAPABILITIES PortCapabilities
    )

/*++

Routine Description:

    This function checks to see if an SCSI logical unit requires an special
    initialization or error processing.

Arguments:

    DeviceObject - Supplies the device object to be tested.

    InquiryData - Supplies the inquiry data returned by the device of interest.

    PortCapabilities - Supplies the capabilities of the device object.

Return Value:

    None.

--*/

{
    PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;

    //
    // Look for a Hitachi CDR-1750. Read-ahead must be disabled in order
    // to get this cdrom drive to work on scsi adapters that use PIO.
    //

    if ((strncmp(InquiryData->VendorId, "HITACHI CDR-1750S", strlen("HITACHI CDR-1750S")) == 0 ||
        strncmp(InquiryData->VendorId, "HITACHI CDR-3650/1650S", strlen("HITACHI CDR-3650/1650S")) == 0)
        && PortCapabilities->AdapterUsesPio) {

        DebugPrint((1, "ScsiCdrom ScanForSpecial:  Found Hitachi CDR-1750S.\n"));

        //
        // Setup an error handler to reinitialize the cd rom after it is reset.
        //

        deviceExtension->ClassError = HitachProcessError;
    }

    return;
}

VOID
HitachProcessError(
    PDEVICE_OBJECT DeviceObject,
    PSCSI_REQUEST_BLOCK Srb,
    NTSTATUS *Status,
    BOOLEAN *Retry
    )
/*++

Routine Description:

   This routine checks the type of error.  If the error indicates CD-ROM the
   CD-ROM needs to be reinitialized then a Mode sense command is sent to the
   device.  This command disables read-ahead for the device.

Arguments:

    DeviceObject - Supplies a pointer to the device object.

    Srb - Supplies a pointer to the failing Srb.

    Status - Not used.

    Retry - Not used.

Return Value:

    None.

--*/

{
    PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
    PSENSE_DATA senseBuffer = Srb->SenseInfoBuffer;
    LARGE_INTEGER largeInt = LiFromUlong(1);
    PUCHAR modePage;
    PIO_STACK_LOCATION irpStack;
    PIRP irp;
    PSCSI_REQUEST_BLOCK srb;
    PCOMPLETION_CONTEXT context;
    PCDB cdb;
    ULONG alignment;

    UNREFERENCED_PARAMETER(Status);
    UNREFERENCED_PARAMETER(Retry);

    //
    // Check the status.  The initialization command only needs to be sent
    // if UNIT ATTENTION is returned.
    //

    if (!(Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID)) {

        //
        // The drive does not require reinitialization.
        //

        return;
    }

    //
    // Found a bad HITACHI cd-rom.  These devices do not work with PIO
    // adapters when read-ahead is enabled.  Read-ahead is disabled by
    // a mode select command.  The mode select page code is zero and the
    // length is 6 bytes.  All of the other bytes should be zero.
    //


    if ((senseBuffer->SenseKey & 0xf) == SCSI_SENSE_UNIT_ATTENTION) {

        DebugPrint((1, "HitachiProcessError: Reinitializing the CD-ROM.\n"));

        //
        // Send the special mode select command to disable read-ahead
        // on the CD-ROM reader.
        //

        alignment = DeviceObject->AlignmentRequirement ?
            DeviceObject->AlignmentRequirement : 1;

        context = ExAllocatePool(
            NonPagedPool,
            sizeof(COMPLETION_CONTEXT) +  HITACHI_MODE_DATA_SIZE + alignment
            );

        if (context == NULL) {

            //
            // If there is not enough memory to fulfill this request,
            // simply return. A subsequent retry will fail and another
            // chance to start the unit.
            //

            return;
        }

        context->DeviceObject = DeviceObject;
        srb = &context->Srb;

        RtlZeroMemory(srb, SCSI_REQUEST_BLOCK_SIZE);

        //
        // Write length to SRB.
        //

        srb->Length = SCSI_REQUEST_BLOCK_SIZE;

        //
        // Set up SCSI bus address.
        //

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

        srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
        srb->TimeOutValue = deviceExtension->TimeOutValue;

        //
        // Set the transfer length.
        //

        srb->DataTransferLength = HITACHI_MODE_DATA_SIZE;
        srb->SrbFlags = SRB_FLAGS_DATA_OUT | SRB_FLAGS_DISABLE_AUTOSENSE | SRB_FLAGS_DISABLE_SYNCH_TRANSFER;

        //
        // The data buffer must be aligned.
        //

        srb->DataBuffer = (PVOID) (((ULONG) (context + 1) + (alignment - 1)) &
            ~(alignment - 1));


        //
        // Build the HITACHI read-ahead mode select CDB.
        //

        srb->CdbLength = 6;
        cdb = (PCDB)srb->Cdb;
        cdb->MODE_SENSE.LogicalUnitNumber = srb->Lun;
        cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SELECT;
        cdb->MODE_SENSE.AllocationLength = HITACHI_MODE_DATA_SIZE;

        //
        // Initialize the mode sense data.
        //

        modePage = srb->DataBuffer;

        RtlZeroMemory(modePage, HITACHI_MODE_DATA_SIZE);

        //
        // Set the page length field to 6.
        //

        modePage[5] = 6;

        //
        // Build the asynchronous request to be sent to the port driver.
        //

        irp = IoBuildAsynchronousFsdRequest(IRP_MJ_WRITE,
                                           DeviceObject,
                                           srb->DataBuffer,
                                           srb->DataTransferLength,
                                           &largeInt,
                                           NULL);

        IoSetCompletionRoutine(irp,
                   (PIO_COMPLETION_ROUTINE)ScsiClassAsynchronousCompletion,
                   context,
                   TRUE,
                   TRUE,
                   TRUE);

        irpStack = IoGetNextIrpStackLocation(irp);

        irpStack->MajorFunction = IRP_MJ_SCSI;

        srb->OriginalRequest = irp;

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

        irpStack->Parameters.Scsi.Srb = (PVOID)srb;

        //
        // Set up IRP Address.
        //

        (VOID)IoCallDriver(deviceExtension->PortDeviceObject, irp);

    }
}

BOOLEAN
CdRomIsPlayActive(
    IN PDEVICE_OBJECT DeviceObject
    )

/*++

Routine Description:

    This routine determines if the cd is currently playing music.

Arguments:

    DeviceObject - Device object to test.

Return Value:

    TRUE if the device is playing music.

--*/
{
    PDEVICE_EXTENSION  deviceExtension = DeviceObject->DeviceExtension;
    PIRP irp;
    IO_STATUS_BLOCK ioStatus;
    KEVENT event;
    NTSTATUS status;
    PSUB_Q_CURRENT_POSITION currentBuffer;

    if (!PLAY_ACTIVE(deviceExtension)) {
        return(FALSE);
    }

    currentBuffer = ExAllocatePool(NonPagedPoolCacheAligned, sizeof(SUB_Q_CURRENT_POSITION));

    if (currentBuffer == NULL) {
        return(FALSE);
    }

    ((PCDROM_SUB_Q_DATA_FORMAT) currentBuffer)->Format = IOCTL_CDROM_CURRENT_POSITION;
    ((PCDROM_SUB_Q_DATA_FORMAT) currentBuffer)->Track = 0;

    //
    // Create notification event object to be used to signal the
    // request completion.
    //

    KeInitializeEvent(&event, NotificationEvent, FALSE);

    //
    // Build the synchronous request  to be sent to the port driver
    // to perform the request.
    //

    irp = IoBuildDeviceIoControlRequest(IOCTL_CDROM_READ_Q_CHANNEL,
                                        deviceExtension->DeviceObject,
                                        currentBuffer,
                                        sizeof(CDROM_SUB_Q_DATA_FORMAT),
                                        currentBuffer,
                                        sizeof(SUB_Q_CURRENT_POSITION),
                                        FALSE,
                                        &event,
                                        &ioStatus);

    if (irp == NULL) {
        ExFreePool(currentBuffer);
        return FALSE;
    }

    //
    // Pass request to port driver and wait for request to complete.
    //

    status = IoCallDriver(deviceExtension->DeviceObject, irp);

    if (status == STATUS_PENDING) {
        KeWaitForSingleObject(&event, Suspended, KernelMode, FALSE, NULL);
        status = ioStatus.Status;
    }

    if (!NT_SUCCESS(status)) {
        ExFreePool(currentBuffer);
        return FALSE;
    }

    ExFreePool(currentBuffer);

    return(PLAY_ACTIVE(deviceExtension));

}



unix.superglobalmegacorp.com

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