/*++ Copyright (c) 2001 Microsoft Corporation Module Name: scsi.c Abstract: This file contains RAM disk driver code for processing SCSI commands. Author: Chuck Lenzmeier (ChuckL) 2001 Environment: Kernel mode only. Notes: Revision History: --*/ #include "precomp.h" #pragma hdrstop // // Local functions. // NTSTATUS Do6ByteCdbCommand ( IN PDEVICE_OBJECT DeviceObject, IN OUT PSCSI_REQUEST_BLOCK Srb ); NTSTATUS Do10ByteCdbCommand ( IN PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp, IN OUT PSCSI_REQUEST_BLOCK Srb ); NTSTATUS Do12ByteCdbCommand ( IN PDEVICE_OBJECT DeviceObject, IN OUT PSCSI_REQUEST_BLOCK Srb ); NTSTATUS BuildInquiryData ( IN PDEVICE_OBJECT DeviceObject, IN OUT PSCSI_REQUEST_BLOCK Srb ); NTSTATUS BuildModeSenseInfo ( IN PDEVICE_OBJECT DeviceObject, IN OUT PSCSI_REQUEST_BLOCK Srb ); // // Declare pageable routines. // #ifdef ALLOC_PRAGMA #pragma alloc_text( PAGE, RamdiskScsiExecuteNone ) #pragma alloc_text( PAGE, RamdiskScsiExecuteIo ) #pragma alloc_text( PAGE, Do6ByteCdbCommand ) #pragma alloc_text( PAGE, Do10ByteCdbCommand ) #pragma alloc_text( PAGE, Do12ByteCdbCommand ) #pragma alloc_text( PAGE, BuildInquiryData ) #pragma alloc_text( PAGE, BuildModeSenseInfo ) #endif // ALLOC_PRAGMA NTSTATUS RamdiskScsi ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine is called by the I/O system to process a SCSI IRP. Arguments: DeviceObject - a pointer to the object that represents the device on which I/O is to be performed Irp - a pointer to the I/O Request Packet for this request Return Value: NTSTATUS - the status of the operation --*/ { NTSTATUS status; PDISK_EXTENSION diskExtension; diskExtension = DeviceObject->DeviceExtension; // // ISSUE: Can't be paged because ClassCheckMediaState calls it from timer // routine. (For removable disks.) Therefore we can't acquire the device // mutex here. // // // Check to see if the device is being removed. // if ( diskExtension->DeviceState > RamdiskDeviceStatePendingRemove ) { status = STATUS_DEVICE_DOES_NOT_EXIST; COMPLETE_REQUEST( status, 0, Irp ); return status; } // // Acquire the remove lock for the device. // status = IoAcquireRemoveLock( &diskExtension->RemoveLock, Irp ); if ( !NT_SUCCESS(status) ) { DBGPRINT( DBG_PNP, DBG_ERROR, ("%s", "RamdiskScsi: acquire RemoveLock failed\n") ); COMPLETE_REQUEST( status, 0, Irp ); return status; } // // This IRP must be processed in thread context. // status = SendIrpToThread( DeviceObject, Irp ); if ( status != STATUS_PENDING ) { DBGPRINT( DBG_PNP, DBG_ERROR, ("%s", "RamdiskScsi: SendIrpToThread failed\n") ); COMPLETE_REQUEST( status, 0, Irp ); return status; } // // Release the remove lock. // IoReleaseRemoveLock(&diskExtension->RemoveLock, Irp ); return STATUS_PENDING; } // RamdiskScsi NTSTATUS RamdiskScsiExecuteNone ( PDEVICE_OBJECT DeviceObject, PIRP Irp, PSCSI_REQUEST_BLOCK Srb, ULONG ControlCode ) /*++ Routine Description: This routine is called by the I/O system to process a SCSI IRP that does not involve I/O. Arguments: DeviceObject - a pointer to the object that represents the device on which I/O is to be performed Irp - a pointer to the I/O Request Packet for this request Srb - the SRB associated with the IRP ControlCode - the control code from the SRB Return Value: NTSTATUS - the status of the operation --*/ { NTSTATUS status; UCHAR function; PDISK_EXTENSION diskExtension; PAGED_CODE(); diskExtension = DeviceObject->DeviceExtension; // // Dispatch based on the SRB function. // function = Srb->Function; switch( function ) { case SRB_FUNCTION_ATTACH_DEVICE: case SRB_FUNCTION_CLAIM_DEVICE: DBGPRINT( DBG_SRB, DBG_VERBOSE, ("%s", "SRB_FUNCTION_CLAIM_DEVICE\n") ); // // If the device has not already been claimed, mark it so now. // Otherwise, indicate to the caller that the device is busy. // if ( (diskExtension->Status & RAMDISK_STATUS_CLAIMED) == 0 ) { diskExtension->DeviceState = RamdiskDeviceStateWorking; diskExtension->Status |= RAMDISK_STATUS_CLAIMED; Srb->DataBuffer = DeviceObject; status = STATUS_SUCCESS; Srb->ScsiStatus = SCSISTAT_GOOD; Srb->SrbStatus = SRB_STATUS_SUCCESS; } else { status = STATUS_DEVICE_BUSY; Srb->ScsiStatus = SCSISTAT_BUSY; Srb->SrbStatus = SRB_STATUS_BUSY; } break; case SRB_FUNCTION_RELEASE_DEVICE: case SRB_FUNCTION_REMOVE_DEVICE: DBGPRINT( DBG_SRB, DBG_VERBOSE, ("%s", "SRB_FUNCTION_RELEASE_DEVICE\n") ); // // Indicate that the device is no longer claimed. // diskExtension->Status &= ~RAMDISK_STATUS_CLAIMED; status = STATUS_SUCCESS; Srb->ScsiStatus = SCSISTAT_GOOD; Srb->SrbStatus = SRB_STATUS_SUCCESS; break; default: // // Unrecognized non-I/O function. Try the I/O path. // status = RamdiskScsiExecuteIo( DeviceObject, Irp, Srb, ControlCode ); break; } return status; } // RamdiskScsiExecuteNone NTSTATUS RamdiskScsiExecuteIo ( PDEVICE_OBJECT DeviceObject, PIRP Irp, PSCSI_REQUEST_BLOCK Srb, ULONG ControlCode ) /*++ Routine Description: This routine is called by the I/O system to process a SCSI IRP that involves I/O. Arguments: DeviceObject - a pointer to the object that represents the device on which I/O is to be performed Irp - a pointer to the I/O Request Packet for this request Srb - the SRB associated with the IRP ControlCode - the control code from the SRB Return Value: NTSTATUS - the status of the operation --*/ { NTSTATUS status; UCHAR function; PDISK_EXTENSION diskExtension; PAGED_CODE(); diskExtension = DeviceObject->DeviceExtension; // // Dispatch based on the SRB function. // function = Srb->Function; switch( function ) { case SRB_FUNCTION_EXECUTE_SCSI: Srb->SrbStatus = SRB_STATUS_SUCCESS; // // Dispatch based on the CDB length. // switch( Srb->CdbLength ) { case 6: status = Do6ByteCdbCommand( DeviceObject, Srb ); break; case 10: status = Do10ByteCdbCommand( DeviceObject, Irp, Srb ); break; case 12: status = Do12ByteCdbCommand( DeviceObject, Srb ); break; default: DBGPRINT( DBG_SRB, DBG_ERROR, ("Unknown CDB length 0x%x for function 0x%x, IOCTL 0x%x\n", Srb->CdbLength, function, ControlCode) ); UNRECOGNIZED_IOCTL_BREAK; status = STATUS_INVALID_DEVICE_REQUEST; Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; break; } break; case SRB_FUNCTION_FLUSH: case SRB_FUNCTION_SHUTDOWN: // // For flush and shutdown on a file-backed RAM disk, we need to flush // the mapped data back to the file. // status = RamdiskFlushBuffersReal( diskExtension ); Srb->SrbStatus = SRB_STATUS_SUCCESS; break; case SRB_FUNCTION_IO_CONTROL: // // We don't handle this function, but we don't want to complain // when we get it. // status = STATUS_INVALID_DEVICE_REQUEST; Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; break; default: DBGPRINT( DBG_SRB, DBG_ERROR, ("Unknown SRB Function 0x%x for IOCTL 0x%x\n", function, ControlCode) ); UNRECOGNIZED_IOCTL_BREAK; status = STATUS_INTERNAL_ERROR; } return status; } // RamdiskScsiExecuteIo NTSTATUS Do6ByteCdbCommand ( IN PDEVICE_OBJECT DeviceObject, IN OUT PSCSI_REQUEST_BLOCK Srb ) /*++ Routine Description: This routine handles 6-byte CDBs. Arguments: DeviceObject - a pointer to the object that represents the device on which I/O is to be performed Srb - the SRB associated with the I/O request Return Value: NTSTATUS - the status of the operation --*/ { NTSTATUS status; PDISK_EXTENSION diskExtension; PCDB cdb; PAGED_CODE(); // // Get pointers to the device extension and to the CDB. // diskExtension = DeviceObject->DeviceExtension; cdb = (PCDB)Srb->Cdb; // // Assume success. // status = STATUS_SUCCESS; Srb->SrbStatus = SRB_STATUS_SUCCESS; Srb->ScsiStatus = SCSISTAT_GOOD; ASSERT( Srb->CdbLength == 6 ); ASSERT( cdb != NULL ); DBGPRINT( DBG_SRB, DBG_VERBOSE, ("Do6ByteCdbCommand Called OpCode 0x%x\n", cdb->CDB6GENERIC.OperationCode) ); // // Dispatch based on the operation code. // switch ( cdb->CDB6GENERIC.OperationCode ) { case SCSIOP_TEST_UNIT_READY: // // RAM disks are always ready. // break; case SCSIOP_REQUEST_SENSE: // // We don't handle request sense. // Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; status = STATUS_INVALID_DEVICE_REQUEST; break; case SCSIOP_FORMAT_UNIT: // ISSUE: Need to do something here, like zero the image? break; case SCSIOP_INQUIRY: // // If the buffer is big enough, build the inquiry data. // if ( Srb->DataTransferLength >= INQUIRYDATABUFFERSIZE ) { status = BuildInquiryData( DeviceObject, Srb ); } else { Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; status = STATUS_INVALID_DEVICE_REQUEST; } break; case SCSIOP_MODE_SENSE: // // Build the mode sense information. // status = BuildModeSenseInfo( DeviceObject, Srb ); break; case SCSIOP_MEDIUM_REMOVAL: // // Remember whether media removal is allowed. // if (cdb->MEDIA_REMOVAL.Prevent == TRUE) { diskExtension->Status |= RAMDISK_STATUS_PREVENT_REMOVE; } else { diskExtension->Status &= ~RAMDISK_STATUS_PREVENT_REMOVE; } break; //case SCSIOP_READ6: //case SCSIOP_WRITE6: //case SCSIOP_REZERO_UNIT: //case SCSIOP_REQUEST_BLOCK_ADDR: //case SCSIOP_READ_BLOCK_LIMITS: //case SCSIOP_REASSIGN_BLOCKS: //case SCSIOP_SEEK6: //case SCSIOP_SEEK_BLOCK: //case SCSIOP_PARTITION: //case SCSIOP_READ_REVERSE: //case SCSIOP_WRITE_FILEMARKS: //case SCSIOP_SPACE: //case SCSIOP_VERIFY6: //case SCSIOP_RECOVER_BUF_DATA: //case SCSIOP_MODE_SELECT: //case SCSIOP_RESERVE_UNIT: //case SCSIOP_RELEASE_UNIT: //case SCSIOP_COPY: //case SCSIOP_ERASE: //case SCSIOP_START_STOP_UNIT: //case SCSIOP_RECEIVE_DIAGNOSTIC: //case SCSIOP_SEND_DIAGNOSTIC: default: DBGPRINT( DBG_SRB, DBG_ERROR, ("Unknown CDB Function 0x%x\n", cdb->CDB6GENERIC.OperationCode) ); UNRECOGNIZED_IOCTL_BREAK; status = STATUS_INVALID_DEVICE_REQUEST; Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; } DBGPRINT( DBG_SRB, DBG_VERBOSE, ("Do6ByteCdbCommand Done status 0x%x\n", status) ); return status; } // Do6ByteCdbCommand NTSTATUS Do10ByteCdbCommand ( IN PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp, IN OUT PSCSI_REQUEST_BLOCK Srb ) /*++ Routine Description: This routine handles 10-byte CDBs. Arguments: DeviceObject - a pointer to the object that represents the device on which I/O is to be performed Irp - a pointer to the I/O Request Packet for this request Srb - the SRB associated with the IRP Return Value: NTSTATUS - the status of the operation --*/ { NTSTATUS status; PDISK_EXTENSION diskExtension; PCDB cdb; PREAD_CAPACITY_DATA readCapacityData; ULONGLONG diskSize; ULONG lastBlock; FOUR_BYTE startingBlockNumber; TWO_BYTE count; ULONG_PTR offset; ULONG dataSize; PUCHAR diskByteAddress; PUCHAR dataBuffer; ULONG mappedLength; PAGED_CODE(); // // Get pointers to the device extension and to the CDB. // diskExtension = DeviceObject->DeviceExtension; cdb = (PCDB)Srb->Cdb; // // Assume success. // status = STATUS_SUCCESS; Srb->SrbStatus = SRB_STATUS_SUCCESS; Srb->ScsiStatus = SCSISTAT_GOOD; ASSERT( Srb->CdbLength == 10 ); ASSERT( cdb != NULL ); DBGPRINT( DBG_SRB, DBG_VERBOSE, ("Do10ByteCdbCommand Called OpCode 0x%x\n", cdb->CDB10.OperationCode) ); // // Dispatch based on the operation code. // switch ( cdb->CDB10.OperationCode ) { case SCSIOP_READ_CAPACITY: // // Return the disk's block size and last block number (big-endian). // readCapacityData = Srb->DataBuffer; diskSize = diskExtension->DiskLength; lastBlock = (ULONG)(diskSize / diskExtension->BytesPerSector) - 1; readCapacityData->BytesPerBlock = _byteswap_ulong( diskExtension->BytesPerSector ); readCapacityData->LogicalBlockAddress = _byteswap_ulong( lastBlock ); break; case SCSIOP_READ: case SCSIOP_WRITE: // // Read from or write to the disk. // // // Convert the transfer length, in blocks, from big-endian. Convert // that to bytes. // count.Byte0 = cdb->CDB10.TransferBlocksLsb; count.Byte1 = cdb->CDB10.TransferBlocksMsb; dataSize = count.AsUShort * diskExtension->BytesPerSector; // // If the CDB length is greater than the SRB length, use the SRB // length. // if ( dataSize > Srb->DataTransferLength ) { dataSize = Srb->DataTransferLength; } // // Convert the starting block number from big-endian. // startingBlockNumber.Byte0 = cdb->CDB10.LogicalBlockByte3; startingBlockNumber.Byte1 = cdb->CDB10.LogicalBlockByte2; startingBlockNumber.Byte2 = cdb->CDB10.LogicalBlockByte1; startingBlockNumber.Byte3 = cdb->CDB10.LogicalBlockByte0; // // We don't handle RelativeAddress requests. // if ( cdb->CDB10.RelativeAddress ) { Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; status = STATUS_INVALID_DEVICE_REQUEST; break; } // // Get the offset within the disk to the start of the operation. // offset = (startingBlockNumber.AsULong * diskExtension->BytesPerSector); // // If the transfer length causes the offset to wrap, or if the request // goes beyond the end of the disk, reject the request. // if ( ((offset + dataSize) < offset) || ((offset + dataSize) > diskExtension->DiskLength) ) { Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; status = STATUS_INVALID_DEVICE_REQUEST; break; } // // For a zero-length transfer, we don't have to do anything. // DBGPRINT( DBG_READWRITE, DBG_VERBOSE, ("%s: Starting Block 0x%x, Length 0x%x at Offset 0x%I64x SrbBuffer=0x%p " "SrbLength=0x%x, MdlLength=0x%x\n", cdb->CDB10.OperationCode == SCSIOP_READ ? "Read" : "Write", startingBlockNumber.AsULong, count.AsUShort, offset, Srb->DataBuffer, Srb->DataTransferLength, Irp->MdlAddress->ByteCount) ); dataBuffer = Srb->DataBuffer; while ( dataSize != 0 ) { // // Map the target section of the disk into memory. Then copy the // data in the appropriate direction. // diskByteAddress = RamdiskMapPages( diskExtension, offset, dataSize, &mappedLength ); if ( diskByteAddress != NULL ) { if ( cdb->CDB10.OperationCode == SCSIOP_READ ) { memcpy( dataBuffer, diskByteAddress, mappedLength ); } else { memcpy( diskByteAddress, dataBuffer, mappedLength ); } RamdiskUnmapPages( diskExtension, diskByteAddress, offset, mappedLength ); dataSize -= mappedLength; offset += mappedLength; dataBuffer += mappedLength; } else { dataSize = 0; Srb->SrbStatus = SRB_STATUS_ERROR; status = STATUS_INSUFFICIENT_RESOURCES; } } break; case SCSIOP_VERIFY: // // Verify always succeeds. // break; case SCSIOP_MODE_SENSE10: // // Build the mode sense information. // status = BuildModeSenseInfo( DeviceObject, Srb ); break; //case SCSIOP_SEEK: //case SCSIOP_WRITE_VERIFY: //case SCSIOP_READ_FORMATTED_CAPACITY: //case SCSIOP_SEARCH_DATA_HIGH: //case SCSIOP_SEARCH_DATA_EQUAL: //case SCSIOP_SEARCH_DATA_LOW: //case SCSIOP_SET_LIMITS: //case SCSIOP_READ_POSITION: //case SCSIOP_SYNCHRONIZE_CACHE: //case SCSIOP_COMPARE: //case SCSIOP_COPY_COMPARE: //case SCSIOP_WRITE_DATA_BUFF: //case SCSIOP_READ_DATA_BUFF: //case SCSIOP_CHANGE_DEFINITION: //case SCSIOP_READ_SUB_CHANNEL: //case SCSIOP_READ_TOC: //case SCSIOP_READ_HEADER: //case SCSIOP_PLAY_AUDIO: //case SCSIOP_GET_CONFIGURATION: //case SCSIOP_PLAY_AUDIO_MSF: //case SCSIOP_PLAY_TRACK_INDEX: //case SCSIOP_PLAY_TRACK_RELATIVE: //case SCSIOP_GET_EVENT_STATUS: //case SCSIOP_PAUSE_RESUME: //case SCSIOP_LOG_SELECT: //case SCSIOP_LOG_SENSE: //case SCSIOP_STOP_PLAY_SCAN: //case SCSIOP_READ_DISK_INFORMATION: //case SCSIOP_READ_TRACK_INFORMATION: //case SCSIOP_RESERVE_TRACK_RZONE: //case SCSIOP_SEND_OPC_INFORMATION: //case SCSIOP_MODE_SELECT10: //case SCSIOP_CLOSE_TRACK_SESSION: //case SCSIOP_READ_BUFFER_CAPACITY: //case SCSIOP_SEND_CUE_SHEET: //case SCSIOP_PERSISTENT_RESERVE_IN: //case SCSIOP_PERSISTENT_RESERVE_OUT: default: DBGPRINT( DBG_SRB, DBG_ERROR, ("Unknown CDB Function 0x%x\n", cdb->CDB10.OperationCode) ); UNRECOGNIZED_IOCTL_BREAK; status = STATUS_INVALID_DEVICE_REQUEST; Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; } DBGPRINT( DBG_SRB, DBG_VERBOSE, ("Do10ByteCdbCommand Done status 0x%x\n", status) ); return status; } // Do10ByteCdbCommand NTSTATUS Do12ByteCdbCommand ( IN PDEVICE_OBJECT DeviceObject, IN OUT PSCSI_REQUEST_BLOCK Srb ) /*++ Routine Description: This routine handles 12-byte CDBs. Arguments: DeviceObject - a pointer to the object that represents the device on which I/O is to be performed Srb - the SRB associated with the IRP Return Value: NTSTATUS - the status of the operation --*/ { NTSTATUS status; PDISK_EXTENSION diskExtension; PCDB cdb; PAGED_CODE(); // // Get pointers to the device extension and to the CDB. // diskExtension = DeviceObject->DeviceExtension; cdb = (PCDB)Srb->Cdb; // // Assume success. // status = STATUS_SUCCESS; Srb->SrbStatus = SRB_STATUS_SUCCESS; Srb->ScsiStatus = SCSISTAT_GOOD; ASSERT( Srb->CdbLength == 12 ); ASSERT( cdb != NULL ); DBGPRINT( DBG_SRB, DBG_VERBOSE, ("Do12ByteCdbCommand Called OpCode 0x%x\n", cdb->CDB12.OperationCode) ); // // Dispatch based on the operation code. // switch ( cdb->CDB12.OperationCode ) { //case SCSIOP_REPORT_LUNS: //case SCSIOP_BLANK: //case SCSIOP_SEND_KEY: //case SCSIOP_REPORT_KEY: //case SCSIOP_MOVE_MEDIUM: //case SCSIOP_LOAD_UNLOAD_SLOT: //case SCSIOP_SET_READ_AHEAD: //case SCSIOP_READ_DVD_STRUCTURE: //case SCSIOP_REQUEST_VOL_ELEMENT: //case SCSIOP_SEND_VOLUME_TAG: //case SCSIOP_READ_ELEMENT_STATUS: //case SCSIOP_READ_CD_MSF: //case SCSIOP_SCAN_CD: //case SCSIOP_SET_CD_SPEED: //case SCSIOP_PLAY_CD: //case SCSIOP_MECHANISM_STATUS: //case SCSIOP_READ_CD: //case SCSIOP_SEND_DVD_STRUCTURE: //case SCSIOP_INIT_ELEMENT_RANGE: default: DBGPRINT( DBG_SRB, DBG_ERROR, ("Unknown CDB Function 0x%x\n", cdb->CDB12.OperationCode) ); UNRECOGNIZED_IOCTL_BREAK; status = STATUS_INVALID_DEVICE_REQUEST; Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; } DBGPRINT( DBG_SRB, DBG_VERBOSE, ("Do12ByteCdbCommand Done status 0x%x\n", status) ); return status; } // Do12ByteCdbCommand NTSTATUS BuildInquiryData ( IN PDEVICE_OBJECT DeviceObject, IN OUT PSCSI_REQUEST_BLOCK Srb ) /*++ Routine Description: This routine builds inquiry data. Arguments: DeviceObject - a pointer to the object that represents the device on which I/O is to be performed Srb - the SRB associated with the I/O request Return Value: NTSTATUS - the status of the operation --*/ { PDISK_EXTENSION diskExtension; PINQUIRYDATA inquiryData; STRING vendor; STRING product; STRING revLevel; PAGED_CODE(); // // Get pointers to the device extension and to the inquiry data buffer. // diskExtension = DeviceObject->DeviceExtension; inquiryData = (PINQUIRYDATA)Srb->DataBuffer; // // Build the inquiry data. // RtlInitString( &vendor, "Microsoft" ); RtlInitString( &product, "Ramdisk" ); RtlInitString( &revLevel, "1.0" ); RtlZeroMemory( inquiryData, INQUIRYDATABUFFERSIZE ); inquiryData->DeviceType = DIRECT_ACCESS_DEVICE; inquiryData->RemovableMedia = (diskExtension->Options.Fixed ? FALSE : TRUE); inquiryData->ANSIVersion = 2; inquiryData->ResponseDataFormat = 2; inquiryData->AdditionalLength = INQUIRYDATABUFFERSIZE - 4; RtlCopyMemory( inquiryData->VendorId, vendor.Buffer, min( vendor.Length, sizeof(inquiryData->VendorId) ) ); RtlCopyMemory( inquiryData->ProductId, product.Buffer, min( product.Length, sizeof(inquiryData->ProductId) ) ); RtlCopyMemory( inquiryData->ProductRevisionLevel, revLevel.Buffer, min( revLevel.Length, sizeof(inquiryData->ProductRevisionLevel) ) ); return STATUS_SUCCESS; } // BuildInquiryData NTSTATUS BuildModeSenseInfo ( IN PDEVICE_OBJECT DeviceObject, IN OUT PSCSI_REQUEST_BLOCK Srb ) /*++ Routine Description: This routine builds mode sense information. Arguments: DeviceObject - a pointer to the object that represents the device on which I/O is to be performed Srb - the SRB associated with the I/O request Return Value: NTSTATUS - the status of the operation --*/ { PDISK_EXTENSION diskExtension; MODE_PARAMETER_HEADER modeHeader = {0}; MODE_PARAMETER_HEADER10 modeHeader10 = {0}; PVOID header = NULL; PVOID data = NULL; unsigned char headerSize; unsigned dataSize = 0; PCDB cdb; unsigned cdbLength; unsigned dataBufferLength; unsigned char valueType; unsigned dataLength; PAGED_CODE(); // // Get pointers to the device extension and to the inquiry data buffer. // diskExtension = DeviceObject->DeviceExtension; cdb = (PCDB)Srb->Cdb; cdbLength = Srb->CdbLength; // // Dispatch based on the CDB length. // switch ( cdbLength ) { case 6: dataBufferLength = cdb->MODE_SENSE.AllocationLength; valueType = cdb->MODE_SENSE.Pc; headerSize = sizeof(MODE_PARAMETER_HEADER); if ( valueType != 0 ) { // // We only support current value retrieval. // Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; return STATUS_INVALID_DEVICE_REQUEST; } if ( dataBufferLength > headerSize ) { header = &modeHeader; data = (char*)header + headerSize; dataLength = headerSize - FIELD_OFFSET( MODE_PARAMETER_HEADER, MediumType ); modeHeader.ModeDataLength = (UCHAR)dataLength; modeHeader.MediumType = 0x00; modeHeader.DeviceSpecificParameter = 0x00; modeHeader.BlockDescriptorLength = 0x00; } break; case 10: dataBufferLength = *(USHORT *)cdb->MODE_SENSE10.AllocationLength; valueType = cdb->MODE_SENSE10.Pc; headerSize = sizeof(MODE_PARAMETER_HEADER10); if ( dataBufferLength > headerSize ) { header = &modeHeader10; data = (char*)header + headerSize; dataLength = headerSize - FIELD_OFFSET( MODE_PARAMETER_HEADER10, MediumType ); RtlCopyMemory( modeHeader10.ModeDataLength, &dataLength, sizeof(modeHeader10.ModeDataLength) ); modeHeader10.MediumType = 0x00; modeHeader10.DeviceSpecificParameter = 0x00; modeHeader10.BlockDescriptorLength[0] = 0; modeHeader10.BlockDescriptorLength[1] = 0; } break; default: // // Can't get here. // ASSERT( FALSE ); Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; return STATUS_INVALID_DEVICE_REQUEST; } if ( header != NULL ) { RtlCopyMemory( Srb->DataBuffer, header, headerSize ); dataBufferLength -= headerSize; } if ( (data != NULL) && (dataBufferLength != 0) ) { RtlCopyMemory( (PUCHAR)Srb->DataBuffer + headerSize, data, min( dataBufferLength, dataSize ) ); } return STATUS_SUCCESS; } // BuildModeSenseInfo