/*++ Copyright (C) 1990 - 99 Microsoft Corporation Module Name: port.c Abstract: Ide bus enumeration Authors: Mike Glass Jeff Havens Joe Dai Environment: kernel mode only Revision History: --*/ #include "ideport.h" #ifdef ALLOC_PRAGMA #pragma alloc_text(NONPAGE, IssueSyncAtapiCommand) #pragma alloc_text(NONPAGE, IssueSyncAtapiCommandSafe) #pragma alloc_text(NONPAGE, IdePortDmaCdromDrive) //#pragma alloc_text(PAGESCAN, IdePortDmaCdromDrive) #pragma alloc_text(PAGE, IdePortInitFdo) #pragma alloc_text(PAGE, IssueInquirySafe) #pragma alloc_text(PAGE, IdePortQueryNonCdNumLun) #pragma alloc_text(PAGE, IdeBuildDeviceMap) #pragma alloc_text(PAGE, IdeCreateNumericKey) extern LONG IdePAGESCANLockCount; #endif static PWCHAR IdePortUserRegistryDeviceTypeName[MAX_IDE_DEVICE * MAX_IDE_LINE] = { USER_MASTER_DEVICE_TYPE_REG_KEY, USER_SLAVE_DEVICE_TYPE_REG_KEY, USER_MASTER_DEVICE_TYPE2_REG_KEY, USER_SLAVE_DEVICE_TYPE2_REG_KEY }; static PWCHAR IdePortRegistryUserDeviceTimingModeAllowedName[MAX_IDE_DEVICE * MAX_IDE_LINE] = { USER_MASTER_DEVICE_TIMING_MODE_ALLOWED, USER_SLAVE_DEVICE_TIMING_MODE_ALLOWED, USER_MASTER_DEVICE_TIMING_MODE_ALLOWED2, USER_SLAVE_DEVICE_TIMING_MODE_ALLOWED2 }; // // Idle Timeout // //ULONG PdoConservationIdleTime = -1; //ULONG PdoPerformanceIdleTime = -1; NTSTATUS IdePortInitFdo( IN OUT PFDO_EXTENSION FdoExtension ) /*++ Routine Description: This routine enumerates the IDE bus and initialize the fdo extension Arguments: FdoExtension - FDO extension RegistryPath - registry path passed in via DriverEntry Return Value: --*/ { PFDO_EXTENSION fdoExtension = FdoExtension; NTSTATUS status; PDEVICE_OBJECT deviceObject; ULONG uniqueId; KIRQL irql; PIO_SCSI_CAPABILITIES capabilities; PIO_ERROR_LOG_PACKET errorLogEntry; ULONG i; ULONG j; BOOLEAN ideDeviceFound; status = STATUS_SUCCESS; deviceObject = fdoExtension->DeviceObject; // // Save the dependent driver routines in the device extension. // fdoExtension->HwDeviceExtension = (PVOID)(fdoExtension + 1); // // Mark this object as supporting direct I/O so that I/O system // will supply mdls in irps. // deviceObject->Flags |= DO_DIRECT_IO; // // Initialize the maximum lu count variable. // fdoExtension->MaxLuCount = SCSI_MAXIMUM_LOGICAL_UNITS; // // Allocate spin lock for critical sections. // KeInitializeSpinLock(&fdoExtension->SpinLock); // // Spinlock that protects LogicalUnitList manipulation // KeInitializeSpinLock(&fdoExtension->LogicalUnitListSpinLock); // // Initialize DPC routine. // IoInitializeDpcRequest(deviceObject, IdePortCompletionDpc); // // Initialize the port timeout counter. // fdoExtension->PortTimeoutCounter = PD_TIMER_STOPPED; // // Initialize timer. // IoInitializeTimer(deviceObject, IdePortTickHandler, NULL); // // Initialize miniport timer and timer DPC. // KeInitializeTimer(&fdoExtension->MiniPortTimer); KeInitializeDpc(&fdoExtension->MiniPortTimerDpc, IdeMiniPortTimerDpc, deviceObject ); // // Start timer. Request timeout counters // in the logical units have already been // initialized. // IoStartTimer(deviceObject); fdoExtension->Flags |= PD_DISCONNECT_RUNNING; // // Check to see if an error was logged. // if (fdoExtension->InterruptData.InterruptFlags & PD_LOG_ERROR) { CLRMASK (fdoExtension->InterruptData.InterruptFlags, PD_LOG_ERROR | PD_NOTIFICATION_REQUIRED); LogErrorEntry(fdoExtension, &fdoExtension->InterruptData.LogEntry); } // // Initialize the capabilities pointer. // capabilities = &fdoExtension->Capabilities; // // Initailize the capabilities structure. // capabilities->Length = sizeof(IO_SCSI_CAPABILITIES); if (fdoExtension->BoundWithBmParent) { if (fdoExtension->HwDeviceExtension->BusMasterInterface.MaxTransferByteSize < MAX_TRANSFER_SIZE_PER_SRB) { capabilities->MaximumTransferLength = fdoExtension->HwDeviceExtension->BusMasterInterface.MaxTransferByteSize; } else { capabilities->MaximumTransferLength = MAX_TRANSFER_SIZE_PER_SRB; } } else { capabilities->MaximumTransferLength = MAX_TRANSFER_SIZE_PER_SRB; } capabilities->TaggedQueuing = FALSE; capabilities->AdapterScansDown = FALSE; capabilities->AlignmentMask = deviceObject->AlignmentRequirement; capabilities->MaximumPhysicalPages = BYTES_TO_PAGES(capabilities->MaximumTransferLength); if (fdoExtension->IdeResource.TranslatedCommandBaseAddress) { DebugPrint((1, "IdePort: Initialize: Translated IO Base address %x\n", fdoExtension->IdeResource.TranslatedCommandBaseAddress)); } for (i=0; i< MAX_IDE_DEVICE * MAX_IDE_LINE; i++) { fdoExtension->UserChoiceDeviceType[i] = DeviceUnknown; IdePortGetDeviceParameter ( fdoExtension, IdePortUserRegistryDeviceTypeName[i], (PULONG)(fdoExtension->UserChoiceDeviceType + i) ); } // // the acpi _GTM buffer should be initialized with -1s // for (i=0; iBootAcpiTimingSettings); timingSettings->Speed[i].Pio = ACPI_XFER_MODE_NOT_SUPPORT; timingSettings->Speed[i].Dma = ACPI_XFER_MODE_NOT_SUPPORT; } fdoExtension->DmaDetectionLevel = DdlFirmwareOk; IdePortGetDeviceParameter ( fdoExtension, DMA_DETECTION_LEVEL_REG_KEY, (PULONG)&fdoExtension->DmaDetectionLevel ); // // non-pcmcia controller, MayHaveSlaveDevice is always set // if pcmcia controller, it is not set unless // registry flag PCMCIA_IDE_CONTROLLER_HAS_SLAVE // is non-zero // if (!ChannelQueryPcmciaParent (fdoExtension)) { fdoExtension->MayHaveSlaveDevice = 1; } else { fdoExtension->MayHaveSlaveDevice = 0; IdePortGetDeviceParameter ( fdoExtension, PCMCIA_IDE_CONTROLLER_HAS_SLAVE, (PULONG)&fdoExtension->MayHaveSlaveDevice ); } #ifdef ENABLE_ATAPI_VERIFIER ViIdeInitVerifierSettings(fdoExtension); #endif return status; } // IdePortInitFdo NTSTATUS SyncAtapiSafeCompletion ( PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context ) { PSYNC_ATA_PASSTHROUGH_CONTEXT context = Context; context->Status = Irp->IoStatus.Status; KeSetEvent (&context->Event, 0, FALSE); return STATUS_MORE_PROCESSING_REQUIRED; } NTSTATUS IssueSyncAtapiCommandSafe ( IN PFDO_EXTENSION FdoExtension, IN PPDO_EXTENSION PdoExtension, IN PCDB Cdb, IN PVOID DataBuffer, IN ULONG DataBufferSize, IN BOOLEAN DataIn, IN ULONG RetryCount, IN BOOLEAN ByPassBlockedQueue ) /*++ Routine Description: Build IRP, SRB and CDB for the given CDB Send and wait for the IRP to complete Arguments: FdoExtension - FDO extension PdoExtension - device extension of the PDO to which the command is sent Cdb - Command Descriptor Block DataBuffer - data buffer for the command DataBufferSize - byte size of DataBuffer DataIn - TRUE is the command causes the device to return data RetryCount - number of times to retry the command if the command fails Return Value: NTSTATUS If any of the pre-alloc related operation fails, it returns STATUS_INSUFFICIENT_RESOURCES The caller should take care of the condition --*/ { PIRP irp; PIO_STACK_LOCATION irpStack; PSCSI_REQUEST_BLOCK srb; KEVENT event; IO_STATUS_BLOCK ioStatusBlock; KIRQL currentIrql; NTSTATUS status; ULONG flushCount; PSENSE_DATA senseInfoBuffer; UCHAR senseInfoBufferSize; PENUMERATION_STRUCT enumStruct; SYNC_ATA_PASSTHROUGH_CONTEXT context; ULONG locked; ASSERT(InterlockedCompareExchange(&(FdoExtension->EnumStructLock), 1, 0) == 0); enumStruct=FdoExtension->PreAllocEnumStruct; if (enumStruct == NULL) { ASSERT(FdoExtension->PreAllocEnumStruct); return STATUS_INSUFFICIENT_RESOURCES; } senseInfoBufferSize = SENSE_BUFFER_SIZE; senseInfoBuffer = enumStruct->SenseInfoBuffer; ASSERT (senseInfoBuffer); DebugPrint((1, "Using Sync Atapi safe!\n")); srb= enumStruct->Srb; ASSERT(srb); status = STATUS_UNSUCCESSFUL; RetryCount = 5; flushCount = 100; irp = enumStruct->Irp1; ASSERT (irp); ASSERT (enumStruct->DataBufferSize >= DataBufferSize); while (!NT_SUCCESS(status) && RetryCount--) { // // Initialize the notification event. // KeInitializeEvent(&context.Event, NotificationEvent, FALSE); IoInitializeIrp(irp, IoSizeOfIrp(PREALLOC_STACK_LOCATIONS), PREALLOC_STACK_LOCATIONS); irp->MdlAddress = enumStruct->MdlAddress; irpStack = IoGetNextIrpStackLocation(irp); irpStack->MajorFunction = IRP_MJ_SCSI; if (DataBuffer) { RtlCopyMemory(enumStruct->DataBuffer, DataBuffer, DataBufferSize); } RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK)); irpStack->Parameters.Scsi.Srb = srb; srb->PathId = PdoExtension->PathId; srb->TargetId = PdoExtension->TargetId; srb->Lun = PdoExtension->Lun; srb->Function = SRB_FUNCTION_EXECUTE_SCSI; srb->Length = sizeof(SCSI_REQUEST_BLOCK); // // Set flags to disable synchronous negociation. // srb->SrbFlags = DataIn ? SRB_FLAGS_DATA_IN | SRB_FLAGS_DISABLE_SYNCH_TRANSFER : SRB_FLAGS_DATA_OUT | SRB_FLAGS_DISABLE_SYNCH_TRANSFER; if (ByPassBlockedQueue) { srb->SrbFlags |= SRB_FLAGS_BYPASS_FROZEN_QUEUE; } srb->SrbStatus = srb->ScsiStatus = 0; srb->NextSrb = 0; srb->OriginalRequest = irp; // // Set timeout to 4 seconds. // srb->TimeOutValue = 4; srb->CdbLength = 6; // // Enable auto request sense. // srb->SenseInfoBuffer = senseInfoBuffer; srb->SenseInfoBufferLength = senseInfoBufferSize; srb->DataBuffer = MmGetMdlVirtualAddress(irp->MdlAddress); srb->DataTransferLength = DataBufferSize; // // Set CDB operation code. // RtlCopyMemory(srb->Cdb, Cdb, sizeof(CDB)); IoSetCompletionRoutine( irp, SyncAtapiSafeCompletion, &context, TRUE, TRUE, TRUE ); // // Wait for request to complete. // if (IoCallDriver(PdoExtension->DeviceObject, irp) == STATUS_PENDING) { KeWaitForSingleObject(&context.Event, Executive, KernelMode, FALSE, NULL); } RtlCopyMemory(DataBuffer, srb->DataBuffer, DataBufferSize); if (SRB_STATUS(srb->SrbStatus) != SRB_STATUS_SUCCESS) { DebugPrint((1,"IssueSyncAtapiCommand: atapi command failed SRB status %x\n", srb->SrbStatus)); if (SRB_STATUS(srb->SrbStatus) == SRB_STATUS_REQUEST_FLUSHED) { // // we will give it a few more retries if our request // got flushed. // flushCount--; if (flushCount) { RetryCount++; } } if (SRB_STATUS(srb->SrbStatus) == SRB_STATUS_DATA_OVERRUN) { status = STATUS_DATA_OVERRUN; } else { status = STATUS_UNSUCCESSFUL; } // // Unfreeze queue if necessary // if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) { DebugPrint((3, "IssueSyncAtapiCommand: Unfreeze Queue TID %d\n", srb->TargetId)); // // unfreeze queue // CLRMASK (PdoExtension->LuFlags, PD_QUEUE_FROZEN); // // restart queue // KeAcquireSpinLock(&FdoExtension->SpinLock, ¤tIrql); GetNextLuRequest(FdoExtension, PdoExtension); KeLowerIrql(currentIrql); } if ((srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) && (senseInfoBuffer->SenseKey == SCSI_SENSE_ILLEGAL_REQUEST)) { // // A sense key of illegal request was recieved. This indicates // that the mech status command is illegal. // status = STATUS_INVALID_DEVICE_REQUEST; // // The command is illegal, no point to keep trying // RetryCount = 0; } } else { status = STATUS_SUCCESS; } } if (flushCount != 100) { DebugPrint ((DBG_ALWAYS, "IssueSyncAtapiCommand: flushCount is %u\n", flushCount)); } // // Unlock // ASSERT(InterlockedCompareExchange(&(FdoExtension->EnumStructLock), 0, 1) == 1); return status; } // IssueSyncAtapiCommandSafe BOOLEAN IdePortDmaCdromDrive( IN PFDO_EXTENSION FdoExtension, IN PPDO_EXTENSION PdoExtension, IN BOOLEAN LowMem ) /*++ Routine Description: Build IRP, SRB and CDB for SCSI MODE_SENSE10 command. Arguments: DeviceExtension - address of adapter's device object extension. LowMem - Low memory condition, use the safe (but not thread-safe) version - This should be one only when called during enumeration. Return Value: NTSTATUS --*/ { CDB cdb; NTSTATUS status; BOOLEAN isDVD = FALSE; ULONG bufLength; ULONG capPageOffset; PMODE_PARAMETER_HEADER10 modePageHeader; PCDVD_CAPABILITIES_PAGE capPage; /* // // Code is paged until locked down. // PAGED_CODE(); #ifdef ALLOC_PRAGMA ASSERT(IdePAGESCANLockCount > 0); #endif */ RtlZeroMemory(&cdb, sizeof(CDB)); bufLength = sizeof(CDVD_CAPABILITIES_PAGE) + sizeof(MODE_PARAMETER_HEADER10); capPageOffset = sizeof(MODE_PARAMETER_HEADER10); cdb.MODE_SENSE10.OperationCode = SCSIOP_MODE_SENSE10; cdb.MODE_SENSE10.Dbd = 1; cdb.MODE_SENSE10.PageCode = MODE_PAGE_CAPABILITIES; cdb.MODE_SENSE10.AllocationLength[0] = (UCHAR)(bufLength >> 8); cdb.MODE_SENSE10.AllocationLength[1] = (UCHAR)(bufLength >> 0); modePageHeader = ExAllocatePool(NonPagedPoolCacheAligned, bufLength); if (modePageHeader) { RtlZeroMemory(modePageHeader, bufLength); if (LowMem) { status = IssueSyncAtapiCommandSafe ( FdoExtension, PdoExtension, &cdb, modePageHeader, bufLength, TRUE, INQUIRY_RETRY_COUNT, TRUE ); } else { status = IssueSyncAtapiCommand ( FdoExtension, PdoExtension, &cdb, modePageHeader, bufLength, TRUE, INQUIRY_RETRY_COUNT, TRUE ); } if (NT_SUCCESS(status) || (status == STATUS_DATA_OVERRUN)) { capPage = (PCDVD_CAPABILITIES_PAGE) (((PUCHAR) modePageHeader) + capPageOffset); if ((capPage->PageCode == MODE_PAGE_CAPABILITIES) && (capPage->CDRWrite || capPage->CDEWrite || capPage->DVDROMRead || capPage->DVDRRead || capPage->DVDRAMRead || capPage->DVDRWrite || capPage->DVDRAMWrite)) { isDVD=TRUE; } } ExFreePool (modePageHeader); } return isDVD; } NTSTATUS IssueInquirySafe( IN PFDO_EXTENSION FdoExtension, IN PPDO_EXTENSION PdoExtension, OUT PINQUIRYDATA InquiryData, IN BOOLEAN LowMem ) /*++ Routine Description: Build IRP, SRB and CDB for SCSI INQUIRY command. Arguments: DeviceExtension - address of adapter's device object extension. LunInfo - address of buffer for INQUIRY information. LowMem - Low memory condition, use the safe (but not thread-safe) version - This should be one only when called during enumeration. Return Value: NTSTATUS --*/ { CDB cdb; NTSTATUS status; PAGED_CODE(); RtlZeroMemory(InquiryData, sizeof(*InquiryData)); RtlZeroMemory(&cdb, sizeof(CDB)); cdb.CDB6INQUIRY.OperationCode = SCSIOP_INQUIRY; // // Set CDB LUN. // cdb.CDB6INQUIRY.LogicalUnitNumber = PdoExtension->Lun; cdb.CDB6INQUIRY.Reserved1 = 0; // // Set allocation length to inquiry data buffer size. // cdb.CDB6INQUIRY.AllocationLength = INQUIRYDATABUFFERSIZE; // // Zero reserve field and // Set EVPD Page Code to zero. // Set Control field to zero. // (See SCSI-II Specification.) // cdb.CDB6INQUIRY.PageCode = 0; cdb.CDB6INQUIRY.IReserved = 0; cdb.CDB6INQUIRY.Control = 0; if (LowMem ) { // Use the memory safe one status = IssueSyncAtapiCommandSafe ( FdoExtension, PdoExtension, &cdb, InquiryData, INQUIRYDATABUFFERSIZE, TRUE, INQUIRY_RETRY_COUNT, FALSE ); } else { // Use the thread safe one status = IssueSyncAtapiCommand ( FdoExtension, PdoExtension, &cdb, InquiryData, INQUIRYDATABUFFERSIZE, TRUE, INQUIRY_RETRY_COUNT, FALSE ); } return status; } // IssueInquiry NTSTATUS IssueSyncAtapiCommand ( IN PFDO_EXTENSION FdoExtension, IN PPDO_EXTENSION PdoExtension, IN PCDB Cdb, IN PVOID DataBuffer, IN ULONG DataBufferSize, IN BOOLEAN DataIn, IN ULONG RetryCount, IN BOOLEAN ByPassBlockedQueue ) /*++ Routine Description: Build IRP, SRB and CDB for the given CDB Send and wait for the IRP to complete Arguments: FdoExtension - FDO extension PdoExtension - device extension of the PDO to which the command is sent Cdb - Command Descriptor Block DataBuffer - data buffer for the command DataBufferSize - byte size of DataBuffer DataIn - TRUE is the command causes the device to return data RetryCount - number of times to retry the command if the command fails Return Value: NTSTATUS --*/ { PIRP irp; PIO_STACK_LOCATION irpStack; SCSI_REQUEST_BLOCK srb; KEVENT event; IO_STATUS_BLOCK ioStatusBlock; KIRQL currentIrql; NTSTATUS status; ULONG flushCount; PSENSE_DATA senseInfoBuffer; UCHAR senseInfoBufferSize; // // Sense buffer is in non-paged pool. // senseInfoBufferSize = SENSE_BUFFER_SIZE; senseInfoBuffer = ExAllocatePool( NonPagedPoolCacheAligned, senseInfoBufferSize); if (senseInfoBuffer == NULL) { DebugPrint((1,"IssueSyncAtapiCommand: Can't allocate request sense buffer\n")); IdeLogNoMemoryError(FdoExtension, PdoExtension->TargetId, NonPagedPoolCacheAligned, senseInfoBufferSize, IDEPORT_TAG_SYNCATAPI_SENSE ); return(STATUS_INSUFFICIENT_RESOURCES); } status = STATUS_UNSUCCESSFUL; RetryCount = 5; flushCount = 100; while (!NT_SUCCESS(status) && RetryCount--) { // // Initialize the notification event. // KeInitializeEvent(&event, NotificationEvent, FALSE); // // Build IRP for this request. // irp = IoBuildDeviceIoControlRequest( DataIn ? IOCTL_SCSI_EXECUTE_IN : IOCTL_SCSI_EXECUTE_OUT, FdoExtension->DeviceObject, DataBuffer, DataBufferSize, DataBuffer, DataBufferSize, TRUE, &event, &ioStatusBlock); if (!irp) { RetryCount = 0; IdeLogNoMemoryError(FdoExtension, PdoExtension->TargetId, NonPagedPool, IoSizeOfIrp(FdoExtension->DeviceObject->StackSize), IDEPORT_TAG_SYNCATAPI_IRP ); status = STATUS_INSUFFICIENT_RESOURCES; break; } irpStack = IoGetNextIrpStackLocation(irp); // // Fill in SRB fields. // RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK)); irpStack->Parameters.Scsi.Srb = &srb; srb.PathId = PdoExtension->PathId; srb.TargetId = PdoExtension->TargetId; srb.Lun = PdoExtension->Lun; srb.Function = SRB_FUNCTION_EXECUTE_SCSI; srb.Length = sizeof(SCSI_REQUEST_BLOCK); // // Set flags to disable synchronous negociation. // srb.SrbFlags = DataIn ? SRB_FLAGS_DATA_IN | SRB_FLAGS_DISABLE_SYNCH_TRANSFER : SRB_FLAGS_DATA_OUT | SRB_FLAGS_DISABLE_SYNCH_TRANSFER; if (ByPassBlockedQueue) { srb.SrbFlags |= SRB_FLAGS_BYPASS_FROZEN_QUEUE; } srb.SrbStatus = srb.ScsiStatus = 0; srb.NextSrb = 0; srb.OriginalRequest = irp; // // Set timeout to 4 seconds. // srb.TimeOutValue = 4; srb.CdbLength = 6; // // Enable auto request sense. // srb.SenseInfoBuffer = senseInfoBuffer; srb.SenseInfoBufferLength = senseInfoBufferSize; srb.DataBuffer = MmGetMdlVirtualAddress(irp->MdlAddress); srb.DataTransferLength = DataBufferSize; // // Set CDB operation code. // RtlCopyMemory(srb.Cdb, Cdb, sizeof(CDB)); // // Wait for request to complete. // if (IoCallDriver(PdoExtension->DeviceObject, irp) == STATUS_PENDING) { KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); } if (SRB_STATUS(srb.SrbStatus) != SRB_STATUS_SUCCESS) { DebugPrint((1,"IssueSyncAtapiCommand: atapi command failed SRB status %x\n", srb.SrbStatus)); if (SRB_STATUS(srb.SrbStatus) == SRB_STATUS_REQUEST_FLUSHED) { // // we will give it a few more retries if our request // got flushed. // flushCount--; if (flushCount) { RetryCount++; } } if (SRB_STATUS(srb.SrbStatus) == SRB_STATUS_DATA_OVERRUN) { status = STATUS_DATA_OVERRUN; } else { status = STATUS_UNSUCCESSFUL; // if (SRB_STATUS(srb.SrbStatus) != SRB_STATUS_REQUEST_FLUSHED) { // if (srb.Lun == 0 && Cdb->CDB6INQUIRY.OperationCode == SCSIOP_INQUIRY) { // DebugPrint ((DBG_ALWAYS, "IssueSyncAtapiCommand: inquiry on lun 0 returned unexpected error: srb, status = 0x%x, 0x%x\n", &srb, srb.SrbStatus)); // DbgBreakPoint(); // } // } } // // Unfreeze queue if necessary // if (srb.SrbStatus & SRB_STATUS_QUEUE_FROZEN) { DebugPrint((3, "IssueSyncAtapiCommand: Unfreeze Queue TID %d\n", srb.TargetId)); // // unfreeze queue // CLRMASK (PdoExtension->LuFlags, PD_QUEUE_FROZEN); // // restart queue // KeAcquireSpinLock(&FdoExtension->SpinLock, ¤tIrql); GetNextLuRequest(FdoExtension, PdoExtension); KeLowerIrql(currentIrql); } if ((srb.SrbStatus & SRB_STATUS_AUTOSENSE_VALID) && (senseInfoBuffer->SenseKey == SCSI_SENSE_ILLEGAL_REQUEST)) { // // A sense key of illegal request was recieved. This indicates // that the mech status command is illegal. // status = STATUS_INVALID_DEVICE_REQUEST; // // The command is illegal, no point to keep trying // RetryCount = 0; } } else { status = STATUS_SUCCESS; } } // // Free buffers // ExFreePool(senseInfoBuffer); if (flushCount != 100) { DebugPrint ((DBG_ALWAYS, "IssueSyncAtapiCommand: flushCount is %u\n", flushCount)); } return status; } // IssueSyncAtapiCommand ULONG IdePortQueryNonCdNumLun ( IN PFDO_EXTENSION FdoExtension, IN PPDO_EXTENSION PdoExtension, IN BOOLEAN ByPassBlockedQueue ) /*++ Routine Description: query number of Luns a device has using the protocol defined in the ATAPI Removable Rewritable Spec (SFF-8070i) Arguments: FdoExtension - FDO extension PdoExtension - device extension of the PDO to be queried Return Value: Number of logical units --*/ { PIRP irp; PIO_STACK_LOCATION irpStack; SCSI_REQUEST_BLOCK srb; CDB cdb; IO_STATUS_BLOCK ioStatusBlock; KIRQL currentIrql; NTSTATUS status; PMODE_PARAMETER_HEADER10 modeParameterHeader; PATAPI_REMOVABLE_BLOCK_ACCESS_CAPABILITIES accessCap; PATAPI_NON_CD_DRIVE_OPERATION_MODE_PAGE opMode; ULONG modePageSize; ULONG accessCapPageSize; ULONG opModePageSize; PAGED_CODE(); if (IsNEC_98) { PIDENTIFY_DATA fullIdentifyData; fullIdentifyData = &FdoExtension->HwDeviceExtension->IdentifyData[PdoExtension->TargetId]; if (fullIdentifyData->GeneralConfiguration & 0x80) { if (fullIdentifyData->ModelNumber[8] == 0x44 && fullIdentifyData->ModelNumber[9] == 0x50 && fullIdentifyData->ModelNumber[10] == 0x31 && fullIdentifyData->ModelNumber[11] == 0x2D ) { // // Find ATAPI PD drive. // return 2; } } } // // compute the size of the mode page needed // accessCapPageSize = sizeof (MODE_PARAMETER_HEADER10) + sizeof (ATAPI_REMOVABLE_BLOCK_ACCESS_CAPABILITIES); opModePageSize = sizeof (MODE_PARAMETER_HEADER10) + sizeof (ATAPI_NON_CD_DRIVE_OPERATION_MODE_PAGE); if (sizeof(ATAPI_REMOVABLE_BLOCK_ACCESS_CAPABILITIES) >= sizeof(ATAPI_NON_CD_DRIVE_OPERATION_MODE_PAGE)) { modePageSize = accessCapPageSize; } else { modePageSize = opModePageSize; } modeParameterHeader = ExAllocatePool ( NonPagedPoolCacheAligned, modePageSize ); if (modeParameterHeader == NULL) { DebugPrint((DBG_ALWAYS,"QueryNonCdNumLun: Can't allocate modeParameterHeader buffer\n")); return(0); } RtlZeroMemory(modeParameterHeader, accessCapPageSize); RtlZeroMemory(&cdb, sizeof(CDB)); // // Set CDB operation code. // cdb.MODE_SENSE10.OperationCode = SCSIOP_MODE_SENSE10; cdb.MODE_SENSE10.PageCode = ATAPI_REMOVABLE_BLOCK_ACCESS_CAPABILITIES_PAGECODE; cdb.MODE_SENSE10.Pc = MODE_SENSE_CURRENT_VALUES; cdb.MODE_SENSE10.AllocationLength[0] = (UCHAR) ((accessCapPageSize & 0xff00) >> 8); cdb.MODE_SENSE10.AllocationLength[1] = (UCHAR) ((accessCapPageSize & 0x00ff) >> 0); // // get the removable block access capabilities page // status = IssueSyncAtapiCommand ( FdoExtension, PdoExtension, &cdb, modeParameterHeader, accessCapPageSize, TRUE, 3, ByPassBlockedQueue ); accessCap = (PATAPI_REMOVABLE_BLOCK_ACCESS_CAPABILITIES) (modeParameterHeader + 1); if (NT_SUCCESS(status) && (accessCap->PageCode == ATAPI_REMOVABLE_BLOCK_ACCESS_CAPABILITIES_PAGECODE)) { DebugPrint ((DBG_PNP, "QueryNonCdNumLun: Removable Block Access Capabilities Page:\n" "page save bit: 0x%x\n" "format progress report support: 0x%x\n" "system floppy device: 0x%x\n" "total LUNs: 0x%x\n" "in single-Lun mode: 0x%x\n" "non-CD optical deivce: 0x%x\n", accessCap->PSBit, accessCap->SRFP, accessCap->SFLP, accessCap->TotalLun, accessCap->SML, accessCap->NCD )); if (accessCap->NCD) { // // we have a non-CD optical deivce // RtlZeroMemory(modeParameterHeader, opModePageSize); RtlZeroMemory(&cdb, sizeof(CDB)); // // Set CDB operation code. // cdb.MODE_SENSE10.OperationCode = SCSIOP_MODE_SENSE10; cdb.MODE_SENSE10.PageCode = ATAPI_NON_CD_DRIVE_OPERATION_MODE_PAGE_PAGECODE; cdb.MODE_SENSE10.Pc = MODE_SENSE_CURRENT_VALUES; cdb.MODE_SENSE10.AllocationLength[0] = (UCHAR) ((opModePageSize & 0xff00) >> 8); cdb.MODE_SENSE10.AllocationLength[1] = (UCHAR) ((opModePageSize & 0x00ff) >> 0); // // get the non-cd drive operation mode page // status = IssueSyncAtapiCommand ( FdoExtension, PdoExtension, &cdb, modeParameterHeader, opModePageSize, TRUE, 3, ByPassBlockedQueue ); opMode = (PATAPI_NON_CD_DRIVE_OPERATION_MODE_PAGE) (modeParameterHeader + 1); if (NT_SUCCESS(status) && (opMode->PageCode == ATAPI_NON_CD_DRIVE_OPERATION_MODE_PAGE_PAGECODE)) { DebugPrint ((DBG_PNP, "QueryNonCdNumLun: Non-CD device Operation Mode Page:\n" "page save bit: 0x%x\n" "disable verify for write: 0x%x\n" "Lun for R/W device: 0x%x\n" "multi-Lun mode: 0x%x\n", opMode->PSBit, opMode->DVW, opMode->SLR, opMode->SLM )); RtlZeroMemory(modeParameterHeader, sizeof (MODE_PARAMETER_HEADER10)); // // With mode select, this is reserved and must be 0 // opMode->PSBit = 0; // // Turn on multi-lun mode // opMode->SLM = 1; // // non-CD device shall be Lun 1 // opMode->SLR = 1; RtlZeroMemory(&cdb, sizeof(CDB)); // // Set CDB operation code. // cdb.MODE_SELECT10.OperationCode = SCSIOP_MODE_SELECT10; cdb.MODE_SELECT10.SPBit = 1; // save page cdb.MODE_SELECT10.PFBit = 1; cdb.MODE_SELECT10.ParameterListLength[0] = (UCHAR) ((opModePageSize & 0xff00) >> 8); cdb.MODE_SELECT10.ParameterListLength[1] = (UCHAR) ((opModePageSize & 0x00ff) >> 0); status = IssueSyncAtapiCommand ( FdoExtension, PdoExtension, &cdb, modeParameterHeader, opModePageSize, FALSE, 3, ByPassBlockedQueue ); if (!NT_SUCCESS(status)) { DebugPrint ((DBG_ALWAYS, "IdePortQueryNonCdNumLun: Unable to set non-CD device into dual Lun Mode\n")); } } } } // // Free buffers // ExFreePool(modeParameterHeader); if (!NT_SUCCESS(status)) { return 0; } else { return 2; } } // IdePortQueryNonCdNumLun VOID IdeBuildDeviceMap( IN PFDO_EXTENSION FdoExtension, IN PUNICODE_STRING ServiceKey ) /*++ Routine Description: The routine takes the inquiry data which has been collected and creates a device map for it. Arguments: FdoExtension - FDO extension ServiceKey - Suppiles the name of the service key. Return Value: None. --*/ { UNICODE_STRING name; UNICODE_STRING unicodeString; ANSI_STRING ansiString; HANDLE key; HANDLE busKey; HANDLE targetKey; HANDLE lunKey; OBJECT_ATTRIBUTES objectAttributes; NTSTATUS status; ULONG disposition; PWSTR start; WCHAR buffer[32]; UCHAR lastTarget; ULONG i; ULONG dmaEnableMask; PCSTR peripheralType; UCHAR lastBus; IDE_PATH_ID pathId; IN PPDO_EXTENSION pdoExtension; PAGED_CODE(); // // Create the SCSI key in the device map. // RtlInitUnicodeString(&name, L"\\Registry\\Machine\\Hardware\\DeviceMap\\Scsi"); // // Initialize the object for the key. // InitializeObjectAttributes(&objectAttributes, &name, OBJ_CASE_INSENSITIVE, NULL, (PSECURITY_DESCRIPTOR) NULL); // // Create the key or open it. // status = ZwCreateKey(&lunKey, KEY_READ | KEY_WRITE, &objectAttributes, 0, (PUNICODE_STRING) NULL, REG_OPTION_VOLATILE, &disposition ); if (!NT_SUCCESS(status)) { return; } status = IdeCreateNumericKey(lunKey, FdoExtension->ScsiPortNumber, L"Scsi Port ", &key); ZwClose(lunKey); if (!NT_SUCCESS(status)) { return; } #ifdef IDE_MEASURE_BUSSCAN_SPEED RtlInitUnicodeString(&name, L"FirstBusScanTimeInMs"); status = ZwSetValueKey(key, &name, 0, REG_DWORD, &FdoExtension->BusScanTime, sizeof(ULONG)); #endif // IDE_MEASURE_BUSSCAN_SPEED // // Add DMA enable mask value. // dmaEnableMask = 0; for (i=0; iHwDeviceExtension->MaxIdeDevice; i++) { if (FdoExtension->HwDeviceExtension->DeviceFlags[i] & DFLAGS_USE_DMA) { dmaEnableMask |= (1 << i); } } RtlInitUnicodeString(&name, L"DMAEnabled"); status = ZwSetValueKey(key, &name, 0, REG_DWORD, &dmaEnableMask, 4); // // Add Interrupt value. // // if (FdoExtension->InterruptLevel) { // // RtlInitUnicodeString(&name, L"Interrupt"); // // status = ZwSetValueKey(key, // &name, // 0, // REG_DWORD, // &FdoExtension->InterruptLevel, // 4); // } // // // // // Add base IO address value. // // // // if (FdoExtension->IdeResource.TranslatedCommandBaseAddress) { // // RtlInitUnicodeString(&name, L"IOAddress"); // // status = ZwSetValueKey(key, // &name, // 0, // REG_DWORD, // &FdoExtension->IdeResource.TranslatedCommandBaseAddress, // 4); // } if (ServiceKey != NULL) { // // Add identifier value. This value is equal to the name of the driver // in the from the service key. Note the service key name is not NULL // terminated. // RtlInitUnicodeString(&name, L"Driver"); // // Get the name of the driver from the service key name. // start = (PWSTR) ((PCHAR) ServiceKey->Buffer + ServiceKey->Length); start--; while (*start != L'\\' && start > ServiceKey->Buffer) { start--; } if (*start != L'\\') { ZwClose(key); return; } start++; for (i = 0; i < 31; i++) { buffer[i] = *start++; if (start >= ServiceKey->Buffer + ServiceKey->Length / sizeof(wchar_t)) { break; } } i++; buffer[i] = L'\0'; status = ZwSetValueKey(key, &name, 0, REG_SZ, buffer, (i + 1) * sizeof(wchar_t)); if (!NT_SUCCESS(status)) { ZwClose(key); return; } } // // Cycle through each of the lun. // lastBus = 0xff; pathId.l = 0; busKey = 0; targetKey = 0; lunKey = 0; while (pdoExtension = NextLogUnitExtensionWithTag ( FdoExtension, &pathId, FALSE, IdeBuildDeviceMap )) { // // Create a key entry for the bus. // if (lastBus != pathId.b.Path) { if (busKey) { ZwClose(busKey); busKey = 0; } if (targetKey) { ZwClose(targetKey); targetKey = 0; } status = IdeCreateNumericKey(key, pathId.b.Path, L"Scsi Bus ", &busKey); if (!NT_SUCCESS(status)) { break; } lastBus = (UCHAR) pathId.b.Path; // // Create a key entry for the Scsi bus adapter. // status = IdeCreateNumericKey(busKey, IDE_PSUEDO_INITIATOR_ID, L"Initiator Id ", &targetKey); if (!NT_SUCCESS(status)) { break; } lastTarget = IDE_PSUEDO_INITIATOR_ID; } // // Process the data for the logical units. // // // If this is a new target Id then create a new target entry. // if (lastTarget != pdoExtension->TargetId) { ZwClose(targetKey); targetKey = 0; status = IdeCreateNumericKey(busKey, pdoExtension->TargetId, L"Target Id ", &targetKey); if (!NT_SUCCESS(status)) { break; } lastTarget = pdoExtension->TargetId; } // // Create the Lun entry. // status = IdeCreateNumericKey(targetKey, pdoExtension->Lun, L"Logical Unit Id ", &lunKey); if (!NT_SUCCESS(status)) { break; } // // Create identifier value. // RtlInitUnicodeString(&name, L"Identifier"); // // Get the Identifier from the inquiry data. // RtlInitAnsiString(&ansiString, pdoExtension->FullVendorProductId); status = RtlAnsiStringToUnicodeString(&unicodeString, &ansiString, TRUE); if (!NT_SUCCESS(status)) { break; } status = ZwSetValueKey(lunKey, &name, 0, REG_SZ, unicodeString.Buffer, unicodeString.Length + sizeof(wchar_t)); RtlFreeUnicodeString(&unicodeString); if (!NT_SUCCESS(status)) { break; } // // Determine the perpherial type. // peripheralType = IdePortGetPeripheralIdString ( pdoExtension->ScsiDeviceType ); if (!peripheralType) { peripheralType = "OtherPeripheral"; } RtlInitAnsiString(&ansiString, peripheralType); unicodeString.MaximumLength = (USHORT) RtlAnsiStringToUnicodeSize(&ansiString) + sizeof(WCHAR); unicodeString.Length = 0; unicodeString.Buffer = ExAllocatePool (PagedPool, unicodeString.MaximumLength); if (unicodeString.Buffer) { status = RtlAnsiStringToUnicodeString( &unicodeString, &ansiString, FALSE ); if (NT_SUCCESS(status)) { // // Set type value. // RtlInitUnicodeString(&name, L"Type"); unicodeString.Buffer[unicodeString.Length / sizeof (WCHAR)] = L'\0'; status = ZwSetValueKey(lunKey, &name, 0, REG_SZ, unicodeString.Buffer, unicodeString.Length + sizeof (WCHAR)); ExFreePool (unicodeString.Buffer); } } else { status = STATUS_NO_MEMORY; } ZwClose(lunKey); lunKey = 0; if (!NT_SUCCESS(status)) { break; } UnrefLogicalUnitExtensionWithTag ( FdoExtension, pdoExtension, IdeBuildDeviceMap ); pdoExtension = NULL; } if (lunKey) { ZwClose(lunKey); } if (busKey) { ZwClose(busKey); } if (targetKey) { ZwClose(targetKey); } if (pdoExtension) { UnrefLogicalUnitExtensionWithTag ( FdoExtension, pdoExtension, IdeBuildDeviceMap ); } ZwClose(key); } // IdeBuildDeviceMap NTSTATUS IdeCreateNumericKey( IN HANDLE Root, IN ULONG Name, IN PWSTR Prefix, OUT PHANDLE NewKey ) /*++ Routine Description: This function creates a registry key. The name of the key is a string version of numeric value passed in. Arguments: RootKey - Supplies a handle to the key where the new key should be inserted. Name - Supplies the numeric value to name the key. Prefix - Supplies a prefix name to add to name. NewKey - Returns the handle for the new key. Return Value: Returns the status of the operation. --*/ { UNICODE_STRING string; UNICODE_STRING stringNum; OBJECT_ATTRIBUTES objectAttributes; WCHAR bufferNum[16]; WCHAR buffer[64]; ULONG disposition; NTSTATUS status; PAGED_CODE(); // // Copy the Prefix into a string. // string.Length = 0; string.MaximumLength=64; string.Buffer = buffer; RtlInitUnicodeString(&stringNum, Prefix); RtlCopyUnicodeString(&string, &stringNum); // // Create a port number key entry. // stringNum.Length = 0; stringNum.MaximumLength = 16; stringNum.Buffer = bufferNum; status = RtlIntegerToUnicodeString(Name, 10, &stringNum); if (!NT_SUCCESS(status)) { return status; } // // Append the prefix and the numeric name. // RtlAppendUnicodeStringToString(&string, &stringNum); InitializeObjectAttributes( &objectAttributes, &string, OBJ_CASE_INSENSITIVE, Root, (PSECURITY_DESCRIPTOR) NULL ); status = ZwCreateKey(NewKey, KEY_READ | KEY_WRITE, &objectAttributes, 0, (PUNICODE_STRING) NULL, REG_OPTION_VOLATILE, &disposition ); return(status); } // IdeCreateNumericKey