|
|
/*++
Copyright (C) Microsoft Corporation, 1991 - 1999
Module Name:
floppy.c
Abstract:
SCSI floppy class driver
Author:
Jeff Havens (jhavens)
Environment:
kernel mode only
Notes:
Revision History: 02/28/96 georgioc Merged this code with code developed by compaq in parallel with microsoft, for 120MB floppy support.
01/17/96 georgioc Made code PNP aware (uses the new \storage\classpnp/scsiport)
--*/
#include "stddef.h"
#include "ntddk.h"
#include "scsi.h"
#include "classpnp.h"
#include "initguid.h"
#include "ntddstor.h"
#define MODE_DATA_SIZE 192
#define SCSI_FLOPPY_TIMEOUT 20
#define SFLOPPY_SRB_LIST_SIZE 4
//
// Define all possible drive/media combinations, given drives listed above
// and media types in ntdddisk.h.
//
// These values are used to index the DriveMediaConstants table.
//
#define NUMBER_OF_DRIVE_TYPES 7
#define DRIVE_TYPE_120M 4 //120MB Floptical
#define DRIVE_TYPE_NONE NUMBER_OF_DRIVE_TYPES
//
// This array describes all media types we support.
// It should be arranged in the increasing order of density
//
// For a given drive, we list all the mediatypes that will
// work with that drive. For instance, a 120MB drive will
// take 720KB media, 1.44MB media, and 120MB media.
//
// Note that, DriveMediaConstants given below is grouped
// as drive and media combination
//
typedef enum _DRIVE_MEDIA_TYPE { Drive360Media160, // 5.25" 360k drive; 160k media
Drive360Media180, // 5.25" 360k drive; 180k media
Drive360Media320, // 5.25" 360k drive; 320k media
Drive360Media32X, // 5.25" 360k drive; 320k 1k secs
Drive360Media360, // 5.25" 360k drive; 360k media
Drive720Media720, // 3.5" 720k drive; 720k media
Drive120Media160, // 5.25" 1.2Mb drive; 160k media
Drive120Media180, // 5.25" 1.2Mb drive; 180k media
Drive120Media320, // 5.25" 1.2Mb drive; 320k media
Drive120Media32X, // 5.25" 1.2Mb drive; 320k 1k secs
Drive120Media360, // 5.25" 1.2Mb drive; 360k media
Drive120Media120, // 5.25" 1.2Mb drive; 1.2Mb media
Drive144Media720, // 3.5" 1.44Mb drive; 720k media
Drive144Media144, // 3.5" 1.44Mb drive; 1.44Mb media
Drive288Media720, // 3.5" 2.88Mb drive; 720k media
Drive288Media144, // 3.5" 2.88Mb drive; 1.44Mb media
Drive288Media288, // 3.5" 2.88Mb drive; 2.88Mb media
Drive2080Media720, // 3.5" 20.8Mb drive; 720k media
Drive2080Media144, // 3.5" 20.8Mb drive; 1.44Mb media
Drive2080Media2080, // 3.5" 20.8Mb drive; 20.8Mb media
Drive32MMedia32M, // 3.5" 32Mb drive; 32MB media
Drive120MMedia720, // 3.5" 120Mb drive; 720k media
Drive120MMedia144, // 3.5" 120Mb drive; 1.44Mb media
Drive120MMedia120M, // 3.5" 120Mb drive; 120Mb media
Drive240MMedia144M, // 3.5" 240Mb drive; 1.44Mb media
Drive240MMedia120M, // 3.5" 240Mb drive; 120Mb media
Drive240MMedia240M // 3.5" 240Mb drive; 240Mb media
} DRIVE_MEDIA_TYPE;
//
// When we want to determine the media type in a drive, we will first
// guess that the media with highest possible density is in the drive,
// and keep trying lower densities until we can successfully read from
// the drive.
//
// These values are used to select a DRIVE_MEDIA_TYPE value.
//
// The following table defines ranges that apply to the DRIVE_MEDIA_TYPE
// enumerated values when trying media types for a particular drive type.
// Note that for this to work, the DRIVE_MEDIA_TYPE values must be sorted
// by ascending densities within drive types. Also, for maximum track
// size to be determined properly, the drive types must be in ascending
// order.
//
typedef struct _DRIVE_MEDIA_LIMITS { DRIVE_MEDIA_TYPE HighestDriveMediaType; DRIVE_MEDIA_TYPE LowestDriveMediaType; } DRIVE_MEDIA_LIMITS, *PDRIVE_MEDIA_LIMITS;
#if 0
DRIVE_MEDIA_LIMITS DriveMediaLimits[NUMBER_OF_DRIVE_TYPES] = {
{ Drive360Media360, Drive360Media160 }, // DRIVE_TYPE_0360
{ Drive120Media120, Drive120Media160 }, // DRIVE_TYPE_1200
{ Drive720Media720, Drive720Media720 }, // DRIVE_TYPE_0720
{ Drive144Media144, Drive144Media720 }, // DRIVE_TYPE_1440
{ Drive288Media288, Drive288Media720 }, // DRIVE_TYPE_2880
{ Drive2080Media2080, Drive2080Media720 } }; #else
DRIVE_MEDIA_LIMITS DriveMediaLimits[NUMBER_OF_DRIVE_TYPES] = {
{ Drive720Media720, Drive720Media720 }, // DRIVE_TYPE_0720
{ Drive144Media144, Drive144Media720}, // DRIVE_TYPE_1440
{ Drive288Media288, Drive288Media720}, // DRIVE_TYPE_2880
{ Drive2080Media2080, Drive2080Media720 }, { Drive32MMedia32M, Drive32MMedia32M }, // DRIVE_TYPE_32M
{ Drive120MMedia120M, Drive120MMedia720 }, // DRIVE_TYPE_120M
{ Drive240MMedia240M, Drive240MMedia144M } // DRIVE_TYPE_240M
};
#endif
//
// For each drive/media combination, define important constants.
//
typedef struct _DRIVE_MEDIA_CONSTANTS { MEDIA_TYPE MediaType; USHORT BytesPerSector; UCHAR SectorsPerTrack; USHORT MaximumTrack; UCHAR NumberOfHeads; } DRIVE_MEDIA_CONSTANTS, *PDRIVE_MEDIA_CONSTANTS;
//
// Magic value to add to the SectorLengthCode to use it as a shift value
// to determine the sector size.
//
#define SECTORLENGTHCODE_TO_BYTESHIFT 7
//
// The following values were gleaned from many different sources, which
// often disagreed with each other. Where numbers were in conflict, I
// chose the more conservative or most-often-selected value.
//
DRIVE_MEDIA_CONSTANTS DriveMediaConstants[] = {
{ F5_160_512, 0x200, 0x08, 0x27, 0x1 }, { F5_180_512, 0x200, 0x09, 0x27, 0x1 }, { F5_320_1024, 0x400, 0x04, 0x27, 0x2 }, { F5_320_512, 0x200, 0x08, 0x27, 0x2 }, { F5_360_512, 0x200, 0x09, 0x27, 0x2 },
{ F3_720_512, 0x200, 0x09, 0x4f, 0x2 },
{ F5_160_512, 0x200, 0x08, 0x27, 0x1 }, { F5_180_512, 0x200, 0x09, 0x27, 0x1 }, { F5_320_1024, 0x400, 0x04, 0x27, 0x2 }, { F5_320_512, 0x200, 0x08, 0x27, 0x2 }, { F5_360_512, 0x200, 0x09, 0x27, 0x2 }, { F5_1Pt2_512, 0x200, 0x0f, 0x4f, 0x2 },
{ F3_720_512, 0x200, 0x09, 0x4f, 0x2 }, { F3_1Pt44_512, 0x200, 0x12, 0x4f, 0x2 },
{ F3_720_512, 0x200, 0x09, 0x4f, 0x2 }, { F3_1Pt44_512, 0x200, 0x12, 0x4f, 0x2 }, { F3_2Pt88_512, 0x200, 0x24, 0x4f, 0x2 },
{ F3_720_512, 0x200, 0x09, 0x4f, 0x2 }, { F3_1Pt44_512, 0x200, 0x12, 0x4f, 0x2 }, { F3_20Pt8_512, 0x200, 0x1b, 0xfa, 0x6 },
{ F3_32M_512, 0x200, 0x20, 0x3ff,0x2},
{ F3_720_512, 0x200, 0x09, 0x4f, 0x2 }, { F3_1Pt44_512, 0x200, 0x12, 0x4f, 0x2 }, { F3_120M_512, 0x200, 0x20, 0x3c2,0x8 },
{ F3_1Pt44_512, 0x200, 0x12, 0x4f, 0x2 }, { F3_120M_512, 0x200, 0x20, 0x3c2,0x8 }, { F3_240M_512, 0x200, 0x38, 0x105,0x20} };
#define NUMBER_OF_DRIVE_MEDIA_COMBINATIONS sizeof(DriveMediaConstants)/sizeof(DRIVE_MEDIA_CONSTANTS)
//
// floppy device data
//
typedef struct _DISK_DATA { ULONG DriveType; BOOLEAN IsDMF; // BOOLEAN EnableDMF;
UNICODE_STRING FloppyInterfaceString; } DISK_DATA, *PDISK_DATA;
//
// The FloppyCapacities and FloppyGeometries arrays are used by the
// USBFlopGetMediaTypes() and USBFlopFormatTracks() routines.
// The FloppyCapacities and FloppyGeometries arrays must be kept in 1:1 sync,
// i.e. each FloppyGeometries[i] must correspond to each FloppyCapacities[i].
// Also, the arrays must be kept in sorted ascending order so that they
// are returned in sorted ascending order by IOCTL_DISK_GET_MEDIA_TYPES.
//
typedef struct _FORMATTED_CAPACITY { ULONG NumberOfBlocks;
ULONG BlockLength;
BOOLEAN CanFormat; // return for IOCTL_DISK_GET_MEDIA_TYPES ?
} FORMATTED_CAPACITY, *PFORMATTED_CAPACITY;
FORMATTED_CAPACITY FloppyCapacities[] = { // Blocks BlockLen CanFormat H T B/S S/T
{0x000500, 0x0200, TRUE}, // 2 80 512 8 640 KB F5_640_512
{0x0005A0, 0x0200, TRUE}, // 2 80 512 9 720 KB F3_720_512
{0x000960, 0x0200, TRUE}, // 2 80 512 15 1.20 MB F3_1Pt2_512 (Toshiba)
{0x0004D0, 0x0400, TRUE}, // 2 77 1024 8 1.23 MB F3_1Pt23_1024 (NEC)
{0x000B40, 0x0200, TRUE}, // 2 80 512 18 1.44 MB F3_1Pt44_512
{0x000D20, 0x0200, FALSE}, // 2 80 512 21 1.70 MB DMF
{0x010000, 0x0200, TRUE}, // 2 1024 512 32 32 MB F3_32M_512
{0x03C300, 0x0200, TRUE}, // 8 963 512 32 120 MB F3_120M_512
{0x0600A4, 0x0200, TRUE}, // 13 890 512 34 200 MB F3_200Mb_512 (HiFD)
{0x072A00, 0x0200, TRUE} // 32 262 512 56 240 MB F3_240M_512
};
DISK_GEOMETRY FloppyGeometries[] = { // Cyl MediaType Trk/Cyl Sec/Trk Bytes/Sec
{{80,0}, F3_640_512, 2, 8, 512}, {{80,0}, F3_720_512, 2, 9, 512}, {{80,0}, F3_1Pt2_512, 2, 15, 512}, {{77,0}, F3_1Pt23_1024, 2, 8, 1024}, {{80,0}, F3_1Pt44_512, 2, 18, 512}, {{80,0}, F3_1Pt44_512, 2, 21, 512}, // DMF -> F3_1Pt44_512
{{1024,0}, F3_32M_512, 2, 32, 512}, {{963,0}, F3_120M_512, 8, 32, 512}, {{890,0}, F3_200Mb_512, 13, 34, 512}, {{262,0}, F3_240M_512, 32, 56, 512} };
#define FLOPPY_CAPACITIES (sizeof(FloppyCapacities)/sizeof(FloppyCapacities[0]))
C_ASSERT((sizeof(FloppyGeometries)/sizeof(FloppyGeometries[0])) == FLOPPY_CAPACITIES);
//
// The following structures are used by USBFlopFormatTracks()
//
#pragma pack (push, 1)
typedef struct _CDB12FORMAT { UCHAR OperationCode; UCHAR DefectListFormat : 3; UCHAR CmpList : 1; UCHAR FmtData : 1; UCHAR LogicalUnitNumber : 3; UCHAR TrackNumber; UCHAR InterleaveMsb; UCHAR InterleaveLsb; UCHAR Reserved1[2]; UCHAR ParameterListLengthMsb; UCHAR ParameterListLengthLsb; UCHAR Reserved2[3]; } CDB12FORMAT, *PCDB12FORMAT;
typedef struct _DEFECT_LIST_HEADER { UCHAR Reserved1; UCHAR Side : 1; UCHAR Immediate : 1; UCHAR Reserved2 : 2; UCHAR SingleTrack : 1; UCHAR DisableCert : 1; UCHAR Reserved3 : 1; UCHAR FormatOptionsValid : 1; UCHAR DefectListLengthMsb; UCHAR DefectListLengthLsb; } DEFECT_LIST_HEADER, *PDEFECT_LIST_HEADER;
typedef struct _FORMAT_UNIT_PARAMETER_LIST { DEFECT_LIST_HEADER DefectListHeader; FORMATTED_CAPACITY_DESCRIPTOR FormatDescriptor; } FORMAT_UNIT_PARAMETER_LIST, *PFORMAT_UNIT_PARAMETER_LIST;
#pragma pack (pop)
NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath );
VOID ScsiFlopUnload( IN PDRIVER_OBJECT DriverObject );
NTSTATUS ScsiFlopAddDevice ( IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT Pdo );
NTSTATUS ScsiFlopInitDevice( IN PDEVICE_OBJECT Fdo );
NTSTATUS ScsiFlopStartDevice( IN PDEVICE_OBJECT Fdo );
NTSTATUS ScsiFlopRemoveDevice( IN PDEVICE_OBJECT Fdo, IN UCHAR Type );
NTSTATUS ScsiFlopStopDevice( IN PDEVICE_OBJECT Fdo, IN UCHAR Type );
BOOLEAN FindScsiFlops( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath, IN PCLASS_INIT_DATA InitializationData, IN PDEVICE_OBJECT PortDeviceObject, IN ULONG PortNumber );
NTSTATUS ScsiFlopReadWriteVerification( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp );
NTSTATUS ScsiFlopDeviceControl( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp );
BOOLEAN IsFloppyDevice( PDEVICE_OBJECT DeviceObject );
NTSTATUS CreateFlopDeviceObject( IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT PortDeviceObject, IN ULONG DeviceCount );
NTSTATUS DetermineMediaType( PDEVICE_OBJECT DeviceObject );
ULONG DetermineDriveType( PDEVICE_OBJECT DeviceObject );
BOOLEAN FlCheckFormatParameters( IN PDEVICE_OBJECT DeviceObject, IN PFORMAT_PARAMETERS FormatParameters );
NTSTATUS FormatMedia( PDEVICE_OBJECT DeviceObject, MEDIA_TYPE MediaType );
NTSTATUS FlopticalFormatMedia( PDEVICE_OBJECT DeviceObject, PFORMAT_PARAMETERS Format );
VOID ScsiFlopProcessError( PDEVICE_OBJECT DeviceObject, PSCSI_REQUEST_BLOCK Srb, NTSTATUS *Status, BOOLEAN *Retry );
LONG SFlopStringCmp ( PCHAR FirstStr, PCHAR SecondStr, ULONG Count );
NTSTATUS USBFlopGetMediaTypes( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp );
NTSTATUS USBFlopFormatTracks( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp );
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, DriverEntry)
#pragma alloc_text(PAGE, ScsiFlopUnload)
#pragma alloc_text(PAGE, ScsiFlopAddDevice)
#pragma alloc_text(PAGE, CreateFlopDeviceObject)
#pragma alloc_text(PAGE, ScsiFlopStartDevice)
#pragma alloc_text(PAGE, IsFloppyDevice)
#pragma alloc_text(PAGE, SFlopStringCmp)
#pragma alloc_text(PAGE, DetermineMediaType)
#pragma alloc_text(PAGE, DetermineDriveType)
#pragma alloc_text(PAGE, FlCheckFormatParameters)
#pragma alloc_text(PAGE, FormatMedia)
#pragma alloc_text(PAGE, FlopticalFormatMedia)
#pragma alloc_text(PAGE, USBFlopGetMediaTypes)
#pragma alloc_text(PAGE, USBFlopFormatTracks)
#endif
NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) /*++
Routine Description:
This is the system initialization routine for installable drivers. It calls the SCSI class driver initialization routine.
Arguments:
DriverObject - Pointer to driver object created by system.
Return Value:
NTSTATUS
--*/
{ CLASS_INIT_DATA InitializationData;
//
// Zero InitData
//
RtlZeroMemory (&InitializationData, sizeof(CLASS_INIT_DATA));
//
// Set sizes
//
InitializationData.InitializationDataSize = sizeof(CLASS_INIT_DATA); InitializationData.FdoData.DeviceExtensionSize = sizeof(FUNCTIONAL_DEVICE_EXTENSION) + sizeof(DISK_DATA);
InitializationData.FdoData.DeviceType = FILE_DEVICE_DISK; InitializationData.FdoData.DeviceCharacteristics = FILE_REMOVABLE_MEDIA | FILE_FLOPPY_DISKETTE;
//
// Set entry points
//
InitializationData.FdoData.ClassInitDevice = ScsiFlopInitDevice; InitializationData.FdoData.ClassStartDevice = ScsiFlopStartDevice; InitializationData.FdoData.ClassStopDevice = ScsiFlopStopDevice; InitializationData.FdoData.ClassRemoveDevice = ScsiFlopRemoveDevice;
InitializationData.FdoData.ClassReadWriteVerification = ScsiFlopReadWriteVerification; InitializationData.FdoData.ClassDeviceControl = ScsiFlopDeviceControl;
InitializationData.FdoData.ClassShutdownFlush = NULL; InitializationData.FdoData.ClassCreateClose = NULL; InitializationData.FdoData.ClassError = ScsiFlopProcessError; InitializationData.ClassStartIo = NULL;
InitializationData.ClassAddDevice = ScsiFlopAddDevice; InitializationData.ClassUnload = ScsiFlopUnload; //
// Call the class init routine
//
return ClassInitialize( DriverObject, RegistryPath, &InitializationData);
} // end DriverEntry()
VOID ScsiFlopUnload( IN PDRIVER_OBJECT DriverObject ) { PAGED_CODE(); UNREFERENCED_PARAMETER(DriverObject); return; }
NTSTATUS ScsiFlopAddDevice ( IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT Pdo ) /*++
Routine Description:
This routine creates and initializes a new FDO for the corresponding PDO. It may perform property queries on the FDO but cannot do any media access operations.
Arguments:
DriverObject - Scsiscan class driver object.
Pdo - the physical device object we are being added to
Return Value:
status
--*/ {
PCONFIGURATION_INFORMATION configurationInformation;
NTSTATUS status; ULONG floppyCount = IoGetConfigurationInformation()->FloppyCount;
//
// Get the number of disks already initialized.
//
status = CreateFlopDeviceObject(DriverObject, Pdo, floppyCount);
if (NT_SUCCESS(status)) {
//
// Increment system floppy device count.
//
IoGetConfigurationInformation()->FloppyCount++; }
return status; }
NTSTATUS CreateFlopDeviceObject( IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT Pdo, IN ULONG DeviceCount )
/*++
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 Floppys. AdapterDescriptor - Pointer to structure returned by SCSI port driver describing adapter capabilites (and limitations). DeviceDescriptor - Pointer to configuration information for this device.
Return Value:
--*/ { NTSTATUS status; PDEVICE_OBJECT deviceObject; PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = NULL;
PDISK_DATA diskData; PVOID senseData;
BOOLEAN freeDevice = TRUE;
DebugPrint((3,"CreateFlopDeviceObject: Enter routine\n"));
//
// Try to claim the device.
//
status = ClassClaimDevice(Pdo,FALSE);
if (!NT_SUCCESS(status)) { return(status); }
DeviceCount--;
do { UCHAR name[256];
//
// Create device object for this device.
//
DeviceCount++;
sprintf(name, "\\Device\\Floppy%d", DeviceCount);
status = ClassCreateDeviceObject(DriverObject, name, Pdo, TRUE, &deviceObject);
} while ((status == STATUS_OBJECT_NAME_COLLISION) || (status == STATUS_OBJECT_NAME_EXISTS));
if (!NT_SUCCESS(status)) { DebugPrint((1,"CreateFlopDeviceObjects: Can not create device\n")); goto CreateFlopDeviceObjectExit; }
//
// Indicate that IRPs should include MDLs.
//
deviceObject->Flags |= DO_DIRECT_IO;
fdoExtension = deviceObject->DeviceExtension;
//
// Back pointer to device object.
//
fdoExtension->CommonExtension.DeviceObject = deviceObject;
//
// This is the physical device.
//
fdoExtension->CommonExtension.PartitionZeroExtension = fdoExtension;
//
// Reset the drive type.
//
diskData = (PDISK_DATA) fdoExtension->CommonExtension.DriverData; diskData->DriveType = DRIVE_TYPE_NONE; diskData->IsDMF = FALSE; // diskData->EnableDMF = TRUE;
//
// Initialize lock count to zero. The lock count is used to
// disable the ejection mechanism when media is mounted.
//
fdoExtension->LockCount = 0;
//
// Save system floppy number
//
fdoExtension->DeviceNumber = DeviceCount;
//
// Set the alignment requirements for the device based on the
// host adapter requirements
//
if (Pdo->AlignmentRequirement > deviceObject->AlignmentRequirement) { deviceObject->AlignmentRequirement = Pdo->AlignmentRequirement; }
//
// Clear the SrbFlags and disable synchronous transfers
//
fdoExtension->SrbFlags = SRB_FLAGS_DISABLE_SYNCH_TRANSFER;
//
// Finally, attach to the PDO
//
fdoExtension->LowerPdo = Pdo;
fdoExtension->CommonExtension.LowerDeviceObject = IoAttachDeviceToDeviceStack(deviceObject, Pdo);
if(fdoExtension->CommonExtension.LowerDeviceObject == NULL) {
status = STATUS_UNSUCCESSFUL; goto CreateFlopDeviceObjectExit; }
deviceObject->StackSize++;
//
// The device is initialized properly - mark it as such.
//
deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
return STATUS_SUCCESS;
CreateFlopDeviceObjectExit:
if (deviceObject != NULL) { IoDeleteDevice(deviceObject); }
return status;
} // end CreateFlopDeviceObject()
NTSTATUS ScsiFlopInitDevice( IN PDEVICE_OBJECT Fdo ) { PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension; PDISK_DATA diskData = commonExtension->DriverData;
PVOID senseData = NULL; ULONG timeOut; BOOLEAN srbListInitialized = FALSE;
NTSTATUS status = STATUS_SUCCESS;
//
// Allocate request sense buffer.
//
senseData = ExAllocatePool(NonPagedPoolCacheAligned, SENSE_BUFFER_SIZE);
if (senseData == NULL) {
//
// The buffer cannot be allocated.
//
status = STATUS_INSUFFICIENT_RESOURCES; return status; }
//
// Set the sense data pointer in the device extension.
//
fdoExtension->SenseData = senseData;
//
// Build the lookaside list for srb's for this device.
//
ClassInitializeSrbLookasideList((PCOMMON_DEVICE_EXTENSION)fdoExtension, SFLOPPY_SRB_LIST_SIZE);
srbListInitialized = TRUE;
//
// Register for media change notification
//
ClassInitializeMediaChangeDetection(fdoExtension, "SFloppy");
//
// Set timeout value in seconds.
//
timeOut = ClassQueryTimeOutRegistryValue(Fdo); if (timeOut) { fdoExtension->TimeOutValue = timeOut; } else { fdoExtension->TimeOutValue = SCSI_FLOPPY_TIMEOUT; }
//
// Floppies are not partitionable so starting offset is 0.
//
fdoExtension->CommonExtension.StartingOffset.QuadPart = (LONGLONG)0;
#if 0
if (!IsFloppyDevice(Fdo) || !(Fdo->Characteristics & FILE_REMOVABLE_MEDIA) || (fdoExtension->DeviceDescriptor->DeviceType != DIRECT_ACCESS_DEVICE)) {
ExFreePool(senseData); status = STATUS_NO_SUCH_DEVICE; return status; } #endif
RtlZeroMemory(&(fdoExtension->DiskGeometry), sizeof(DISK_GEOMETRY));
//
// Determine the media type if possible. Set the current media type to
// Unknown so that determine media type will check the media.
//
fdoExtension->DiskGeometry.MediaType = Unknown;
#if 0
{ PUCHAR vendorId; UCHAR vendorString[6] = {'I','N','S','I','T','E'};
vendorId = (PUCHAR)fdoExtension->DeviceDescriptor + fdoExtension->DeviceDescriptor->VendorIdOffset; if (!SFlopStringCmp(vendorId,vendorString,6)) { diskData->EnableDMF = FALSE; } } #endif
//
// Register interfaces for this device.
//
{ UNICODE_STRING interfaceName;
RtlInitUnicodeString(&interfaceName, NULL);
status = IoRegisterDeviceInterface(fdoExtension->LowerPdo, (LPGUID) &FloppyClassGuid, NULL, &interfaceName);
if(NT_SUCCESS(status)) { diskData->FloppyInterfaceString = interfaceName; } else { RtlInitUnicodeString(&(diskData->FloppyInterfaceString), NULL); DebugPrint((1, "ScsiFlopStartDevice: Unable to register device " "interface for fdo %#p [%#08lx]\n", Fdo, status)); } }
return (STATUS_SUCCESS); }
NTSTATUS ScsiFlopStartDevice( IN PDEVICE_OBJECT Fdo ) { PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension;
PIRP irp; IO_STATUS_BLOCK ioStatus;
SCSI_ADDRESS scsiAddress;
WCHAR ntNameBuffer[256]; UNICODE_STRING ntUnicodeString;
WCHAR arcNameBuffer[256]; UNICODE_STRING arcUnicodeString;
KEVENT event;
NTSTATUS status = STATUS_SUCCESS;
KeInitializeEvent(&event,SynchronizationEvent,FALSE);
DetermineMediaType(Fdo); // ignore unsuccessful here
//
// Create a symbolic link from the disk name to the corresponding
// ARC name, to be used if we're booting off the disk. This will
// fail if it's not system initialization time; that's fine. The
// ARC name looks something like \ArcName\scsi(0)Flop(0)fdisk(0).
// In order to get the address, we need to send a IOCTL_SCSI_GET_ADDRESS...
//
irp = IoBuildDeviceIoControlRequest(IOCTL_SCSI_GET_ADDRESS, Fdo, NULL, 0, &scsiAddress, sizeof(scsiAddress), FALSE, &event, &ioStatus);
if (irp == NULL) { return STATUS_INSUFFICIENT_RESOURCES; }
status = IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp);
if (status == STATUS_PENDING) { KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = ioStatus.Status; }
swprintf(arcNameBuffer, L"\\ArcName\\scsi(%d)disk(%d)fdisk(%d)", scsiAddress.PortNumber, scsiAddress.TargetId, scsiAddress.Lun);
RtlInitUnicodeString(&arcUnicodeString, arcNameBuffer);
//
// Create device object for this device.
//
swprintf(ntNameBuffer,L"\\Device\\Floppy%d",fdoExtension->DeviceNumber);
//
// Create local copy of unicode string
//
RtlInitUnicodeString(&ntUnicodeString,ntNameBuffer);
IoAssignArcName(&arcUnicodeString, &ntUnicodeString);
//
// Create the multi() arc name -- Create the "fake"
// name of multi(0)disk(0)fdisk(#) to handle the case where the
// SCSI floppy is the only floppy in the system. If this fails
// it doesn't matter because the previous scsi() based ArcName
// will work. This name is necessary for installation.
//
swprintf(arcNameBuffer, L"\\ArcName\\multi(%d)disk(%d)fdisk(%d)", 0, 0, fdoExtension->DeviceNumber);
RtlInitUnicodeString(&arcUnicodeString, arcNameBuffer);
IoAssignArcName(&arcUnicodeString, &ntUnicodeString);
//
// Set our interface state.
//
{ PDISK_DATA diskData = commonExtension->DriverData;
if(diskData->FloppyInterfaceString.Buffer != NULL) {
status = IoSetDeviceInterfaceState( &(diskData->FloppyInterfaceString), TRUE);
#if DBG
if(!NT_SUCCESS(status)) { DebugPrint((1, "ScsiFlopStartDevice: Unable to set device " "interface state to TRUE for fdo %#p " "[%#08lx]\n", Fdo, status)); } #endif
} }
return STATUS_SUCCESS; }
NTSTATUS ScsiFlopReadWriteVerification( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp )
/*++
Routine Description:
Arguments:
Return Value:
NT Status
--*/
{ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension; PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); NTSTATUS status = STATUS_SUCCESS;
//
// Make sure that the number of bytes to transfer is a multiple of the sector size
//
if ((irpSp->Parameters.Read.Length & (fdoExtension->DiskGeometry.BytesPerSector - 1)) != 0) { status = STATUS_INVALID_PARAMETER; }
Irp->IoStatus.Status = status;
return status; }
NTSTATUS ScsiFlopDeviceControl( PDEVICE_OBJECT DeviceObject, PIRP Irp )
/*++
Routine Description:
Arguments:
Return Value:
Status is returned.
--*/
{ KIRQL currentIrql; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension; PSCSI_REQUEST_BLOCK srb; PCDB cdb; NTSTATUS status; PDISK_GEOMETRY outputBuffer; ULONG outputBufferLength; ULONG i; DRIVE_MEDIA_TYPE lowestDriveMediaType; DRIVE_MEDIA_TYPE highestDriveMediaType; PFORMAT_PARAMETERS formatParameters; PMODE_PARAMETER_HEADER modeData; ULONG length;
//
// Initialize the information field
//
Irp->IoStatus.Information = 0;
srb = ExAllocatePool(NonPagedPool, SCSI_REQUEST_BLOCK_SIZE);
if (srb == NULL) {
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; if (IoIsErrorUserInduced(STATUS_INSUFFICIENT_RESOURCES)) {
IoSetHardErrorOrVerifyDevice(Irp, DeviceObject); }
KeRaiseIrql(DISPATCH_LEVEL, ¤tIrql); ClassReleaseRemoveLock(DeviceObject, Irp); ClassCompleteRequest(DeviceObject, Irp, 0); KeLowerIrql(currentIrql);
return(STATUS_INSUFFICIENT_RESOURCES); }
//
// Write zeros to Srb.
//
RtlZeroMemory(srb, SCSI_REQUEST_BLOCK_SIZE);
cdb = (PCDB)srb->Cdb;
switch (irpStack->Parameters.DeviceIoControl.IoControlCode) {
case IOCTL_DISK_VERIFY: {
PVERIFY_INFORMATION verifyInfo = Irp->AssociatedIrp.SystemBuffer; LARGE_INTEGER byteOffset; ULONG sectorOffset; USHORT sectorCount;
//
// Validate buffer length.
//
if (irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(VERIFY_INFORMATION)) {
status = STATUS_INFO_LENGTH_MISMATCH; break; }
//
// Perform a bounds check on the sector range
//
if ((verifyInfo->StartingOffset.QuadPart > fdoExtension->CommonExtension.PartitionLength.QuadPart) || (verifyInfo->StartingOffset.QuadPart < 0)) { status = STATUS_NONEXISTENT_SECTOR; break; } else { ULONGLONG bytesRemaining = fdoExtension->CommonExtension.PartitionLength.QuadPart - verifyInfo->StartingOffset.QuadPart;
if ((ULONGLONG)verifyInfo->Length > bytesRemaining) { status = STATUS_NONEXISTENT_SECTOR; break; } }
//
// Verify sectors
//
srb->CdbLength = 10;
cdb->CDB10.OperationCode = SCSIOP_VERIFY;
//
// Add disk offset to starting sector.
//
byteOffset.QuadPart = fdoExtension->CommonExtension.StartingOffset.QuadPart + verifyInfo->StartingOffset.QuadPart;
//
// Convert byte offset to sector offset.
//
sectorOffset = (ULONG)(byteOffset.QuadPart >> fdoExtension->SectorShift);
//
// Convert ULONG byte count to USHORT sector count.
//
sectorCount = (USHORT)(verifyInfo->Length >> fdoExtension->SectorShift);
//
// Move little endian values into CDB in big endian format.
//
cdb->CDB10.LogicalBlockByte0 = ((PFOUR_BYTE)§orOffset)->Byte3; cdb->CDB10.LogicalBlockByte1 = ((PFOUR_BYTE)§orOffset)->Byte2; cdb->CDB10.LogicalBlockByte2 = ((PFOUR_BYTE)§orOffset)->Byte1; cdb->CDB10.LogicalBlockByte3 = ((PFOUR_BYTE)§orOffset)->Byte0;
cdb->CDB10.TransferBlocksMsb = ((PFOUR_BYTE)§orCount)->Byte1; cdb->CDB10.TransferBlocksLsb = ((PFOUR_BYTE)§orCount)->Byte0;
//
// The verify command is used by the NT FORMAT utility and
// requests are sent down for 5% of the volume size. The
// request timeout value is calculated based on the number of
// sectors verified.
//
srb->TimeOutValue = ((sectorCount + 0x7F) >> 7) * fdoExtension->TimeOutValue;
status = ClassSendSrbAsynchronous(DeviceObject, srb, Irp, NULL, 0, FALSE); return(status);
}
case IOCTL_DISK_GET_PARTITION_INFO: {
if (fdoExtension->AdapterDescriptor->BusType == BusTypeUsb) {
status = USBFlopGetMediaTypes(DeviceObject, NULL);
// Don't need to propagate any error if one occurs
//
status = STATUS_SUCCESS;
} else {
status = DetermineMediaType(DeviceObject); }
if (!NT_SUCCESS(status)) { // so will propogate error
NOTHING; } else if (fdoExtension->DiskGeometry.MediaType == F3_120M_512) { //so that the format code will not try to partition it.
status = STATUS_INVALID_DEVICE_REQUEST; } else { //
// Free the Srb, since it is not needed.
//
ExFreePool(srb);
//
// Pass the request to the common device control routine.
//
return(ClassDeviceControl(DeviceObject, Irp)); } break; }
case IOCTL_DISK_GET_DRIVE_GEOMETRY: {
DebugPrint((3,"ScsiDeviceIoControl: Get drive geometry\n"));
if (fdoExtension->AdapterDescriptor->BusType == BusTypeUsb) { status = USBFlopGetMediaTypes(DeviceObject, Irp); break; }
//
// If there's not enough room to write the
// data, then fail the request.
//
if ( irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof( DISK_GEOMETRY ) ) {
status = STATUS_INVALID_PARAMETER; break; }
status = DetermineMediaType(DeviceObject);
if (!NT_SUCCESS(status)) {
Irp->IoStatus.Information = 0; Irp->IoStatus.Status = status;
} else {
//
// Copy drive geometry information from device extension.
//
RtlMoveMemory(Irp->AssociatedIrp.SystemBuffer, &(fdoExtension->DiskGeometry), sizeof(DISK_GEOMETRY));
Irp->IoStatus.Information = sizeof(DISK_GEOMETRY); status = STATUS_SUCCESS;
}
break; }
case IOCTL_DISK_GET_MEDIA_TYPES: {
if (fdoExtension->AdapterDescriptor->BusType == BusTypeUsb) { status = USBFlopGetMediaTypes(DeviceObject, Irp); break; }
i = DetermineDriveType(DeviceObject);
if (i == DRIVE_TYPE_NONE) { status = STATUS_UNRECOGNIZED_MEDIA; break; }
lowestDriveMediaType = DriveMediaLimits[i].LowestDriveMediaType; highestDriveMediaType = DriveMediaLimits[i].HighestDriveMediaType;
outputBufferLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
//
// Make sure that the input buffer has enough room to return
// at least one descriptions of a supported media type.
//
if ( outputBufferLength < ( sizeof( DISK_GEOMETRY ) ) ) {
status = STATUS_BUFFER_TOO_SMALL; break; }
//
// Assume success, although we might modify it to a buffer
// overflow warning below (if the buffer isn't big enough
// to hold ALL of the media descriptions).
//
status = STATUS_SUCCESS;
if (outputBufferLength < ( sizeof( DISK_GEOMETRY ) * ( highestDriveMediaType - lowestDriveMediaType + 1 ) ) ) {
//
// The buffer is too small for all of the descriptions;
// calculate what CAN fit in the buffer.
//
status = STATUS_BUFFER_OVERFLOW;
highestDriveMediaType = (DRIVE_MEDIA_TYPE)( ( lowestDriveMediaType - 1 ) + ( outputBufferLength / sizeof( DISK_GEOMETRY ) ) ); }
outputBuffer = (PDISK_GEOMETRY) Irp->AssociatedIrp.SystemBuffer;
for (i = (UCHAR)lowestDriveMediaType;i <= (UCHAR)highestDriveMediaType;i++ ) {
outputBuffer->MediaType = DriveMediaConstants[i].MediaType; outputBuffer->Cylinders.LowPart = DriveMediaConstants[i].MaximumTrack + 1; outputBuffer->Cylinders.HighPart = 0; outputBuffer->TracksPerCylinder = DriveMediaConstants[i].NumberOfHeads; outputBuffer->SectorsPerTrack = DriveMediaConstants[i].SectorsPerTrack; outputBuffer->BytesPerSector = DriveMediaConstants[i].BytesPerSector; outputBuffer++;
Irp->IoStatus.Information += sizeof( DISK_GEOMETRY ); }
break; }
case IOCTL_DISK_FORMAT_TRACKS: {
if (fdoExtension->AdapterDescriptor->BusType == BusTypeUsb) { status = USBFlopFormatTracks(DeviceObject, Irp); break; }
//
// Make sure that we got all the necessary format parameters.
//
if ( irpStack->Parameters.DeviceIoControl.InputBufferLength <sizeof( FORMAT_PARAMETERS ) ) {
status = STATUS_INVALID_PARAMETER; break; }
formatParameters = (PFORMAT_PARAMETERS) Irp->AssociatedIrp.SystemBuffer;
//
// Make sure the parameters we got are reasonable.
//
if ( !FlCheckFormatParameters(DeviceObject, formatParameters)) {
status = STATUS_INVALID_PARAMETER; break; }
//
// If this request is for a 20.8 MB floppy then call a special
// floppy format routine.
//
if (formatParameters->MediaType == F3_20Pt8_512) { status = FlopticalFormatMedia(DeviceObject, formatParameters );
break; }
//
// All the work is done in the pass. If this is not the first pass,
// then complete the request and return;
//
if (formatParameters->StartCylinderNumber != 0 || formatParameters->StartHeadNumber != 0) {
status = STATUS_SUCCESS; break; }
status = FormatMedia( DeviceObject, formatParameters->MediaType); break; }
case IOCTL_DISK_IS_WRITABLE: {
if ((fdoExtension->DiskGeometry.MediaType) == F3_32M_512) {
//
// 32MB media is READ ONLY. Just return
// STATUS_MEDIA_WRITE_PROTECTED
//
status = STATUS_MEDIA_WRITE_PROTECTED;
break; }
//
// Determine if the device is writable.
//
modeData = ExAllocatePool(NonPagedPoolCacheAligned, MODE_DATA_SIZE);
if (modeData == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; break; }
RtlZeroMemory(modeData, MODE_DATA_SIZE);
length = ClassModeSense(DeviceObject, (PUCHAR) modeData, MODE_DATA_SIZE, MODE_SENSE_RETURN_ALL);
if (length < sizeof(MODE_PARAMETER_HEADER)) {
//
// Retry the request in case of a check condition.
//
length = ClassModeSense(DeviceObject, (PUCHAR) modeData, MODE_DATA_SIZE, MODE_SENSE_RETURN_ALL);
if (length < sizeof(MODE_PARAMETER_HEADER)) { status = STATUS_IO_DEVICE_ERROR; ExFreePool(modeData); break; } }
if (modeData->DeviceSpecificParameter & MODE_DSP_WRITE_PROTECT) { status = STATUS_MEDIA_WRITE_PROTECTED; } else { status = STATUS_SUCCESS; }
DebugPrint((2,"IOCTL_DISK_IS_WRITABLE returns %08X\n", status));
ExFreePool(modeData); break; }
default: {
DebugPrint((3,"ScsiIoDeviceControl: Unsupported device IOCTL\n"));
//
// Free the Srb, since it is not needed.
//
ExFreePool(srb);
//
// Pass the request to the common device control routine.
//
return(ClassDeviceControl(DeviceObject, Irp));
break; }
} // end switch( ...
//
// Check if SL_OVERRIDE_VERIFY_VOLUME flag is set in the IRP.
// If so, do not return STATUS_VERIFY_REQUIRED
//
if ((status == STATUS_VERIFY_REQUIRED) && (TEST_FLAG(irpStack->Flags, SL_OVERRIDE_VERIFY_VOLUME))) {
status = STATUS_IO_DEVICE_ERROR;
}
Irp->IoStatus.Status = status;
if (!NT_SUCCESS(status) && IoIsErrorUserInduced(status)) {
IoSetHardErrorOrVerifyDevice(Irp, DeviceObject); }
KeRaiseIrql(DISPATCH_LEVEL, ¤tIrql); ClassReleaseRemoveLock(DeviceObject, Irp); ClassCompleteRequest(DeviceObject, Irp, 0); KeLowerIrql(currentIrql);
ExFreePool(srb);
return status;
} // end ScsiFlopDeviceControl()
#if 0
BOOLEAN IsFloppyDevice( PDEVICE_OBJECT DeviceObject ) /*++
Routine Description:
The routine performs the necessary funcitons to deterime if the device is really a floppy rather than a harddisk. This is done by a mode sense command. First a check is made to see if the medimum type is set. Second a check is made for the flexible parameters mode page.
Arguments:
DeviceObject - Supplies the device object to be tested.
Return Value:
Return TRUE if the indicated device is a floppy.
--*/ {
PVOID modeData; PUCHAR pageData; ULONG length;
modeData = ExAllocatePool(NonPagedPoolCacheAligned, MODE_DATA_SIZE);
if (modeData == NULL) { return(FALSE); }
RtlZeroMemory(modeData, MODE_DATA_SIZE);
length = ClassModeSense(DeviceObject, modeData, MODE_DATA_SIZE, MODE_SENSE_RETURN_ALL);
if (length < sizeof(MODE_PARAMETER_HEADER)) {
//
// Retry the request in case of a check condition.
//
length = ClassModeSense(DeviceObject, modeData, MODE_DATA_SIZE, MODE_SENSE_RETURN_ALL);
if (length < sizeof(MODE_PARAMETER_HEADER)) {
ExFreePool(modeData); return(FALSE);
} }
#if 0
//
// Some drives incorrectly report this. In particular the SONY RMO-S350
// when in disk mode.
//
if (((PMODE_PARAMETER_HEADER) modeData)->MediumType >= MODE_FD_SINGLE_SIDE && ((PMODE_PARAMETER_HEADER) modeData)->MediumType <= MODE_FD_MAXIMUM_TYPE) {
DebugPrint((1, "ScsiFlop: MediumType value %2x, This is a floppy.\n", ((PMODE_PARAMETER_HEADER) modeData)->MediumType)); ExFreePool(modeData); return(TRUE); }
#endif
//
// If the length is greater than length indiated by the mode data reset
// the data to the mode data.
//
if (length > (ULONG)((PMODE_PARAMETER_HEADER) modeData)->ModeDataLength + 1) { length = (ULONG)((PMODE_PARAMETER_HEADER) modeData)->ModeDataLength + 1;
}
//
// Look for the flexible disk mode page.
//
pageData = ClassFindModePage( modeData, length, MODE_PAGE_FLEXIBILE, TRUE);
if (pageData != NULL) {
DebugPrint((1, "ScsiFlop: Flexible disk page found, This is a floppy.\n"));
//
// As a special case for the floptical driver do a magic mode sense to
// enable the drive.
//
ClassModeSense(DeviceObject, modeData, 0x2a, 0x2e);
ExFreePool(modeData); return(TRUE);
}
ExFreePool(modeData); return(FALSE);
} #endif
NTSTATUS DetermineMediaType( PDEVICE_OBJECT DeviceObject ) /*++
Routine Description:
This routine determines the floppy media type based on the size of the device. The geometry information is set for the device object.
Arguments:
DeviceObject - Supplies the device object to be tested.
Return Value:
None
--*/ { PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension; PDISK_GEOMETRY geometry; LONG index; NTSTATUS status;
geometry = &(fdoExtension->DiskGeometry);
//
// Issue ReadCapacity to update device extension
// with information for current media.
//
status = ClassReadDriveCapacity(DeviceObject);
if (!NT_SUCCESS(status)) {
//
// Set the media type to unknow and zero the geometry information.
//
geometry->MediaType = Unknown;
return status;
}
//
// Look at the capcity of disk to determine its type.
//
for (index = NUMBER_OF_DRIVE_MEDIA_COMBINATIONS - 1; index >= 0; index--) {
//
// Walk the table backward untill the drive capacity holds all of the
// data and the bytes per setor are equal
//
if ((ULONG) (DriveMediaConstants[index].NumberOfHeads * (DriveMediaConstants[index].MaximumTrack + 1) * DriveMediaConstants[index].SectorsPerTrack * DriveMediaConstants[index].BytesPerSector) <= fdoExtension->CommonExtension.PartitionLength.LowPart && DriveMediaConstants[index].BytesPerSector == geometry->BytesPerSector) {
geometry->MediaType = DriveMediaConstants[index].MediaType; geometry->TracksPerCylinder = DriveMediaConstants[index].NumberOfHeads; geometry->SectorsPerTrack = DriveMediaConstants[index].SectorsPerTrack; geometry->Cylinders.LowPart = DriveMediaConstants[index].MaximumTrack+1; break; } }
if (index == -1) {
//
// Set the media type to unknow and zero the geometry information.
//
geometry->MediaType = Unknown;
} else { //
// DMF check breaks the insight SCSI floppy, so its disabled for that case
//
PDISK_DATA diskData = (PDISK_DATA) fdoExtension->CommonExtension.DriverData;
// if (diskData->EnableDMF == TRUE) {
//
//check to see if DMF
//
PSCSI_REQUEST_BLOCK srb; PVOID readData;
//
// Allocate a Srb for the read command.
//
readData = ExAllocatePool(NonPagedPool, geometry->BytesPerSector); if (readData == NULL) { return STATUS_NO_MEMORY; }
srb = ExAllocatePool(NonPagedPool, SCSI_REQUEST_BLOCK_SIZE);
if (srb == NULL) {
ExFreePool(readData); return STATUS_NO_MEMORY; }
RtlZeroMemory(readData, geometry->BytesPerSector); RtlZeroMemory(srb, SCSI_REQUEST_BLOCK_SIZE);
srb->CdbLength = 10; srb->Cdb[0] = SCSIOP_READ; srb->Cdb[5] = 0; srb->Cdb[8] = (UCHAR) 1;
//
// Set timeout value.
//
srb->TimeOutValue = fdoExtension->TimeOutValue;
//
// Send the mode select data.
//
status = ClassSendSrbSynchronous(DeviceObject, srb, readData, geometry->BytesPerSector, FALSE );
if (NT_SUCCESS(status)) { char *pchar = (char *)readData;
pchar += 3; //skip 3 bytes jump code
// If the MSDMF3. signature is there then mark it as DMF diskette
if (RtlCompareMemory(pchar, "MSDMF3.", 7) == 7) { diskData->IsDMF = TRUE; }
} ExFreePool(readData); ExFreePool(srb); // }// else
} return status; }
ULONG DetermineDriveType( PDEVICE_OBJECT DeviceObject ) /*++
Routine Description:
The routine determines the device type so that the supported medias can be determined. It does a mode sense for the default parameters. This code assumes that the returned values are for the maximum device size.
Arguments:
DeviceObject - Supplies the device object to be tested.
Return Value:
None
--*/ { PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension; PVOID modeData; PDISK_DATA diskData = (PDISK_DATA) fdoExtension->CommonExtension.DriverData; PMODE_FLEXIBLE_DISK_PAGE pageData; ULONG length; LONG index; UCHAR numberOfHeads; UCHAR sectorsPerTrack; USHORT maximumTrack; BOOLEAN applyFix = FALSE;
if (diskData->DriveType != DRIVE_TYPE_NONE) { return(diskData->DriveType); }
modeData = ExAllocatePool(NonPagedPoolCacheAligned, MODE_DATA_SIZE);
if (modeData == NULL) { return(DRIVE_TYPE_NONE); }
RtlZeroMemory(modeData, MODE_DATA_SIZE);
length = ClassModeSense(DeviceObject, modeData, MODE_DATA_SIZE, MODE_PAGE_FLEXIBILE);
if (length < sizeof(MODE_PARAMETER_HEADER)) {
//
// Retry the request one more time
// in case of a check condition.
//
length = ClassModeSense(DeviceObject, modeData, MODE_DATA_SIZE, MODE_PAGE_FLEXIBILE);
if (length < sizeof(MODE_PARAMETER_HEADER)) {
ExFreePool(modeData); return(DRIVE_TYPE_NONE); } }
//
// Look for the flexible disk mode page.
//
pageData = ClassFindModePage( modeData, length, MODE_PAGE_FLEXIBILE, TRUE);
//
// Make sure the page is returned and is large enough.
//
if ((pageData != NULL) && (pageData->PageLength + 2 >= offsetof(MODE_FLEXIBLE_DISK_PAGE, StartWritePrecom))) {
//
// Pull out the heads, cylinders, and sectors.
//
numberOfHeads = pageData->NumberOfHeads; maximumTrack = pageData->NumberOfCylinders[1]; maximumTrack |= pageData->NumberOfCylinders[0] << 8; sectorsPerTrack = pageData->SectorsPerTrack;
//
// Convert from number of cylinders to maximum track.
//
maximumTrack--;
//
// Search for the maximum supported media. Based on the number of heads,
// sectors per track and number of cylinders
//
for (index = 0; index < NUMBER_OF_DRIVE_MEDIA_COMBINATIONS; index++) {
//
// Walk the table forward until the drive capacity holds all of the
// data and the bytes per setor are equal
//
if (DriveMediaConstants[index].NumberOfHeads == numberOfHeads && DriveMediaConstants[index].MaximumTrack == maximumTrack && DriveMediaConstants[index].SectorsPerTrack ==sectorsPerTrack) {
ExFreePool(modeData);
//
// index is now a drive media combination. Compare this to
// the maximum drive media type in the drive media table.
//
for (length = 0; length < NUMBER_OF_DRIVE_TYPES; length++) {
if (DriveMediaLimits[length].HighestDriveMediaType == index) { return(length); } } return(DRIVE_TYPE_NONE); } }
// If the maximum track is greater than 8 bits then divide the
// number of tracks by 3 and multiply the number of heads by 3.
// This is a special case for the 20.8 MB floppy.
//
if (!applyFix && maximumTrack >= 0x0100) { applyFix = TRUE; maximumTrack++; maximumTrack /= 3; maximumTrack--; numberOfHeads *= 3; } else { ExFreePool(modeData); return(DRIVE_TYPE_NONE); }
}
ExFreePool(modeData); return(DRIVE_TYPE_NONE); }
BOOLEAN FlCheckFormatParameters( IN PDEVICE_OBJECT DeviceObject, IN PFORMAT_PARAMETERS FormatParameters )
/*++
Routine Description:
This routine checks the supplied format parameters to make sure that they'll work on the drive to be formatted.
Arguments:
DeviceObject - Pointer to the device object to be formated.
FormatParameters - a pointer to the caller's parameters for the FORMAT.
Return Value:
TRUE if parameters are OK. FALSE if the parameters are bad.
--*/
{ PDRIVE_MEDIA_CONSTANTS driveMediaConstants; DRIVE_MEDIA_TYPE driveMediaType; ULONG index;
//
// Get the device type.
//
index = DetermineDriveType(DeviceObject);
if (index == DRIVE_TYPE_NONE) {
//
// If the determine device type failed then just use the media type
// and try the parameters.
//
driveMediaType = Drive360Media160;
while (( DriveMediaConstants[driveMediaType].MediaType != FormatParameters->MediaType ) && ( driveMediaType < Drive288Media288) ) {
driveMediaType++; }
} else {
//
// Figure out which entry in the DriveMediaConstants table to use.
//
driveMediaType = DriveMediaLimits[index].HighestDriveMediaType;
while ( ( DriveMediaConstants[driveMediaType].MediaType != FormatParameters->MediaType ) && ( driveMediaType > DriveMediaLimits[index]. LowestDriveMediaType ) ) {
driveMediaType--; }
}
if ( DriveMediaConstants[driveMediaType].MediaType != FormatParameters->MediaType ) { return FALSE;
} else {
driveMediaConstants = &DriveMediaConstants[driveMediaType];
if ( ( FormatParameters->StartHeadNumber > (ULONG)( driveMediaConstants->NumberOfHeads - 1 ) ) || ( FormatParameters->EndHeadNumber > (ULONG)( driveMediaConstants->NumberOfHeads - 1 ) ) || ( FormatParameters->StartCylinderNumber > driveMediaConstants->MaximumTrack ) || ( FormatParameters->EndCylinderNumber > driveMediaConstants->MaximumTrack ) || ( FormatParameters->EndCylinderNumber < FormatParameters->StartCylinderNumber ) ) {
return FALSE;
} else {
return TRUE; } } }
NTSTATUS FormatMedia( PDEVICE_OBJECT DeviceObject, MEDIA_TYPE MediaType ) /*++
Routine Description:
This routine formats the floppy disk. The entire floppy is formated in one shot.
Arguments:
DeviceObject - Supplies the device object to be tested.
Irp - Supplies a pointer to the requesting Irp.
MediaType - Supplies the media type format the device for.
Return Value:
Returns a status for the operation.
--*/ { PVOID modeData; PSCSI_REQUEST_BLOCK srb; PMODE_FLEXIBLE_DISK_PAGE pageData; DRIVE_MEDIA_TYPE driveMediaType; PDRIVE_MEDIA_CONSTANTS driveMediaConstants; ULONG length; NTSTATUS status;
modeData = ExAllocatePool(NonPagedPoolCacheAligned, MODE_DATA_SIZE);
if (modeData == NULL) { return(STATUS_INSUFFICIENT_RESOURCES); }
RtlZeroMemory(modeData, MODE_DATA_SIZE);
length = ClassModeSense(DeviceObject, modeData, MODE_DATA_SIZE, MODE_PAGE_FLEXIBILE);
if (length < sizeof(MODE_PARAMETER_HEADER)) { ExFreePool(modeData); return(STATUS_INVALID_DEVICE_REQUEST); }
//
// Look for the flexible disk mode page.
//
pageData = ClassFindModePage( modeData, length, MODE_PAGE_FLEXIBILE, TRUE);
//
// Make sure the page is returned and is large enough.
//
if ((pageData == NULL) || (pageData->PageLength + 2 < offsetof(MODE_FLEXIBLE_DISK_PAGE, StartWritePrecom))) {
ExFreePool(modeData); return(STATUS_INVALID_DEVICE_REQUEST);
}
//
// Look for a drive media type which matches the requested media type.
//
//
//start from Drive120MMedia120M instead of Drive2080Media2080
//
for (driveMediaType = Drive120MMedia120M; DriveMediaConstants[driveMediaType].MediaType != MediaType; driveMediaType--) { if (driveMediaType == Drive360Media160) {
ExFreePool(modeData); return(STATUS_INVALID_PARAMETER);
} }
driveMediaConstants = &DriveMediaConstants[driveMediaType];
if ((pageData->NumberOfHeads != driveMediaConstants->NumberOfHeads) || (pageData->SectorsPerTrack != driveMediaConstants->SectorsPerTrack) || ((pageData->NumberOfCylinders[0] != (UCHAR)(driveMediaConstants->MaximumTrack+1) >> 8) && (pageData->NumberOfCylinders[1] != (UCHAR)driveMediaConstants->MaximumTrack+1)) || (pageData->BytesPerSector[0] != driveMediaConstants->BytesPerSector >> 8 )) {
//
// Update the flexible parameters page with the new parameters.
//
pageData->NumberOfHeads = driveMediaConstants->NumberOfHeads; pageData->SectorsPerTrack = driveMediaConstants->SectorsPerTrack; pageData->NumberOfCylinders[0] = (UCHAR)(driveMediaConstants->MaximumTrack+1) >> 8; pageData->NumberOfCylinders[1] = (UCHAR)driveMediaConstants->MaximumTrack+1; pageData->BytesPerSector[0] = driveMediaConstants->BytesPerSector >> 8;
//
// Clear the mode parameter header.
//
RtlZeroMemory(modeData, sizeof(MODE_PARAMETER_HEADER));
//
// Set the length equal to the length returned for the flexible page.
//
length = pageData->PageLength + 2;
//
// Copy the page after the mode parameter header.
//
RtlMoveMemory((PCHAR) modeData + sizeof(MODE_PARAMETER_HEADER), pageData, length ); length += sizeof(MODE_PARAMETER_HEADER);
//
// Allocate a Srb for the format command.
//
srb = ExAllocatePool(NonPagedPool, SCSI_REQUEST_BLOCK_SIZE);
if (srb == NULL) {
ExFreePool(modeData); return(STATUS_INSUFFICIENT_RESOURCES); }
RtlZeroMemory(srb, SCSI_REQUEST_BLOCK_SIZE);
srb->CdbLength = 6; srb->Cdb[0] = SCSIOP_MODE_SELECT; srb->Cdb[4] = (UCHAR) length;
//
// Set the PF bit.
//
srb->Cdb[1] |= 0x10;
//
// Set timeout value.
//
srb->TimeOutValue = 2;
//
// Send the mode select data.
//
status = ClassSendSrbSynchronous(DeviceObject, srb, modeData, length, TRUE );
//
// The mode data not needed any more so free it.
//
ExFreePool(modeData);
if (!NT_SUCCESS(status)) { ExFreePool(srb); return(status); }
} else {
//
// The mode data not needed any more so free it.
//
ExFreePool(modeData);
//
// Allocate a Srb for the format command.
//
srb = ExAllocatePool(NonPagedPool, SCSI_REQUEST_BLOCK_SIZE);
if (srb == NULL) { return(STATUS_INSUFFICIENT_RESOURCES); }
}
RtlZeroMemory(srb, SCSI_REQUEST_BLOCK_SIZE);
srb->CdbLength = 6;
srb->Cdb[0] = SCSIOP_FORMAT_UNIT;
//
// Set timeout value.
//
srb->TimeOutValue = 10 * 60;
status = ClassSendSrbSynchronous(DeviceObject, srb, NULL, 0, FALSE ); ExFreePool(srb);
return(status);
}
VOID ScsiFlopProcessError( PDEVICE_OBJECT DeviceObject, PSCSI_REQUEST_BLOCK Srb, NTSTATUS *Status, BOOLEAN *Retry ) /*++
Routine Description:
This routine checks the type of error. If the error indicate the floppy controller needs to be reinitialize a command is made to do it.
Arguments:
DeviceObject - Supplies a pointer to the device object.
Srb - Supplies a pointer to the failing Srb.
Status - Status with which the IRP will be completed.
Retry - Indication of whether the request will be retried.
Return Value:
None.
--*/
{ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension; PDISK_DATA diskData = (PDISK_DATA) fdoExtension->CommonExtension.DriverData; PSENSE_DATA senseBuffer = Srb->SenseInfoBuffer; PIO_STACK_LOCATION irpStack; PIRP irp; PSCSI_REQUEST_BLOCK srb; LARGE_INTEGER largeInt; PCOMPLETION_CONTEXT context; PCDB cdb; ULONG_PTR alignment; ULONG majorFunction;
UNREFERENCED_PARAMETER(Status); UNREFERENCED_PARAMETER(Retry);
largeInt.QuadPart = 1;
//
// Check the status. The initialization command only needs to be sent
// if UNIT ATTENTION or LUN NOT READY is returned.
//
if (!(Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID)) {
//
// The drive does not require reinitialization.
//
return; }
//
// Reset the drive type.
//
diskData->DriveType = DRIVE_TYPE_NONE; diskData->IsDMF = FALSE;
fdoExtension->DiskGeometry.MediaType = Unknown;
if (fdoExtension->AdapterDescriptor->BusType == BusTypeUsb) {
// FLPYDISK.SYS never returns a non-zero value for the ChangeCount
// on an IOCTL_DISK_CHECK_VERIFY. Some things seem to work better
// if we do the same. In particular, FatVerifyVolume() can exit between
// the IOCTL_DISK_CHECK_VERIFY and the IOCTL_DISK_GET_DRIVE_GEOMETRY
// if a non-zero ChangeCount is returned, and this appears to cause
// issues formatting unformatted media in some situations.
//
// This is something that should probably be revisited at some point.
//
fdoExtension->MediaChangeCount = 0;
if (((senseBuffer->SenseKey & 0xf) == SCSI_SENSE_UNIT_ATTENTION) && (senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_MEDIUM_CHANGED)) {
struct _START_STOP *startStopCdb;
DebugPrint((2,"Sending SCSIOP_START_STOP_UNIT\n"));
context = ExAllocatePool(NonPagedPool, sizeof(COMPLETION_CONTEXT));
if (context == NULL) {
return; }
srb = &context->Srb;
RtlZeroMemory(srb, SCSI_REQUEST_BLOCK_SIZE);
srb->SrbFlags = SRB_FLAGS_DISABLE_AUTOSENSE;
srb->CdbLength = 6;
startStopCdb = (struct _START_STOP *)srb->Cdb;
startStopCdb->OperationCode = SCSIOP_START_STOP_UNIT; startStopCdb->Start = 1;
// A Start Stop Unit request has no transfer buffer.
// Set the request to IRP_MJ_FLUSH_BUFFERS when calling
// IoBuildAsynchronousFsdRequest() so that it ignores
// the buffer pointer and buffer length parameters.
//
majorFunction = IRP_MJ_FLUSH_BUFFERS;
} else if ((senseBuffer->SenseKey & 0xf) == SCSI_SENSE_MEDIUM_ERROR) {
// Return ERROR_UNRECOGNIZED_MEDIA instead of
// STATUS_DEVICE_DATA_ERROR to make shell happy.
//
*Status = STATUS_UNRECOGNIZED_MEDIA; return;
} else {
return; }
} else if (((senseBuffer->SenseKey & 0xf) == SCSI_SENSE_NOT_READY) && senseBuffer->AdditionalSenseCodeQualifier == SCSI_SENSEQ_INIT_COMMAND_REQUIRED || (senseBuffer->SenseKey & 0xf) == SCSI_SENSE_UNIT_ATTENTION) {
DebugPrint((1, "ScsiFlopProcessError: Reinitializing the floppy.\n"));
//
// Send the special mode sense command to enable writes on the
// floptical drive.
//
alignment = DeviceObject->AlignmentRequirement ? DeviceObject->AlignmentRequirement : 1;
context = ExAllocatePool( NonPagedPool, sizeof(COMPLETION_CONTEXT) + 0x2a + (ULONG)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; }
srb = &context->Srb; RtlZeroMemory(srb, SCSI_REQUEST_BLOCK_SIZE);
//
// Set the transfer length.
//
srb->DataTransferLength = 0x2a; srb->SrbFlags = SRB_FLAGS_DATA_IN | SRB_FLAGS_DISABLE_AUTOSENSE | SRB_FLAGS_DISABLE_SYNCH_TRANSFER;
//
// The data buffer must be aligned.
//
srb->DataBuffer = (PVOID) (((ULONG_PTR) (context + 1) + (alignment - 1)) & ~(alignment - 1));
//
// Build the start unit CDB.
//
srb->CdbLength = 6; cdb = (PCDB)srb->Cdb; cdb->MODE_SENSE.LogicalUnitNumber = srb->Lun; cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE; cdb->MODE_SENSE.PageCode = 0x2e; cdb->MODE_SENSE.AllocationLength = 0x2a;
majorFunction = IRP_MJ_READ;
} else {
return; }
context->DeviceObject = DeviceObject;
//
// Write length to SRB.
//
srb->Length = SCSI_REQUEST_BLOCK_SIZE;
srb->Function = SRB_FUNCTION_EXECUTE_SCSI; srb->TimeOutValue = fdoExtension->TimeOutValue;
//
// Build the asynchronous request
// to be sent to the port driver.
//
irp = IoBuildAsynchronousFsdRequest(majorFunction, DeviceObject, srb->DataBuffer, srb->DataTransferLength, &largeInt, NULL);
if(irp == NULL) { ExFreePool(context); return; }
IoSetCompletionRoutine(irp, (PIO_COMPLETION_ROUTINE)ClassAsynchronousCompletion, context, TRUE, TRUE, TRUE);
ClassAcquireRemoveLock(DeviceObject, irp);
irpStack = IoGetNextIrpStackLocation(irp);
irpStack->MajorFunction = IRP_MJ_SCSI;
srb->OriginalRequest = irp;
//
// Save SRB address in next stack for port driver.
//
irpStack->Parameters.Others.Argument1 = (PVOID)srb;
//
// Can't release the remove lock yet - let ClassAsynchronousCompletion
// take care of that for us.
//
(VOID)IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp);
return; }
NTSTATUS FlopticalFormatMedia( PDEVICE_OBJECT DeviceObject, PFORMAT_PARAMETERS Format ) /*++
Routine Description:
This routine is used to do perform a format tracks for the 20.8 MB floppy. Because the device does not support format tracks and the full format takes a long time a write of zeros is done instead.
Arguments:
DeviceObject - Supplies the device object to be tested.
Format - Supplies the format parameters.
Return Value:
Returns a status for the operation.
--*/ { IO_STATUS_BLOCK ioStatus; PIRP irp; KEVENT event; LARGE_INTEGER offset; ULONG length; PVOID buffer; PDRIVE_MEDIA_CONSTANTS driveMediaConstants; NTSTATUS status;
driveMediaConstants = &DriveMediaConstants[Drive2080Media2080];
//
// Calculate the length of the buffer.
//
length = ((Format->EndCylinderNumber - Format->StartCylinderNumber) * driveMediaConstants->NumberOfHeads + Format->EndHeadNumber - Format->StartHeadNumber + 1) * driveMediaConstants->SectorsPerTrack * driveMediaConstants->BytesPerSector;
buffer = ExAllocatePool(NonPagedPoolCacheAligned, length);
if (buffer == NULL) { return(STATUS_INSUFFICIENT_RESOURCES); }
RtlZeroMemory(buffer, length);
offset.QuadPart = (Format->StartCylinderNumber * driveMediaConstants->NumberOfHeads + Format->StartHeadNumber) * driveMediaConstants->SectorsPerTrack * driveMediaConstants->BytesPerSector;
//
// Set the event object to the unsignaled state.
// It will be used to signal request completion.
//
KeInitializeEvent(&event, NotificationEvent, FALSE);
//
// Build the synchronous request with data transfer.
//
irp = IoBuildSynchronousFsdRequest( IRP_MJ_WRITE, DeviceObject, buffer, length, &offset, &event, &ioStatus);
if (irp != NULL) { status = IoCallDriver(DeviceObject, irp);
if (status == STATUS_PENDING) {
//
// Wait for the request to complete if necessary.
//
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); }
//
// If the call driver suceeded then set the status to the status block.
//
if (NT_SUCCESS(status)) { status = ioStatus.Status; } } else { status = STATUS_INSUFFICIENT_RESOURCES; }
ExFreePool(buffer);
return(status);
}
#if 0
LONG SFlopStringCmp ( PCHAR FirstStr, PCHAR SecondStr, ULONG Count ) { UCHAR first ,last;
if (Count) { do {
//
// Get next char.
//
first = *FirstStr++; last = *SecondStr++;
if (first != last) {
//
// If no match, try lower-casing.
//
if (first>='A' && first<='Z') { first = first - 'A' + 'a'; } if (last>='A' && last<='Z') { last = last - 'A' + 'a'; } if (first != last) {
//
// No match
//
return first - last; } } }while (--Count && first); }
return 0; } #endif
NTSTATUS ScsiFlopRemoveDevice( IN PDEVICE_OBJECT DeviceObject, IN UCHAR Type ) /*++
Routine Description:
This routine is responsible for releasing any resources in use by the sfloppy driver. This routine is called when all outstanding requests have been completed and the driver has disappeared - no requests may be issued to the lower drivers.
Arguments:
DeviceObject - the device object being removed
Type - the type of remove operation (QUERY, REMOVE or CANCEL)
Return Value:
for a query - success if the device can be removed or a failure code indiciating why not.
for a remove or cancel - STATUS_SUCCESS
--*/
{ PFUNCTIONAL_DEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; PDISK_DATA diskData = deviceExtension->CommonExtension.DriverData;
PAGED_CODE();
if((Type == IRP_MN_QUERY_REMOVE_DEVICE) || (Type == IRP_MN_CANCEL_REMOVE_DEVICE)) { return STATUS_SUCCESS; }
if (Type == IRP_MN_REMOVE_DEVICE){ if(deviceExtension->DeviceDescriptor) { ExFreePool(deviceExtension->DeviceDescriptor); deviceExtension->DeviceDescriptor = NULL; }
if(deviceExtension->AdapterDescriptor) { ExFreePool(deviceExtension->AdapterDescriptor); deviceExtension->AdapterDescriptor = NULL; }
if(deviceExtension->SenseData) { ExFreePool(deviceExtension->SenseData); deviceExtension->SenseData = NULL; }
ClassDeleteSrbLookasideList(&deviceExtension->CommonExtension); } if(diskData->FloppyInterfaceString.Buffer != NULL) { IoSetDeviceInterfaceState( &(diskData->FloppyInterfaceString), FALSE); RtlFreeUnicodeString(&(diskData->FloppyInterfaceString)); RtlInitUnicodeString(&(diskData->FloppyInterfaceString), NULL); }
if(Type == IRP_MN_REMOVE_DEVICE) { IoGetConfigurationInformation()->FloppyCount--; }
return STATUS_SUCCESS; }
NTSTATUS ScsiFlopStopDevice( IN PDEVICE_OBJECT DeviceObject, IN UCHAR Type ) { return STATUS_SUCCESS; }
NTSTATUS USBFlopGetMediaTypes( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { /*++
Routine Description:
This routines determines the current or default geometry of the drive for IOCTL_DISK_GET_DRIVE_GEOMETRY, or all currently supported geometries of the drive (which is determined by its currently inserted media) for IOCTL_DISK_GET_MEDIA_TYPES.
The returned geometries are determined by issuing a Read Format Capacities request and then matching the returned {Number of Blocks, Block Length} pairs in a table of known floppy geometries.
Arguments:
DeviceObject - Supplies the device object.
Irp - A IOCTL_DISK_GET_DRIVE_GEOMETRY or a IOCTL_DISK_GET_MEDIA_TYPES Irp. If NULL, the device geometry is updated with the current device geometry.
Return Value:
Status is returned.
--*/ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension; PIO_STACK_LOCATION irpStack; ULONG ioControlCode; PDISK_GEOMETRY outputBuffer; PDISK_GEOMETRY outputBufferEnd; ULONG outputBufferLength; PSCSI_REQUEST_BLOCK srb; PVOID dataBuffer; ULONG dataTransferLength; struct _READ_FORMATTED_CAPACITIES *cdb; PFORMATTED_CAPACITY_LIST capList; NTSTATUS status;
fdoExtension = DeviceObject->DeviceExtension;
if (Irp != NULL) {
// Get the Irp parameters
//
irpStack = IoGetCurrentIrpStackLocation(Irp);
ioControlCode = irpStack->Parameters.DeviceIoControl.IoControlCode;
Irp->IoStatus.Information = 0;
outputBuffer = (PDISK_GEOMETRY) Irp->AssociatedIrp.SystemBuffer;
outputBufferLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
if (outputBufferLength < sizeof(DISK_GEOMETRY)) { return STATUS_BUFFER_TOO_SMALL; }
// Pointer arithmetic to allow multiple DISK_GEOMETRY's to be returned.
// Rounds BufferEnd down to integral multiple of DISK_GEOMETRY structs.
//
outputBufferEnd = outputBuffer + outputBufferLength / sizeof(DISK_GEOMETRY);
} else {
// No Irp to return the result in, just update the current geometry
// in the device extension.
//
ioControlCode = IOCTL_DISK_GET_DRIVE_GEOMETRY;
outputBuffer = NULL;
outputBufferEnd = NULL;
outputBufferLength = 0; }
if (ioControlCode == IOCTL_DISK_GET_DRIVE_GEOMETRY) {
fdoExtension->DiskGeometry.MediaType = Unknown;
status = ClassReadDriveCapacity(DeviceObject);
if (!NT_SUCCESS(status)) { // If the media is not recongized, we want to return the default
// geometry so that the media can be formatted. Unrecognized media
// causes SCSI_SENSE_MEDIUM_ERROR, which gets reported as
// STATUS_DEVICE_DATA_ERROR. Ignore these errors, but return other
// errors, such as STATUS_NO_MEDIA_IN_DEVICE.
//
if (status != STATUS_UNRECOGNIZED_MEDIA) { DebugPrint((2,"IOCTL_DISK_GET_DRIVE_GEOMETRY returns %08X\n", status));
return status; } } }
// Allocate an SRB for the SCSIOP_READ_FORMATTED_CAPACITY request
//
srb = ExAllocatePool(NonPagedPool, SCSI_REQUEST_BLOCK_SIZE);
if (srb == NULL) { return STATUS_INSUFFICIENT_RESOURCES; }
// Allocate a transfer buffer for the SCSIOP_READ_FORMATTED_CAPACITY request
// The length of the returned descriptor array is limited to a byte field
// in the capacity list header.
//
dataTransferLength = sizeof(FORMATTED_CAPACITY_LIST) + 31 * sizeof(FORMATTED_CAPACITY_DESCRIPTOR);
ASSERT(dataTransferLength < 0x100);
dataBuffer = ExAllocatePool(NonPagedPool, dataTransferLength);
if (dataBuffer == NULL) { ExFreePool(srb); return STATUS_INSUFFICIENT_RESOURCES; }
// Initialize the SRB and CDB
//
RtlZeroMemory(srb, SCSI_REQUEST_BLOCK_SIZE);
RtlZeroMemory(dataBuffer, dataTransferLength);
srb->CdbLength = sizeof(struct _READ_FORMATTED_CAPACITIES);
srb->TimeOutValue = fdoExtension->TimeOutValue;
cdb = (struct _READ_FORMATTED_CAPACITIES *)srb->Cdb;
cdb->OperationCode = SCSIOP_READ_FORMATTED_CAPACITY; cdb->AllocationLength[1] = (UCHAR)dataTransferLength;
//
// Send down the SCSIOP_READ_FORMATTED_CAPACITY request
//
status = ClassSendSrbSynchronous(DeviceObject, srb, dataBuffer, dataTransferLength, FALSE);
capList = (PFORMATTED_CAPACITY_LIST)dataBuffer;
// If we don't get as much data as requested, it is not an error.
//
if (SRB_STATUS(srb->SrbStatus) == SRB_STATUS_DATA_OVERRUN) { status = STATUS_SUCCESS; }
if (NT_SUCCESS(status) && srb->DataTransferLength >= sizeof(FORMATTED_CAPACITY_LIST) && capList->CapacityListLength && capList->CapacityListLength % sizeof(FORMATTED_CAPACITY_DESCRIPTOR) == 0) { ULONG NumberOfBlocks; ULONG BlockLength; ULONG count; ULONG i, j; LONG currentGeometry; BOOLEAN capacityMatches[FLOPPY_CAPACITIES];
// Subtract the size of the Capacity List Header to get
// just the size of the Capacity List Descriptor array.
//
srb->DataTransferLength -= sizeof(FORMATTED_CAPACITY_LIST);
// Only look at the Capacity List Descriptors that were actually
// returned.
//
if (srb->DataTransferLength < capList->CapacityListLength) { count = srb->DataTransferLength / sizeof(FORMATTED_CAPACITY_DESCRIPTOR); } else { count = capList->CapacityListLength / sizeof(FORMATTED_CAPACITY_DESCRIPTOR); }
// Updated only if a match is found for the first Capacity List
// Descriptor returned by the device.
//
currentGeometry = -1;
// Initialize the array of capacities that hit a match.
//
RtlZeroMemory(capacityMatches, sizeof(capacityMatches));
// Iterate over each Capacity List Descriptor returned from the device
// and record matching capacities in the capacity match array.
//
for (i = 0; i < count; i++) { NumberOfBlocks = (capList->Descriptors[i].NumberOfBlocks[0] << 24) + (capList->Descriptors[i].NumberOfBlocks[1] << 16) + (capList->Descriptors[i].NumberOfBlocks[2] << 8) + (capList->Descriptors[i].NumberOfBlocks[3]);
BlockLength = (capList->Descriptors[i].BlockLength[0] << 16) + (capList->Descriptors[i].BlockLength[1] << 8) + (capList->Descriptors[i].BlockLength[2]);
// Given the {NumberOfBlocks, BlockLength} from this Capacity List
// Descriptor, find a matching entry in FloppyCapacities[].
//
for (j = 0; j < FLOPPY_CAPACITIES; j++) { if (NumberOfBlocks == FloppyCapacities[j].NumberOfBlocks && BlockLength == FloppyCapacities[j].BlockLength) { // A matching capacity was found, record it.
//
capacityMatches[j] = TRUE;
// A match was found for the first Capacity List
// Descriptor returned by the device.
//
if (i == 0) { currentGeometry = j; } } else if ((capList->Descriptors[i].Valid) && (BlockLength == FloppyCapacities[j].BlockLength)) {
ULONG inx; ULONG mediaInx;
//
// Check if this is 32MB media type. 32MB media
// reports variable NumberOfBlocks. So we cannot
// use that to determine the drive type
//
inx = DetermineDriveType(DeviceObject); if (inx != DRIVE_TYPE_NONE) { mediaInx = DriveMediaLimits[inx].HighestDriveMediaType; if ((DriveMediaConstants[mediaInx].MediaType) == F3_32M_512) { capacityMatches[j] = TRUE;
if (i == 0) { currentGeometry = j; } } } } } }
// Default status is STATUS_UNRECOGNIZED_MEDIA, unless we return
// either STATUS_SUCCESS or STATUS_BUFFER_OVERFLOW.
//
status = STATUS_UNRECOGNIZED_MEDIA;
if (ioControlCode == IOCTL_DISK_GET_DRIVE_GEOMETRY) {
if (currentGeometry != -1) { // Update the current device geometry
//
fdoExtension->DiskGeometry = FloppyGeometries[currentGeometry];
//
// Calculate sector to byte shift.
//
WHICH_BIT(fdoExtension->DiskGeometry.BytesPerSector, fdoExtension->SectorShift);
fdoExtension->CommonExtension.PartitionLength.QuadPart = (LONGLONG)FloppyCapacities[currentGeometry].NumberOfBlocks * FloppyCapacities[currentGeometry].BlockLength;
DebugPrint((2,"geometry is: %3d %2d %d %2d %4d %2d %08X\n", fdoExtension->DiskGeometry.Cylinders.LowPart, fdoExtension->DiskGeometry.MediaType, fdoExtension->DiskGeometry.TracksPerCylinder, fdoExtension->DiskGeometry.SectorsPerTrack, fdoExtension->DiskGeometry.BytesPerSector, fdoExtension->SectorShift, fdoExtension->CommonExtension.PartitionLength.LowPart));
// Return the current device geometry
//
if (Irp != NULL) { *outputBuffer = FloppyGeometries[currentGeometry];
Irp->IoStatus.Information = sizeof(DISK_GEOMETRY); }
status = STATUS_SUCCESS; }
} else {
// Iterate over the capacities and return the geometry
// corresponding to each matching Capacity List Descriptor
// returned from the device.
//
// The resulting list should be in sorted ascending order,
// assuming that the FloppyGeometries[] array is in sorted
// ascending order.
//
for (i = 0; i < FLOPPY_CAPACITIES; i++) { if (capacityMatches[i] && FloppyCapacities[i].CanFormat) { if (outputBuffer < outputBufferEnd) { *outputBuffer++ = FloppyGeometries[i];
Irp->IoStatus.Information += sizeof(DISK_GEOMETRY);
DebugPrint((2,"geometry : %3d %2d %d %2d %4d\n", FloppyGeometries[i].Cylinders.LowPart, FloppyGeometries[i].MediaType, FloppyGeometries[i].TracksPerCylinder, FloppyGeometries[i].SectorsPerTrack, FloppyGeometries[i].BytesPerSector));
status = STATUS_SUCCESS; } else { // We ran out of output buffer room before we ran out
// geometries to return.
//
status = STATUS_BUFFER_OVERFLOW; } } } } } else if (NT_SUCCESS(status)) { // The SCSIOP_READ_FORMATTED_CAPACITY request was successful, but
// returned data does not appear valid.
//
status = STATUS_UNSUCCESSFUL; }
ExFreePool(dataBuffer); ExFreePool(srb);
return status; }
NTSTATUS USBFlopFormatTracks( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { /*++
Routine Description:
This routines formats the specified tracks. If multiple tracks are specified, each is formatted with a separate Format Unit request.
Arguments:
DeviceObject - Supplies the device object.
Irp - A IOCTL_DISK_FORMAT_TRACKS Irp.
Return Value:
Status is returned.
--*/ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension; PIO_STACK_LOCATION irpStack; PFORMAT_PARAMETERS formatParameters; PDISK_GEOMETRY geometry; PFORMATTED_CAPACITY capacity; PSCSI_REQUEST_BLOCK srb; PFORMAT_UNIT_PARAMETER_LIST parameterList; PCDB12FORMAT cdb; ULONG i; ULONG cylinder, head; NTSTATUS status;
fdoExtension = DeviceObject->DeviceExtension;
// Get the Irp parameters
//
irpStack = IoGetCurrentIrpStackLocation(Irp);
if (irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(FORMAT_PARAMETERS)) { return STATUS_INVALID_PARAMETER; }
formatParameters = (PFORMAT_PARAMETERS)Irp->AssociatedIrp.SystemBuffer;
// Find the geometry / capacity entries corresponding to the format
// parameters MediaType
//
geometry = NULL; capacity = NULL;
for (i=0; i<FLOPPY_CAPACITIES; i++) { if (FloppyGeometries[i].MediaType == formatParameters->MediaType) { geometry = &FloppyGeometries[i]; capacity = &FloppyCapacities[i];
break; } }
if (geometry == NULL) { return STATUS_INVALID_PARAMETER; }
// Check if the format parameters are valid
//
if ((formatParameters->StartCylinderNumber > geometry->Cylinders.LowPart - 1) ||
(formatParameters->EndCylinderNumber > geometry->Cylinders.LowPart - 1) ||
(formatParameters->StartHeadNumber > geometry->TracksPerCylinder - 1) ||
(formatParameters->EndHeadNumber > geometry->TracksPerCylinder - 1) ||
(formatParameters->StartCylinderNumber > formatParameters->EndCylinderNumber) ||
(formatParameters->StartHeadNumber > formatParameters->EndHeadNumber)) { return STATUS_INVALID_PARAMETER; }
// Don't low level format LS-120 media, Imation says it's best to not
// do this.
//
if ((formatParameters->MediaType == F3_120M_512) || (formatParameters->MediaType == F3_240M_512) || (formatParameters->MediaType == F3_32M_512)) { return STATUS_SUCCESS; }
// Allocate an SRB for the SCSIOP_FORMAT_UNIT request
//
srb = ExAllocatePool(NonPagedPool, SCSI_REQUEST_BLOCK_SIZE);
if (srb == NULL) { return STATUS_INSUFFICIENT_RESOURCES; }
// Allocate a transfer buffer for the SCSIOP_FORMAT_UNIT parameter list
//
parameterList = ExAllocatePool(NonPagedPool, sizeof(FORMAT_UNIT_PARAMETER_LIST));
if (parameterList == NULL) { ExFreePool(srb); return STATUS_INSUFFICIENT_RESOURCES; }
// Initialize the parameter list
//
RtlZeroMemory(parameterList, sizeof(FORMAT_UNIT_PARAMETER_LIST));
parameterList->DefectListHeader.SingleTrack = 1; parameterList->DefectListHeader.DisableCert = 1; // TEAC requires this set
parameterList->DefectListHeader.FormatOptionsValid = 1; parameterList->DefectListHeader.DefectListLengthLsb = 8;
parameterList->FormatDescriptor.NumberOfBlocks[0] = (UCHAR)((capacity->NumberOfBlocks >> 24) & 0xFF);
parameterList->FormatDescriptor.NumberOfBlocks[1] = (UCHAR)((capacity->NumberOfBlocks >> 16) & 0xFF);
parameterList->FormatDescriptor.NumberOfBlocks[2] = (UCHAR)((capacity->NumberOfBlocks >> 8) & 0xFF);
parameterList->FormatDescriptor.NumberOfBlocks[3] = (UCHAR)(capacity->NumberOfBlocks & 0xFF);
parameterList->FormatDescriptor.BlockLength[0] = (UCHAR)((capacity->BlockLength >> 16) & 0xFF);
parameterList->FormatDescriptor.BlockLength[1] = (UCHAR)((capacity->BlockLength >> 8) & 0xFF);
parameterList->FormatDescriptor.BlockLength[2] = (UCHAR)(capacity->BlockLength & 0xFF);
for (cylinder = formatParameters->StartCylinderNumber; cylinder <= formatParameters->EndCylinderNumber; cylinder++) { for (head = formatParameters->StartHeadNumber; head <= formatParameters->EndHeadNumber; head++) { // Initialize the SRB and CDB
//
RtlZeroMemory(srb, SCSI_REQUEST_BLOCK_SIZE);
srb->CdbLength = sizeof(CDB12FORMAT);
srb->TimeOutValue = fdoExtension->TimeOutValue;
cdb = (PCDB12FORMAT)srb->Cdb;
cdb->OperationCode = SCSIOP_FORMAT_UNIT; cdb->DefectListFormat = 7; cdb->FmtData = 1; cdb->TrackNumber = (UCHAR)cylinder; cdb->ParameterListLengthLsb = sizeof(FORMAT_UNIT_PARAMETER_LIST);
parameterList->DefectListHeader.Side = (UCHAR)head;
//
// Send down the SCSIOP_FORMAT_UNIT request
//
status = ClassSendSrbSynchronous(DeviceObject, srb, parameterList, sizeof(FORMAT_UNIT_PARAMETER_LIST), TRUE);
if (!NT_SUCCESS(status)) { break; } } if (!NT_SUCCESS(status)) { break; } }
if (NT_SUCCESS(status) && formatParameters->StartCylinderNumber == 0) { // Update the device geometry
//
DebugPrint((2,"geometry was: %3d %2d %d %2d %4d %2d %08X\n", fdoExtension->DiskGeometry.Cylinders.LowPart, fdoExtension->DiskGeometry.MediaType, fdoExtension->DiskGeometry.TracksPerCylinder, fdoExtension->DiskGeometry.SectorsPerTrack, fdoExtension->DiskGeometry.BytesPerSector, fdoExtension->SectorShift, fdoExtension->CommonExtension.PartitionLength.LowPart));
fdoExtension->DiskGeometry = *geometry;
//
// Calculate sector to byte shift.
//
WHICH_BIT(fdoExtension->DiskGeometry.BytesPerSector, fdoExtension->SectorShift);
fdoExtension->CommonExtension.PartitionLength.QuadPart = (LONGLONG)capacity->NumberOfBlocks * capacity->BlockLength;
DebugPrint((2,"geometry is: %3d %2d %d %2d %4d %2d %08X\n", fdoExtension->DiskGeometry.Cylinders.LowPart, fdoExtension->DiskGeometry.MediaType, fdoExtension->DiskGeometry.TracksPerCylinder, fdoExtension->DiskGeometry.SectorsPerTrack, fdoExtension->DiskGeometry.BytesPerSector, fdoExtension->SectorShift, fdoExtension->CommonExtension.PartitionLength.LowPart)); }
// Free everything we allocated
//
ExFreePool(parameterList); ExFreePool(srb);
return status; }
|