[FLOPPY_NEW] Import the floppy driver from MS GitHub repository
authorPierre Schweitzer <pierre@reactos.org>
Sat, 15 Dec 2018 19:02:03 +0000 (20:02 +0100)
committerPierre Schweitzer <pierre@reactos.org>
Sat, 15 Dec 2018 19:04:00 +0000 (20:04 +0100)
Do not enable it yet, as it doesn't work in ROS for the moment :-(.
Its place in tree is not optimal (it should be with disk/class/etc.),
but I prefer keeping it close to actual driver for now.

All the work has been done so that it compiles and links with ReactOS
SDK though.

drivers/storage/floppy_new/CMakeLists.txt [new file with mode: 0644]
drivers/storage/floppy_new/floppy.c [new file with mode: 0644]
drivers/storage/floppy_new/floppy.rc [new file with mode: 0644]
drivers/storage/floppy_new/floppy_reg.inf [new file with mode: 0644]
drivers/storage/floppy_new/guid.c [new file with mode: 0644]
media/doc/README.FSD

diff --git a/drivers/storage/floppy_new/CMakeLists.txt b/drivers/storage/floppy_new/CMakeLists.txt
new file mode 100644 (file)
index 0000000..154e553
--- /dev/null
@@ -0,0 +1,9 @@
+list(APPEND SOURCE
+    guid.c
+    floppy.c)
+
+add_library(floppy SHARED ${SOURCE} floppy.rc)
+set_module_type(floppy kernelmodedriver)
+add_importlibs(floppy classpnp hal ntoskrnl)
+add_cd_file(TARGET floppy DESTINATION reactos/system32/drivers NO_CAB FOR all)
+add_registry_inf(floppy_reg.inf)
diff --git a/drivers/storage/floppy_new/floppy.c b/drivers/storage/floppy_new/floppy.c
new file mode 100644 (file)
index 0000000..3c9a1eb
--- /dev/null
@@ -0,0 +1,3574 @@
+/*++
+
+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)
+
+--*/
+
+#ifdef _MSC_VER
+#pragma warning(disable:4214) // nonstandard extension used : bit field types other than int
+#pragma warning(disable:4201) // nonstandard extension used : nameless struct/union
+#endif
+
+#include <stddef.h>
+#include <ntddk.h>
+#ifndef __REACTOS__
+#include <winerror.h>
+#endif
+#include <scsi.h>
+#include <classpnp.h>
+#include <initguid.h>
+#include <ntddstor.h>
+
+#include <ntstrsafe.h>
+#include <intsafe.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
+#ifndef __REACTOS__
+    {{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}
+#else
+    {{.LowPart = 80, .HighPart = 0},    F3_640_512,     2,      8,      512},
+    {{.LowPart = 80, .HighPart = 0},    F3_720_512,     2,      9,      512},
+    {{.LowPart = 80, .HighPart = 0},    F3_1Pt2_512,    2,      15,     512},
+    {{.LowPart = 77, .HighPart = 0},    F3_1Pt23_1024,  2,      8,      1024},
+    {{.LowPart = 80, .HighPart = 0},    F3_1Pt44_512,   2,      18,     512},
+    {{.LowPart = 80, .HighPart = 0},    F3_1Pt44_512,   2,      21,     512},   // DMF -> F3_1Pt44_512
+    {{.LowPart = 1024, .HighPart = 0},  F3_32M_512,     2,      32,     512},
+    {{.LowPart = 963, .HighPart = 0},   F3_120M_512,    8,      32,     512},
+    {{.LowPart = 890, .HighPart = 0},   F3_200Mb_512,   13,     34,     512},
+    {{.LowPart = 262, .HighPart = 0},   F3_240M_512,    32,     56,     512}
+#endif
+};
+
+#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)
+
+DRIVER_INITIALIZE DriverEntry;
+
+DRIVER_UNLOAD     ScsiFlopUnload;
+
+DRIVER_ADD_DEVICE ScsiFlopAddDevice;
+
+NTSTATUS
+#ifdef __REACTOS__
+NTAPI
+#endif
+ScsiFlopInitDevice(
+    IN PDEVICE_OBJECT Fdo
+    );
+
+NTSTATUS
+#ifdef __REACTOS__
+NTAPI
+#endif
+ScsiFlopStartDevice(
+    IN PDEVICE_OBJECT Fdo
+    );
+
+NTSTATUS
+#ifdef __REACTOS__
+NTAPI
+#endif
+ScsiFlopRemoveDevice(
+    IN PDEVICE_OBJECT Fdo,
+    IN UCHAR Type
+    );
+
+NTSTATUS
+#ifdef __REACTOS__
+NTAPI
+#endif
+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
+#ifdef __REACTOS__
+NTAPI
+#endif
+ScsiFlopReadWriteVerification(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PIRP Irp
+    );
+
+NTSTATUS
+#ifdef __REACTOS__
+NTAPI
+#endif
+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
+#ifdef __REACTOS__
+NTAPI
+#endif
+ScsiFlopProcessError(
+    PDEVICE_OBJECT DeviceObject,
+    PSCSI_REQUEST_BLOCK Srb,
+    NTSTATUS *Status,
+    BOOLEAN *Retry
+    );
+
+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, ScsiFlopRemoveDevice)
+#pragma alloc_text(PAGE, IsFloppyDevice)
+#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
+
+\f
+NTSTATUS
+#ifdef __REACTOS__
+NTAPI
+#endif
+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()
+\f
+VOID
+#ifdef __REACTOS__
+NTAPI
+#endif
+ScsiFlopUnload(
+    IN PDRIVER_OBJECT DriverObject
+    )
+{
+    PAGED_CODE();
+    UNREFERENCED_PARAMETER(DriverObject);
+    return;
+}
+
+//
+// AddDevice operation is performed in CreateFlopDeviceObject function which
+// is called by ScsiFlopAddDevice (The AddDevice routine for sfloppy.sys).
+// DO_DEVICE_INITIALIZING flag is cleard upon successfully processing AddDevice
+// operation in CreateFlopDeviceObject. But PREFAST is currently unable to
+// detect that DO_DEVICE_INITIALIZING is indeed cleard in CreateFlopDeviceObject
+// and it raises Warning 28152 (The return from an AddDevice-like function
+// unexpectedly did not clear DO_DEVICE_INITIALIZING). Suppress that warning
+// using #pragma.
+//
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable:28152)
+#endif
+\f
+NTSTATUS
+#ifdef __REACTOS__
+NTAPI
+#endif
+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
+
+--*/
+{
+    NTSTATUS status;
+    ULONG floppyCount = IoGetConfigurationInformation()->FloppyCount;
+
+    PAGED_CODE();
+
+    //
+    // 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 = NULL;
+    PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = NULL;
+    PDISK_DATA  diskData;
+
+    PAGED_CODE();
+
+    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++;
+
+        status = RtlStringCbPrintfA((PCCHAR) name,
+                                    sizeof(name)/sizeof(UCHAR),
+                                    "\\Device\\Floppy%u",
+                                    DeviceCount);
+        if (NT_SUCCESS(status)) {
+
+            status = ClassCreateDeviceObject(DriverObject,
+                                             (PCCHAR) 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()
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+NTSTATUS
+#ifdef __REACTOS__
+NTAPI
+#endif
+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;
+
+    NTSTATUS status = STATUS_SUCCESS;
+
+    //
+    // Allocate request sense buffer.
+    //
+
+#ifndef __REACTOS__
+    senseData = ExAllocatePool(NonPagedPoolNxCacheAligned, SENSE_BUFFER_SIZE);
+#else
+    senseData = ExAllocatePool(NonPagedPoolCacheAligned, SENSE_BUFFER_SIZE);
+#endif
+
+    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);
+
+    //
+    // Register for media change notification
+    //
+    ClassInitializeMediaChangeDetection(fdoExtension,
+                                        (PUCHAR) "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;
+
+    //
+    // Register interfaces for this device.
+    //
+
+    {
+        UNICODE_STRING interfaceName;
+
+        RtlInitUnicodeString(&interfaceName, NULL);
+
+        status = IoRegisterDeviceInterface(fdoExtension->LowerPdo,
+                                           (LPGUID) &GUID_DEVINTERFACE_FLOPPY,
+                                           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);
+}
+
+#ifdef _MSC_VER
+#pragma warning(suppress:6262) // This function uses 1096 bytes of stack which exceed default value of 1024 bytes used by Code Analysis for flagging as warning
+#endif
+#ifdef __REACTOS__
+NTSTATUS NTAPI ScsiFlopStartDevice(
+#else
+NTSTATUS ScsiFlopStartDevice(
+#endif
+    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;
+
+    PAGED_CODE();
+
+    KeInitializeEvent(&event,SynchronizationEvent,FALSE);
+
+    DetermineMediaType(Fdo); // ignore unsuccessful here
+
+    //
+    // Create device object for this device.
+    //
+
+    RtlStringCbPrintfW(ntNameBuffer,
+                       sizeof(ntNameBuffer)/sizeof(WCHAR),
+                       L"\\Device\\Floppy%u",
+                       fdoExtension->DeviceNumber);
+
+    //
+    // Create local copy of unicode string
+    //
+    RtlInitUnicodeString(&ntUnicodeString,ntNameBuffer);
+
+    //
+    // 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;
+    }
+
+    //
+    // IOCTL_SCSI_GET_ADDRESS might not be supported by the port driver and
+    // hence may fail. But it is not a fatal error. Do not fail PnP start
+    // if this IOCTL fails.
+    //
+    if (NT_SUCCESS(status)) {
+
+        RtlStringCbPrintfW(arcNameBuffer,
+                           sizeof(arcNameBuffer)/sizeof(WCHAR),
+                           L"\\ArcName\\scsi(%u)disk(%u)fdisk(%u)",
+                           scsiAddress.PortNumber,
+                           scsiAddress.TargetId,
+                           scsiAddress.Lun);
+
+        RtlInitUnicodeString(&arcUnicodeString, arcNameBuffer);
+
+        IoAssignArcName(&arcUnicodeString, &ntUnicodeString);
+    }
+
+    status = STATUS_SUCCESS;
+
+    //
+    // 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.
+    //
+
+    RtlStringCbPrintfW(arcNameBuffer,
+                       sizeof(arcNameBuffer)/sizeof(WCHAR),
+                       L"\\ArcName\\multi(%u)disk(%u)fdisk(%u)",
+                       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(!NT_SUCCESS(status)) {
+                DebugPrint((1, "ScsiFlopStartDevice: Unable to set device "
+                               "interface state to TRUE for fdo %p "
+                               "[%08lx]\n",
+                            Fdo, status));
+            }
+        }
+    }
+
+    return STATUS_SUCCESS;
+}
+
+\f
+NTSTATUS
+#ifdef __REACTOS__
+NTAPI
+#endif
+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
+#ifdef __REACTOS__
+NTAPI
+#endif
+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;
+
+#ifndef __REACTOS__
+    srb = ExAllocatePool(NonPagedPoolNx, SCSI_REQUEST_BLOCK_SIZE);
+#else
+    srb = ExAllocatePool(NonPagedPool, SCSI_REQUEST_BLOCK_SIZE);
+#endif
+
+    if (srb == NULL) {
+
+        Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
+        if (IoIsErrorUserInduced(Irp->IoStatus.Status)) {
+
+            IoSetHardErrorOrVerifyDevice(Irp, DeviceObject);
+        }
+
+        KeRaiseIrql(DISPATCH_LEVEL, &currentIrql);
+        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)&sectorOffset)->Byte3;
+       cdb->CDB10.LogicalBlockByte1 = ((PFOUR_BYTE)&sectorOffset)->Byte2;
+       cdb->CDB10.LogicalBlockByte2 = ((PFOUR_BYTE)&sectorOffset)->Byte1;
+       cdb->CDB10.LogicalBlockByte3 = ((PFOUR_BYTE)&sectorOffset)->Byte0;
+
+       cdb->CDB10.TransferBlocksMsb = ((PFOUR_BYTE)&sectorCount)->Byte1;
+       cdb->CDB10.TransferBlocksLsb = ((PFOUR_BYTE)&sectorCount)->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) {
+
+            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.
+        //
+
+#ifndef __REACTOS__
+        modeData = ExAllocatePool(NonPagedPoolNxCacheAligned, MODE_DATA_SIZE);
+#else
+        modeData = ExAllocatePool(NonPagedPoolCacheAligned, MODE_DATA_SIZE);
+#endif
+
+        if (modeData == NULL) {
+            status = STATUS_INSUFFICIENT_RESOURCES;
+            break;
+        }
+
+        RtlZeroMemory(modeData, MODE_DATA_SIZE);
+
+        length = ClassModeSense(DeviceObject,
+                                (PCHAR) 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,
+                                    (PCHAR) 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, &currentIrql);
+    ClassReleaseRemoveLock(DeviceObject, Irp);
+    ClassCompleteRequest(DeviceObject, Irp, 0);
+    KeLowerIrql(currentIrql);
+
+    ExFreePool(srb);
+
+    return status;
+
+} // end ScsiFlopDeviceControl()
+
+#if 0
+\f
+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(NonPagedPoolNxCacheAligned, 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
+
+\f
+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;
+
+    PAGED_CODE();
+
+    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.
+            //
+
+#ifndef __REACTOS__
+            readData = ExAllocatePool(NonPagedPoolNx, geometry->BytesPerSector);
+#else
+            readData = ExAllocatePool(NonPagedPool, geometry->BytesPerSector);
+#endif
+            if (readData == NULL) {
+                return STATUS_NO_MEMORY;
+            }
+
+#ifndef __REACTOS__
+            srb = ExAllocatePool(NonPagedPoolNx, SCSI_REQUEST_BLOCK_SIZE);
+#else
+            srb = ExAllocatePool(NonPagedPool, SCSI_REQUEST_BLOCK_SIZE);
+#endif
+
+            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;
+}
+\f
+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;
+
+    PAGED_CODE();
+
+    if (diskData->DriveType != DRIVE_TYPE_NONE) {
+        return(diskData->DriveType);
+    }
+
+#ifndef __REACTOS__
+    modeData = ExAllocatePool(NonPagedPoolNxCacheAligned, MODE_DATA_SIZE);
+#else
+    modeData = ExAllocatePool(NonPagedPoolCacheAligned, MODE_DATA_SIZE);
+#endif
+
+    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 >=
+         (UCHAR)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) {
+           maximumTrack++;
+           maximumTrack /= 3;
+           maximumTrack--;
+           numberOfHeads *= 3;
+       } else {
+           ExFreePool(modeData);
+           return(DRIVE_TYPE_NONE);
+       }
+
+    }
+
+    ExFreePool(modeData);
+    return(DRIVE_TYPE_NONE);
+}
+
+\f
+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;
+
+    PAGED_CODE();
+
+    //
+    // 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--;
+        }
+
+    }
+
+
+     // driveMediaType is bounded below by DriveMediaLimits[].LowestDriveMediaType
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable:33010) // 33010: Enum used as array index may be negative
+#endif
+    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;
+        }
+    }
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+}
+\f
+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;
+
+    PAGED_CODE();
+
+#ifndef __REACTOS__
+    modeData = ExAllocatePool(NonPagedPoolNxCacheAligned, MODE_DATA_SIZE);
+#else
+    modeData = ExAllocatePool(NonPagedPoolCacheAligned, MODE_DATA_SIZE);
+#endif
+
+    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 <
+         (UCHAR)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.
+        //
+
+#ifndef __REACTOS__
+        srb = ExAllocatePool(NonPagedPoolNx, SCSI_REQUEST_BLOCK_SIZE);
+#else
+        srb = ExAllocatePool(NonPagedPool, SCSI_REQUEST_BLOCK_SIZE);
+#endif
+
+        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.
+        //
+
+#ifndef __REACTOS__
+        srb = ExAllocatePool(NonPagedPoolNx, SCSI_REQUEST_BLOCK_SIZE);
+#else
+        srb = ExAllocatePool(NonPagedPool, SCSI_REQUEST_BLOCK_SIZE);
+#endif
+
+        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);
+
+}
+\f
+VOID
+#ifdef __REACTOS__
+NTAPI
+#endif
+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"));
+
+#ifndef __REACTOS__
+            context = ExAllocatePool(NonPagedPoolNx,
+#else
+            context = ExAllocatePool(NonPagedPool,
+#endif
+                                     sizeof(COMPLETION_CONTEXT));
+
+            if (context == NULL) {
+
+                return;
+            }
+#if (NTDDI_VERSION >= NTDDI_WIN8)
+            srb = &context->Srb.Srb;
+#else
+            srb = &context->Srb;
+#endif
+
+            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;
+        }
+
+#ifndef __REACTOS__
+    } else if (((senseBuffer->SenseKey & 0xf) == SCSI_SENSE_NOT_READY) &&
+             senseBuffer->AdditionalSenseCodeQualifier == SCSI_SENSEQ_INIT_COMMAND_REQUIRED ||
+             (senseBuffer->SenseKey & 0xf) == SCSI_SENSE_UNIT_ATTENTION) {
+#else
+    } else if ((((senseBuffer->SenseKey & 0xf) == SCSI_SENSE_NOT_READY) &&
+             senseBuffer->AdditionalSenseCodeQualifier == SCSI_SENSEQ_INIT_COMMAND_REQUIRED) ||
+             (senseBuffer->SenseKey & 0xf) == SCSI_SENSE_UNIT_ATTENTION) {
+#endif
+
+        ULONG sizeNeeded;
+        ULONG tmpSize;
+        BOOLEAN overFlow;
+
+        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;
+
+        sizeNeeded = 0;
+        overFlow = TRUE;
+        if (SUCCEEDED(ULongAdd(sizeof(COMPLETION_CONTEXT), 0x2a, &tmpSize))) {
+
+            if (SUCCEEDED(ULongAdd(tmpSize, (ULONG) alignment, &sizeNeeded))) {
+                overFlow = FALSE;
+            }
+        }
+
+        context = NULL;
+
+        if (!overFlow) {
+#ifndef __REACTOS__
+            context = ExAllocatePool(NonPagedPoolNx, sizeNeeded);
+#else
+            context = ExAllocatePool(NonPagedPool, sizeNeeded);
+#endif
+        }
+
+        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;
+        }
+
+#if (NTDDI_VERSION >= NTDDI_WIN8)
+        srb = &context->Srb.Srb;
+#else
+        srb = &context->Srb;
+#endif
+
+        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.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;
+}
+\f
+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;
+
+    PAGED_CODE();
+
+    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;
+
+#ifndef __REACTOS__
+    buffer = ExAllocatePool(NonPagedPoolNxCacheAligned, length);
+#else
+    buffer = ExAllocatePool(NonPagedPoolCacheAligned, length);
+#endif
+
+    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);
+
+}
+
+\f
+NTSTATUS
+#ifdef __REACTOS__
+NTAPI
+#endif
+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;
+    NTSTATUS status;
+
+    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) {
+        
+        status = IoSetDeviceInterfaceState(
+                   &(diskData->FloppyInterfaceString),
+                   FALSE);
+
+        if (!NT_SUCCESS(status)) {
+            // Failed to disable device interface during removal. Not a fatal error.
+            DebugPrint((1, "ScsiFlopRemoveDevice: Unable to set device "
+                           "interface state to FALSE for fdo %p "
+                           "[%08lx]\n",
+                        DeviceObject, status));
+        }
+
+        RtlFreeUnicodeString(&(diskData->FloppyInterfaceString));
+        RtlInitUnicodeString(&(diskData->FloppyInterfaceString), NULL);
+    }
+
+    if(Type == IRP_MN_REMOVE_DEVICE) {
+        IoGetConfigurationInformation()->FloppyCount--;
+    }
+
+    return STATUS_SUCCESS;
+}
+
+\f
+NTSTATUS
+#ifdef __REACTOS__
+NTAPI
+#endif
+ScsiFlopStopDevice(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN UCHAR Type
+    )
+{
+    UNREFERENCED_PARAMETER(DeviceObject);
+    UNREFERENCED_PARAMETER(Type);
+
+    return STATUS_SUCCESS;
+}
+
+\f
+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;
+
+    PAGED_CODE();
+
+    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
+    //
+#ifndef __REACTOS__
+    srb = ExAllocatePool(NonPagedPoolNx, SCSI_REQUEST_BLOCK_SIZE);
+#else
+    srb = ExAllocatePool(NonPagedPool, SCSI_REQUEST_BLOCK_SIZE);
+#endif
+
+    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);
+
+#ifndef __REACTOS__
+    dataBuffer = ExAllocatePool(NonPagedPoolNx, dataTransferLength);
+#else
+    dataBuffer = ExAllocatePool(NonPagedPool, dataTransferLength);
+#endif
+
+    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;
+}
+
+\f
+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 = STATUS_SUCCESS;
+
+    PAGED_CODE();
+
+    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
+    //
+#ifndef __REACTOS__
+    srb = ExAllocatePool(NonPagedPoolNx, SCSI_REQUEST_BLOCK_SIZE);
+#else
+    srb = ExAllocatePool(NonPagedPool, SCSI_REQUEST_BLOCK_SIZE);
+#endif
+
+    if (srb == NULL)
+    {
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    // Allocate a transfer buffer for the SCSIOP_FORMAT_UNIT parameter list
+    //
+#ifndef __REACTOS__
+    parameterList = ExAllocatePool(NonPagedPoolNx,
+#else
+    parameterList = ExAllocatePool(NonPagedPool,
+#endif
+                                   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;
+}
+
diff --git a/drivers/storage/floppy_new/floppy.rc b/drivers/storage/floppy_new/floppy.rc
new file mode 100644 (file)
index 0000000..d23ec11
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+ * PROJECT:         ReactOS Floppy Driver
+ * LICENSE:         GPL - See COPYING in the top level directory
+ * FILE:            drivers/storage/floppy/floppy.rc
+ * PURPOSE:         Resource definition file
+ * PROGRAMMERS:     Vizzini (vizzini@plasmic.com)
+ */
+
+#define REACTOS_VERSION_DLL
+#define REACTOS_STR_FILE_DESCRIPTION  "Floppy Disk Controller Driver"
+#define REACTOS_STR_INTERNAL_NAME     "floppy"
+#define REACTOS_STR_ORIGINAL_FILENAME "floppy.sys"
+#include <reactos/version.rc>
diff --git a/drivers/storage/floppy_new/floppy_reg.inf b/drivers/storage/floppy_new/floppy_reg.inf
new file mode 100644 (file)
index 0000000..ad620f4
--- /dev/null
@@ -0,0 +1,7 @@
+; Floppy driver
+[AddReg]
+HKLM,"SYSTEM\CurrentControlSet\Services\Floppy","ErrorControl",0x00010001,0x00000000
+HKLM,"SYSTEM\CurrentControlSet\Services\Floppy","Group",0x00000000,"Primary Disk"
+HKLM,"SYSTEM\CurrentControlSet\Services\Floppy","ImagePath",0x00020000,"system32\drivers\floppy.sys"
+HKLM,"SYSTEM\CurrentControlSet\Services\Floppy","Start",0x00010001,0x00000001
+HKLM,"SYSTEM\CurrentControlSet\Services\Floppy","Type",0x00010001,0x00000001
diff --git a/drivers/storage/floppy_new/guid.c b/drivers/storage/floppy_new/guid.c
new file mode 100644 (file)
index 0000000..757901b
--- /dev/null
@@ -0,0 +1,11 @@
+/* DO NOT USE THE PRECOMPILED HEADER FOR THIS FILE! */
+
+#include <ntdef.h>
+#include <initguid.h>
+#include <ioevent.h>
+#include <wdmguid.h>
+
+#define DEVICE_TYPE ULONG
+#include <ntddstor.h>
+
+/* NO CODE HERE, THIS IS JUST REQUIRED FOR THE GUID DEFINITIONS */
index 8d9993d..4f28521 100644 (file)
@@ -19,7 +19,8 @@ reactos/drivers/filesystems/reiserfs        # Synced to 0.26
 The following FSD are shared with: https://github.com/Microsoft/Windows-driver-samples
 
 reactos/drivers/filesystems/fastfat_new     # Synced to 2817004
-reactos/drivers/filesystems/cdfs_new        # Synced to f73e552
+reactos/drivers/filesystems/cdfs            # Synced to f73e552
+reactos/drivers/storage/floppy_new         # Synced to 9d23d43
 
 The following FS libs are shared with: https://github.com/dosfstools/dosfstools
 reactos/sdk/lib/fslib/vfatlib/check         # Synced to 4.1