summaryrefslogtreecommitdiffstats
path: root/private/mvdm/softpc.new/host/src/nt_rflop.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--private/mvdm/softpc.new/host/src/nt_rflop.c2036
1 files changed, 2036 insertions, 0 deletions
diff --git a/private/mvdm/softpc.new/host/src/nt_rflop.c b/private/mvdm/softpc.new/host/src/nt_rflop.c
new file mode 100644
index 000000000..ec6444f5f
--- /dev/null
+++ b/private/mvdm/softpc.new/host/src/nt_rflop.c
@@ -0,0 +1,2036 @@
+/*
+ * Name: nt_flop.c
+ * Derived From: DEC begat M88K begat NeXT finally begat Generic.
+ * Author: Jason Proctor
+ * Created On: Nov 8 1990
+ * Sccs ID: 10/13/92 @(#)nt_flop.c 1.9
+ * Purpose: nt real floppy server.
+ *
+ * (c)Copyright Insignia Solutions Ltd., 1990. All rights reserved.
+ *
+ * Notes:
+ * Updated for 3.0 base by Jerry Richemont.
+ * Further updated by Ian Reid to support two floppy
+ * drives. Support is compile time dependant on the
+ * standard SoftPC defines.
+ *
+ * This implementation requires that you provide a
+ * host_rflop_drive_type() function which knows what kind of drive(s)
+ * your machine has; ie returns GFI_DRIVE_TYPE_xxxx.
+ */
+
+/********************************************************/
+
+/* INCLUDES */
+
+#include "nt.h"
+#include "ntrtl.h"
+#include "nturtl.h"
+#include "ntdddisk.h"
+#include "windows.h"
+
+#include "insignia.h"
+#include "host_def.h"
+
+
+#include <stdio.h>
+#include <errno.h>
+#include <sys\types.h>
+
+#include "xt.h"
+#include CpuH
+#include "trace.h"
+#include "error.h"
+#include "fla.h"
+#include "dma.h"
+#include "config.h"
+#include "debug.h"
+#include "lock.h"
+#include "timer.h"
+#include "floppy.h"
+#include "cmos.h"
+#include "gfi.h"
+
+#include "nt_uis.h"
+#include "nt_reset.h"
+#include "nt_fdisk.h"
+/********************************************************/
+
+/* DEFINES */
+
+#ifdef min
+#undef min
+#endif
+#define min(a,b) (a > b ? b : a)
+
+#define PC_MAX_DRIVE_TYPES 2
+#define PC_MAX_DENSITY_TYPES 2
+#define PC_MAX_FLOPPY_TYPES (PC_MAX_DRIVE_TYPES * PC_MAX_DENSITY_TYPES)
+
+#define PC_HEADS_PER_DISKETTE 2
+#define PC_N_VALUE 2
+#define PC_BYTES_PER_SECTOR 512
+
+/* disk buffer size, in bytes */
+// KEEP SYNC WITH MAX_DISKIO_SIZE defined in nt_fdisk.c
+#define BS_DISK_BUFFER_SIZE 0x9000
+
+/* double stepping factor */
+#define DOUBLE_STEP_FACTOR 1
+
+/* density types */
+#define DENSITY_LOW 0
+#define DENSITY_HIGH 1
+#define DENSITY_EXTENDED 2
+#define DENSITY_UNKNOWN 100
+/* motor states */
+#define MOTOR_OFF 0
+#define MOTOR_ON 1
+
+#ifndef PROD
+#define BREAK_ON_AND 0x01
+#define BREAK_ON_OR 0x02
+#define BREAK_ON_XOR 0x03
+
+UTINY break_cylinder = 0xff;
+UTINY break_head = 0xff;
+UTINY break_sector = 0xff;
+#endif
+
+#ifndef PROD
+DWORD rflop_dbg = 0;
+
+#define RFLOP_READ 0x01
+#define RFLOP_WRITE 0x02
+#define RFLOP_FORMAT 0x04
+#define RFLOP_SEEK 0x08
+#define RFLOP_READID 0x10
+#define RFLOP_RESET 0x20
+#define RFLOP_SPECIFY 0x40
+#define RFLOP_READTRACK 0x80
+#define RFLOP_RECAL 0x100
+#define RFLOP_SENSEDRV 0x200
+#define RFLOP_RATE 0x1000
+#define RFLOP_CHANGE 0x2000
+#define RFLOP_DRIVE_ON 0x4000
+#define RFLOP_DRIVE_OFF 0x8000
+#define RFLOP_OPEN 0x10000
+#define RFLOP_CLOSE 0x20000
+#define RFLOP_GUESS_MEDIA 0x40000
+
+#define RFLOP_BREAK 0x80000000
+
+#endif
+
+/********************************************************/
+
+/* TYPEDEFS */
+
+struct flop_struct
+{
+ int trks_per_disk;
+ int secs_per_trk;
+};
+
+/*
+ * This structure contains all the drive specific information. That is
+ * status which is unique to each drive, and must therefore be maintained
+ * on a per drive basis.
+ */
+typedef struct floppy_info
+{
+ HANDLE diskette_fd;
+
+/*
+ * drive_type - the highest density format which this drive supports,
+ * e.g. GFI_DRIVE_TYPE_144, GFI_DRIVE_TYPE_288
+ * flop_type - the basic drive type, expressed as the lowest density
+ * possible for this format. For 5.25" disks this is
+ * GFI_DRIVE_TYPE_360, for 3.5" it is GFI_DRIVE_TYPE_720.
+ */
+ USHORT drive_type;
+ USHORT flop_type;
+ USHORT last_seek;
+ USHORT last_head_seek;
+/*
+ * Change line state.
+ * This is a heuristic to try to fake up the correct change line behaviour
+ * without having a change line. The state of the change line is returned
+ * as CHANGED unless the diskette motor has been continuously ON since the
+ * last reset.
+ */
+ BOOLEAN change_line_state;
+ BOOLEAN auto_locked;
+ USHORT owner_pdb;
+ SHORT motor_state;
+ SHORT media_density;
+ USHORT max_track;
+
+ USHORT secs_per_trk;
+ USHORT trks_per_disk;
+ DWORD align_factor;
+ UTINY idle_counter;
+ UTINY C;
+ UTINY H;
+ UTINY R;
+ UTINY N;
+ char device_name[MAX_PATH]; /* device name */
+} FL, *FLP;
+
+#define FLOPPY_IDLE_PERIOD 0xFF
+
+
+/* parameter passed from main thread to FDC thread */
+typedef struct _FDC_PARMS{
+FDC_CMD_BLOCK * command_block;
+FDC_RESULT_BLOCK * result_block;
+USHORT owner_pdb;
+BOOLEAN auto_lock;
+
+} FDC_PARMS, *PFDC_PARMS;
+
+
+/********************************************************/
+
+
+/* routines used internally */
+
+/* routines called via vector table: all the prototypes are in gfi.h
+** so everybody matches. If you want to use this file to base a
+** host floppy module on, you know that all the functions that must
+** be declared properly for gfi to work will be.
+** GM.
+ */
+ULONG nt_floppy_read (UTINY drive, ULONG Offset, ULONG Size, PBYTE Buffer);
+ULONG nt_floppy_write (UTINY drive, ULONG Offset, ULONG Size, PBYTE Buffer);
+BOOL nt_floppy_verify (UTINY drive, ULONG Offset, ULONG Size);
+MEDIA_TYPE nt_floppy_get_media_type(BYTE drive, WORD cylinders, WORD sectors, WORD heads);
+BOOL nt_floppy_format (UTINY drive, WORD Cylinder, WORD Head, MEDIA_TYPE media);
+BOOL nt_floppy_close (UTINY drive);
+BOOL nt_floppy_media_check (UTINY drive);
+BOOL dismount_drive(FLP flp);
+
+
+#ifndef PROD
+VOID nt_rflop_break(VOID);
+#endif
+
+void fdc_command_completed (BYTE drive, BYTE fdc_command);
+void fdc_thread (PFDC_PARMS fdc_parms);
+
+BOOL nt_gfi_rdiskette_init IPT1( UTINY, drive );
+VOID nt_gfi_rdiskette_term IPT1( FLP, flp );
+SHORT nt_rflop_drive_on IPT1( UTINY, drive );
+SHORT nt_rflop_drive_off IPT1( UTINY, drive );
+SHORT nt_rflop_change IPT1( UTINY, drive );
+SHORT nt_rflop_drive_type IPT1( UTINY, drive );
+SHORT nt_rflop_rate IPT2( UTINY, drive, half_word, rate);
+SHORT nt_rflop_reset IPT2( FDC_RESULT_BLOCK *, res, UTINY, drive );
+SHORT nt_rflop_command IPT2( FDC_CMD_BLOCK *, ip, FDC_RESULT_BLOCK *, res);
+HANDLE nt_rdiskette_open_drive IPT1 ( UTINY, drive );
+SHORT guess_media_density IPT1 (UTINY, drive);
+VOID set_floppy_parms IPT1 (FLP, flp);
+BOOL dos_compatible
+ IPT5 (FLP, flp, UTINY, cyl, UTINY, hd, UTINY, sec, UTINY, n);
+int dos_offset
+ IPT4 (FLP, flp, UTINY, cyl, UTINY, hd, UTINY, sec);
+VOID update_chrn(FLP flp, UTINY mt, UTINY eot, UTINY sector_count);
+HANDLE get_drive_handle (UTINY drive, USHORT pdb, BOOL auto_lock);
+SHORT fdc_read_write ( FDC_CMD_BLOCK * ip, FDC_RESULT_BLOCK * res);
+VOID floppy_close_down(USHORT, BOOL);
+VOID HostFloppyReset(VOID);
+VOID FloppyTerminatePDB(USHORT);
+int DiskOpenRetry(CHAR);
+
+extern USHORT * pusCurrentPDB;
+
+#ifdef EJECT_FLOPPY
+GLOBAL void host_floppy_eject IFN1(UTINY, drive)
+#endif
+
+
+/********************************************************/
+
+/* STATIC GLOBALS */
+
+
+FL floppy_data[MAX_FLOPPY];
+
+ struct flop_struct floppy_tksc [6] =
+{
+ {0, 0}, /* GFI_DRIVE_TYPE_NULL */
+ {40, 9}, /* GFI_DRIVE_TYPE_360 */
+ {80, 15}, /* GFI_DRIVE_TYPE_12 */
+ {80, 9}, /* GFI_DRIVE_TYPE_720 */
+ {80, 18}, /* GFI_DRIVE_TYPE_144 */
+ {80, 36} /* GFI_DRIVE_TYPE_288 */
+};
+// table used to convert GFI diskette type to NT diskette type
+static MEDIA_TYPE media_table[GFI_DRIVE_TYPE_MAX] = {
+ Unknown,
+ F5_360_512,
+ F5_1Pt2_512,
+ F3_720_512,
+ F3_1Pt44_512,
+ F3_2Pt88_512
+ };
+
+SHORT density_state;
+BOOL density_changed = TRUE;
+UTINY last_drive = 0xff;
+BOOL fdc_reset = FALSE;
+HANDLE fdc_thread_handle = NULL;
+extern UTINY number_of_floppy;
+FDC_PARMS fdc_parms;
+ULONG floppy_open_count = 0;
+
+
+
+/*
+ * Debugging info only, for non-prod cases
+ */
+#ifndef PROD
+ CHAR *cmd_name [] =
+{
+ "Invalid command (00)", /* 00 */
+ "Invalid command (01)", /* 01 */
+ "Read a Track", /* 02 */
+ "Specify", /* 03 */
+ "Sense Drive Status", /* 04 */
+ "Write Data", /* 05 */
+ "Read Data", /* 06 */
+ "Recalibrate", /* 07 */
+ "Sense Interrupt Status", /* 08 */
+ "Write Deleted Data", /* 09 */
+ "Read ID", /* 0A */
+ "Invalid Command (0B)", /* 0B */
+ "Read Deleted Data", /* 0C */
+ "Format a Track", /* 0D */
+ "Invalid Command (0E)", /* 0E */
+ "Seek", /* 0F */
+ "Invalid Command (10)", /* 10 */
+ "Scan Equal", /* 11 */
+ "Invalid Command (12)", /* 12 */
+ "Invalid Command (13)", /* 13 */
+ "Invalid Command (14)", /* 14 */
+ "Invalid Command (15)", /* 15 */
+ "Invalid Command (16)", /* 16 */
+ "Invalid Command (17)", /* 17 */
+ "Invalid Command (18)", /* 18 */
+ "Scan Low or Equal", /* 19 */
+ "Invalid Command (1A)", /* 1A */
+ "Invalid Command (1B)", /* 1B */
+ "Invalid Command (1C)", /* 1C */
+ "Scan High or Equal", /* 1D */
+ "Invalid Command (1E)", /* 1E */
+ "Invalid Command (1F)", /* 1F */
+};
+#endif /* PROD */
+
+char dump_buf[256];
+
+
+/* the disk buffer, there is only one, even though there may be two
+ * drives. Should be O.K. as floppy disk accesses will be single
+ * threaded.
+ */
+ UTINY *disk_buffer;
+
+/* Report any errors in open_diskette() */
+ int last_error = C_CONFIG_OP_OK;
+
+/********************************************************/
+
+/* GLOBAL FUNCTIONS */
+
+/* These functions called by config/UIF/startup now form the only
+** interface between SoftPC and a floppy module. XXX_active() will
+** turn the floppy emmulation in the module on by loading the global
+** gfi_function_table[] with pointers to appropriate functions
+** defined in this module.
+** The floppy supported here is turned off by asking the empty floppy
+** module to turn itself on in its place.
+**
+** This makes a nice orthogonal interface which keeps everything save
+** the three control functions (private). The functions that are put
+** in the table are defined only in gfi.h as typedefs so they are easy to
+** get right.
+**
+** This enabling/disabling via the gfi_function_table[] does not
+** take place instead of any host ioctls/opens/closes etc that are needed
+** to actually open or close the device, it forms the interface for SoftPC.
+**
+** Really, this approach is a small tidy up of the way things are
+** already done; existing host floppy code will require very small changes.
+**
+** GM
+*/
+
+/********************************************************/
+
+/* Turn the floppy on and off. Off means release the driver so another
+** process can use it.
+*/
+
+GLOBAL SHORT
+host_gfi_rdiskette_active IFN3(UTINY, hostID, BOOL, active, CHAR *, err)
+{
+ UTINY drive = hostID - C_FLOPPY_A_DEVICE;
+ FLP flp = &floppy_data[drive];
+
+ if(active)
+ {
+ if (!nt_gfi_rdiskette_init(drive))
+ {
+ /* Device is not a valid floppy */
+
+ return( C_CONFIG_NOT_VALID );
+ }
+ return(C_CONFIG_OP_OK);
+ }
+ else
+ {
+#ifdef EJECT_FLOPPY
+ host_floppy_eject(drive);
+#endif /* EJECT_FLOPPY */
+ nt_gfi_rdiskette_term(flp); /* shutdown process */
+ gfi_empty_active(hostID,TRUE,err); /* Tell gfi 'empty' is now active */
+ return(C_CONFIG_OP_OK);
+ }
+}
+
+/********************************************************/
+
+
+/* Validate the floppy device name passed from the config system.
+** Empty string is valid; it means 'no floppy'. Otherwise return OK is
+** the name is 'probably' a valid device. It cannot be opened at this
+** stage because if there is no floppy in the drive, the open will fail.
+**
+** GM.
+*/
+
+GLOBAL SHORT
+host_gfi_rdiskette_valid IFN3(UTINY,hostID,ConfigValues *,vals,CHAR *,err)
+{
+#ifndef NTVDM
+ UTINY cmos_byte;
+ UTINY drive = hostID - C_FLOPPY_A_DEVICE;
+ FLP flp = &floppy_data[drive];
+
+ if(!strcmp(vals->string,""))
+ return(C_CONFIG_OP_OK);
+
+ strcpy(flp->device_name, host_expand_environment_vars(vals->string));
+
+ if(!host_validate_pathname(flp->device_name))
+ {
+ strcpy(err, host_strerror(errno));
+ flp->device_name[0] = '\0';
+ return( EG_MISSING_FILE );
+ }
+ if(!host_file_is_char_dev(flp->device_name))
+ {
+ flp->device_name[0] = '\0';
+ return( EG_NOT_CHAR_DEV );
+ }
+
+ /* Check the CMOS RAM values */
+ cmos_read_byte(CMOS_DISKETTE, &cmos_byte);
+ if (drive == 0)
+ cmos_byte >>= 4;
+
+ cmos_byte &= 0xf; /* compare nibble value only */
+ flp->drive_type = host_rflop_drive_type(drive);
+ if (cmos_byte != flp->drive_type)
+ vals->rebootReqd = TRUE;
+#endif
+ return(C_CONFIG_OP_OK);
+}
+
+/********************************************************/
+
+GLOBAL VOID
+host_gfi_rdiskette_change IFN2(UTINY, hostID, BOOL, apply)
+{
+#ifndef NTVDM
+ FLP flp = &floppy_data[hostID - C_FLOPPY_A_DEVICE];
+
+ if (apply)
+ {
+ nt_gfi_rdiskette_term(flp);
+ }
+#endif
+}
+
+/********************************************************/
+
+#ifdef EJECT_FLOPPY
+GLOBAL void host_floppy_eject IFN1(UTINY, drive)
+{
+ CHAR *ebuf;
+ FLP flp = &floppy_data[drive];
+ BOOL device_was_closed = FALSE;
+
+ /* open the device */
+ if (flp->diskette_fd == INVALID_HANDLE_VALUE)
+ {
+ device_was_closed = TRUE;
+ (void) nt_rdiskette_open_drive(drive);
+ }
+
+ /* Do the ioctl, put your ioctl here
+
+ if (ioctl(flp->diskette_fd, SMFDEJECT) < 0)
+ {
+ ebuf = host_strerror(errno);
+ assert1(NO, "host_eject_floppy: %s", ebuf);
+ }*/
+
+ /* Close the device if it wasn't open */
+
+ if (device_was_closed)
+ {
+ nt_gfi_rdiskette_term(flp);
+ }
+ else
+ {
+ /* Line change ONLY if device was actively open */
+ flp->change_line_state = TRUE;
+ }
+}
+
+#endif /* EJECT_FLOPPY */
+
+
+/*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
+/*:::::::::::::::::: FLOPPY heart beat call ::::::::::::::::::::::::::::::::::::*/
+/*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
+GLOBAL void host_flpy_heart_beat(void)
+{
+
+ UTINY drive;
+ FLP flp;
+
+ if (pFDAccess && *pFDAccess) {
+ if (floppy_open_count) {
+ for (drive = 0; drive < number_of_floppy; drive++) {
+ flp = & floppy_data[drive];
+ if (flp->diskette_fd != INVALID_HANDLE_VALUE &&
+ --flp->idle_counter == 0) {
+ nt_floppy_close(drive);
+
+ }
+ }
+
+ }
+ if (number_of_fdisk != 0)
+ fdisk_heart_beat();
+ }
+}
+
+
+
+
+/********************************************************/
+
+/* initialise GFI function table */
+BOOL nt_gfi_rdiskette_init IFN1(UTINY, drive)
+{
+ FLP flp;
+ DISK_GEOMETRY disk_geometry[20];
+ ULONG media_types;
+ CHAR DeviceName[] = "\\\\.\\A:";
+ NTSTATUS status;
+ IO_STATUS_BLOCK io_status_block;
+ FILE_ALIGNMENT_INFORMATION align_info;
+
+ flp = &floppy_data[drive];
+ flp->diskette_fd = INVALID_HANDLE_VALUE;
+
+ DeviceName[4] += drive;
+ strcpy(flp->device_name, (const char *)DeviceName);
+ /*
+ * Initialise the floppy on the required drive:
+ *
+ * 0 - Drive A, 1 - Drive B
+ */
+
+ flp->drive_type = GFI_DRIVE_TYPE_NULL;
+ /* open the device */
+ if ((flp->diskette_fd = nt_rdiskette_open_drive (drive)) == NULL) {
+ return FALSE;
+ }
+ // get alignment factor
+ status = NtQueryInformationFile(flp->diskette_fd,
+ &io_status_block,
+ &align_info,
+ sizeof(FILE_ALIGNMENT_INFORMATION),
+ FileAlignmentInformation
+ );
+ if (!NT_SUCCESS(status)) {
+ nt_gfi_rdiskette_term(flp);
+ return(FALSE);
+ }
+ flp->align_factor = align_info.AlignmentRequirement;
+ if (flp->align_factor > max_align_factor)
+ max_align_factor = flp->align_factor;
+
+
+ // enumerate possible supported media for this drive
+ // to figure out the drive type.
+ status = NtDeviceIoControlFile(flp->diskette_fd,
+ NULL,
+ NULL,
+ NULL,
+ &io_status_block,
+ IOCTL_DISK_GET_MEDIA_TYPES,
+ NULL,
+ 0L,
+ (PVOID)&disk_geometry,
+ sizeof(disk_geometry)
+ );
+ if (!NT_SUCCESS(status)) {
+ nt_gfi_rdiskette_term(flp);
+ return FALSE;
+ }
+ nt_gfi_rdiskette_term(flp);
+ media_types = io_status_block.Information / sizeof(DISK_GEOMETRY);
+
+ for (; media_types != 0; media_types--) {
+ switch (disk_geometry[media_types - 1].MediaType) {
+ case F5_360_512:
+ if (flp->drive_type != GFI_DRIVE_TYPE_12)
+ flp->drive_type = GFI_DRIVE_TYPE_360;
+ break;
+ case F5_1Pt2_512:
+ flp->drive_type = GFI_DRIVE_TYPE_12;
+ break;
+ case F3_720_512:
+ if (flp->drive_type != GFI_DRIVE_TYPE_144 &&
+ flp->drive_type != GFI_DRIVE_TYPE_288)
+ flp->drive_type = GFI_DRIVE_TYPE_720;
+ break;
+ case F3_1Pt44_512:
+ if (flp->drive_type != GFI_DRIVE_TYPE_288)
+ flp->drive_type = GFI_DRIVE_TYPE_144;
+ break;
+ case F3_2Pt88_512:
+ flp->drive_type = GFI_DRIVE_TYPE_288;
+ break;
+ }
+ }
+ if (flp->drive_type == GFI_DRIVE_TYPE_NULL)
+ return FALSE;
+ /* configure its vectors here */
+ gfi_function_table[drive].command_fn = nt_rflop_command;
+ gfi_function_table[drive].drive_on_fn = nt_rflop_drive_on;
+ gfi_function_table[drive].drive_off_fn = nt_rflop_drive_off;
+ gfi_function_table[drive].reset_fn = nt_rflop_reset;
+ gfi_function_table[drive].high_fn = nt_rflop_rate;
+ gfi_function_table[drive].drive_type_fn= nt_rflop_drive_type;
+ gfi_function_table[drive].change_fn = nt_rflop_change;
+ flp->C = flp->H = 0;
+ flp->R = 1;
+ flp->N = PC_N_VALUE;
+ flp->auto_locked = FALSE;
+ flp->owner_pdb = 0;
+ return TRUE;
+}
+
+/********************************************************/
+
+/* reset GFI function table */
+/* currently drive is ignored */
+VOID nt_gfi_rdiskette_term IFN1(FLP, flp)
+{
+
+ // NtOpenFile returns NULL if we can not open a handle
+ // while win32 OpenFile/CreateFile returns INVALID_HANDLE_VALUE
+ // if failed to open/create the file.
+ if (flp->diskette_fd != NULL)
+ {
+// host_clear_lock(flp->diskette_fd);
+ NtClose(flp->diskette_fd);
+ flp->diskette_fd = INVALID_HANDLE_VALUE;
+ }
+}
+
+/********************************************************/
+
+/* open the floppy device file */
+HANDLE nt_rdiskette_open_drive IFN1(UTINY, drive)
+{
+
+ CHAR NtDeviceName[] = "\\DosDevices\\A:";
+ PUNICODE_STRING Unicode;
+ ANSI_STRING DeviceNameA;
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES FloppyObj;
+ IO_STATUS_BLOCK IoStatusBlock;
+ HANDLE fd;
+
+ NtDeviceName[12] += drive;
+
+ RtlInitAnsiString( &DeviceNameA, NtDeviceName);
+
+ Unicode = &NtCurrentTeb()->StaticUnicodeString;
+
+ Status = RtlAnsiStringToUnicodeString(Unicode,
+ &DeviceNameA,
+ FALSE
+ );
+ if ( !NT_SUCCESS(Status) )
+ return NULL;
+
+
+ InitializeObjectAttributes(
+ &FloppyObj,
+ Unicode,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+ Status = NtOpenFile(
+ &fd,
+ (ACCESS_MASK) FILE_READ_ATTRIBUTES | SYNCHRONIZE,
+ &FloppyObj,
+ &IoStatusBlock,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE
+ );
+
+ if (!NT_SUCCESS(Status))
+ return NULL;
+ else
+ return fd;
+
+}
+
+
+ULONG nt_floppy_read(BYTE drive, ULONG Offset, ULONG Size, PBYTE Buffer)
+{
+ HANDLE fd;
+ LARGE_INTEGER large_integer;
+
+ fd = get_drive_handle(drive, *pusCurrentPDB, FALSE);
+
+ if (fd == INVALID_HANDLE_VALUE)
+ return 0;
+ large_integer.LowPart = Offset;
+ large_integer.HighPart = 0;
+ return(disk_read(fd, &large_integer, Size, Buffer));
+}
+
+
+ULONG nt_floppy_write(BYTE drive, ULONG Offset, ULONG Size, PBYTE Buffer)
+{
+ HANDLE fd;
+ LARGE_INTEGER large_integer;
+ ULONG size_returned;
+
+ fd = get_drive_handle(drive, *pusCurrentPDB, TRUE);
+ if (fd == INVALID_HANDLE_VALUE)
+ return 0;
+
+ large_integer.LowPart = Offset;
+ large_integer.HighPart = 0;
+ size_returned = disk_write(fd, &large_integer, Size, Buffer);
+ return (size_returned);
+}
+
+BOOL nt_floppy_format(BYTE drive, WORD Cylinder, WORD Head, MEDIA_TYPE Media)
+{
+ FORMAT_PARAMETERS fmt;
+ WORD bad_track;
+ ULONG size_returned;
+ HANDLE fd;
+ BOOL result;
+
+ result = FALSE;
+ fmt.MediaType = Media;
+ fmt.StartHeadNumber = fmt.EndHeadNumber = Head;
+ fmt.StartCylinderNumber = fmt.EndCylinderNumber = Cylinder;
+ fd = get_drive_handle(drive, *pusCurrentPDB,TRUE);
+ if (fd == INVALID_HANDLE_VALUE)
+ return FALSE;
+ result = DeviceIoControl(fd,
+ IOCTL_DISK_FORMAT_TRACKS,
+ (PVOID) &fmt,
+ sizeof(fmt),
+ &bad_track,
+ sizeof(bad_track),
+ &size_returned,
+ NULL
+ );
+ return result;
+}
+
+// for floppy, the ioctl call DISK_VERIFY doesn't work
+// we have to use read for verification
+BOOL nt_floppy_verify(BYTE drive, DWORD Offset, DWORD Size)
+{
+ HANDLE fd;
+ LARGE_INTEGER large_integer;
+
+ fd = get_drive_handle(drive, *pusCurrentPDB, FALSE);
+ if (fd != INVALID_HANDLE_VALUE) {
+ large_integer.LowPart = Offset;
+ large_integer.HighPart = 0;
+ return(disk_verify(fd,
+ &large_integer,
+ Size
+ ));
+ }
+ else
+ return FALSE;
+}
+
+int DiskOpenRetry(char chDrive)
+{
+ char FormatString[32];
+ char DriveLetter[32];
+
+ if (!LoadString(GetModuleHandle(NULL), ED_DRIVENUM,
+ FormatString,sizeof(FormatString)) )
+ {
+ strcpy(FormatString,"Drive %c: ");
+ }
+ sprintf(DriveLetter, FormatString, chDrive);
+ return(RcMessageBox(ED_LOCKDRIVE, DriveLetter, NULL,
+ RMB_ABORT | RMB_RETRY | RMB_IGNORE | RMB_ICON_BANG
+ ));
+}
+
+
+HANDLE get_drive_handle(UTINY drive, USHORT pdb, BOOL auto_lock)
+{
+ FLP flp;
+ DWORD share_access;
+
+
+ flp = &floppy_data[drive];
+ // assign new alignment factor and grab the buffer
+ cur_align_factor = flp->align_factor;
+ if ((disk_buffer = get_aligned_disk_buffer()) == NULL)
+ return (INVALID_HANDLE_VALUE);
+
+
+ if (flp->diskette_fd != INVALID_HANDLE_VALUE &&
+ (fdc_reset || (auto_lock && !flp->auto_locked) ||
+ flp->owner_pdb != pdb))
+ {
+ nt_floppy_close(drive);
+ fdc_reset = FALSE;
+ }
+ share_access = auto_lock ? FILE_SHARE_READ :
+ FILE_SHARE_READ | FILE_SHARE_WRITE;
+ while(flp->diskette_fd == INVALID_HANDLE_VALUE) {
+ flp->diskette_fd = CreateFile ((const char *)flp->device_name,
+ GENERIC_READ | GENERIC_WRITE,
+ share_access,
+ NULL,
+ OPEN_EXISTING,
+ 0,
+ 0
+ );
+ if (flp->diskette_fd != INVALID_HANDLE_VALUE) {
+ floppy_open_count++;
+ flp->auto_locked = auto_lock;
+ flp->owner_pdb = pdb;
+ (*(pFDAccess))++;
+ break;
+ }
+ if (auto_lock && GetLastError() == ERROR_SHARING_VIOLATION &&
+ DiskOpenRetry((char)(drive + (UTINY)'A')) == RMB_RETRY)
+ continue;
+ else
+ break;
+
+ }
+ flp->idle_counter = FLOPPY_IDLE_PERIOD;
+ return (flp->diskette_fd);
+}
+
+
+
+VOID HostFloppyReset(VOID)
+{
+ FloppyTerminatePDB((USHORT)0);
+}
+
+VOID FloppyTerminatePDB(USHORT PDB)
+{
+ UTINY drive;
+ FLP flp;
+
+ if (floppy_open_count) {
+ for (drive = 0; drive < number_of_floppy; drive++) {
+ flp = &floppy_data[drive];
+ if (flp->diskette_fd != INVALID_HANDLE_VALUE &&
+ (PDB == 0 || flp->owner_pdb == PDB))
+ nt_floppy_close(drive);
+ }
+
+ }
+}
+
+BOOL nt_floppy_close(UTINY drive)
+{
+ FLP flp;
+ flp = &floppy_data[drive];
+
+#ifndef PROD
+ if (rflop_dbg & RFLOP_CLOSE)
+ sprintf(dump_buf, "Close drive %C: handle\n", drive + 'A');
+ OutputDebugString(dump_buf);
+#endif
+
+ if (flp->diskette_fd != INVALID_HANDLE_VALUE) {
+ CloseHandle(flp->diskette_fd);
+ flp->diskette_fd = INVALID_HANDLE_VALUE;
+ (*(pFDAccess))--;
+ flp->auto_locked = FALSE;
+ flp->owner_pdb = 0;
+ floppy_open_count--;
+ }
+ density_changed = TRUE;
+ return TRUE;
+}
+
+
+MEDIA_TYPE
+nt_floppy_get_media_type
+(
+BYTE drive,
+WORD cylinders,
+WORD sectors,
+WORD heads
+)
+{
+ FLP flp;
+ USHORT index;
+
+ flp = &floppy_data[drive];
+ if (heads == 2){
+ index = flp->drive_type;
+ switch (index) {
+ case GFI_DRIVE_TYPE_12:
+ if (cylinders == floppy_tksc[index].trks_per_disk &&
+ sectors == floppy_tksc[index].secs_per_trk)
+ break;
+ index = GFI_DRIVE_TYPE_360;
+
+ case GFI_DRIVE_TYPE_360:
+ if (cylinders != floppy_tksc[index].trks_per_disk ||
+ sectors != floppy_tksc[index].secs_per_trk)
+ index = GFI_DRIVE_TYPE_NULL;
+ break;
+
+ case GFI_DRIVE_TYPE_288:
+ if (cylinders == floppy_tksc[index].trks_per_disk &&
+ sectors == floppy_tksc[index].secs_per_trk)
+ break;
+ index = GFI_DRIVE_TYPE_144;
+
+ case GFI_DRIVE_TYPE_144:
+ if (cylinders == floppy_tksc[index].trks_per_disk &&
+ sectors == floppy_tksc[index].secs_per_trk)
+ break;
+ index = GFI_DRIVE_TYPE_720;
+
+ case GFI_DRIVE_TYPE_720:
+ if (cylinders == floppy_tksc[index].trks_per_disk &&
+ sectors == floppy_tksc[index].secs_per_trk)
+ break;
+ default:
+ index = GFI_DRIVE_TYPE_NULL;
+ }
+ }
+ else
+ index = GFI_DRIVE_TYPE_NULL;
+ return(media_table[index]);
+
+}
+
+BOOL nt_floppy_media_check (UTINY drive)
+{
+ FLP flp;
+ ULONG size_returned;
+
+ flp = &floppy_data[drive];
+ if (flp->diskette_fd == INVALID_HANDLE_VALUE)
+ return FALSE;
+ return(DeviceIoControl(flp->diskette_fd,
+ IOCTL_DISK_CHECK_VERIFY,
+ NULL,
+ 0,
+ NULL,
+ 0,
+ &size_returned,
+ NULL
+ ));
+}
+
+/********************************************************/
+
+/* perform an FDC command */
+ SHORT
+nt_rflop_command
+ IFN2(FDC_CMD_BLOCK *, command_block, FDC_RESULT_BLOCK *,result_block)
+{
+ UTINY drive;
+ FLP flp;
+ BOOL failed = FALSE;
+ UTINY C, H, N, S, D;
+ DWORD fdc_thread_id;
+ BYTE fdc_command;
+ BOOL auto_lock;
+
+
+ note_trace1 (GFI_VERBOSE, "FDC: %s command",
+ cmd_name [get_type_cmd (command_block)]);
+
+ drive = get_type_drive(command_block);
+
+ flp = &floppy_data[drive];
+ flp->idle_counter = FLOPPY_IDLE_PERIOD;
+
+ /* Clear result status registers */
+ put_r0_ST0 (result_block, 0);
+ put_r0_ST1 (result_block, 0);
+ put_r0_ST2 (result_block, 0);
+
+ fdc_command = get_type_cmd(command_block);
+ /* for those commands which need a valid floppy be inserted
+ we may have to create an independent thread to perform
+ the real operation if there is currenly no media
+ in the drive. The reason of this independent thread is that
+ the FDC is always in its execution phase even though there is
+ not media in the drive. As soon as you insert a media(bad or
+ good), it then performs its operation, terminates the phase,
+ raises interrupt and enters result phase. Some applications just
+ do a read id and then wait the interrupt to occur no matter how
+ long the user will take to insert a media. To do this I broke up
+ the fdc_command routine so that both main and the fdc thread can
+ use the same code. There is not a good point that we can close the
+ thread handle as soon as it terminated. Therefore, we close the
+ handle on next fdc command
+ */
+ if (fdc_thread_handle != NULL) {
+ CloseHandle(fdc_thread_handle);
+ fdc_thread_handle = NULL;
+ }
+
+ if ( (auto_lock = (fdc_command == FDC_WRITE_DATA || fdc_command == FDC_FORMAT_TRACK)) ||
+ fdc_command == FDC_READ_DATA ||
+ fdc_command == FDC_READ_ID ||
+ fdc_command == FDC_READ_TRACK) {
+ // this might fail due to media changed and from FDC point of
+ // view, media change is meaningless. Therefore, we close the
+ // handle to the drive and reopen it so that the file system
+ // will mount a new volume for us. Then we check the the
+ // media again. If it still fails, we are sure that there is
+ // no media in the drive so we go ahead to create a thread.
+ if (!nt_floppy_media_check(drive)) {
+ nt_floppy_close(drive);
+ get_drive_handle(drive, *pusCurrentPDB, auto_lock);
+ if (!nt_floppy_media_check(drive)) {
+ fdc_parms.auto_lock = auto_lock;
+ fdc_parms.command_block = command_block;
+ fdc_parms.result_block = result_block;
+ fdc_parms.owner_pdb = *pusCurrentPDB;
+ fdc_thread_handle = CreateThread(NULL,
+ 0,
+ (LPTHREAD_START_ROUTINE)fdc_thread,
+ (PVOID)&fdc_parms,
+ 0,
+ &fdc_thread_id
+ );
+ return FAILURE;
+ }
+ else { // media changed
+ fdc_read_write(command_block, result_block);
+ return SUCCESS;
+ }
+
+ }
+ else {
+ fdc_read_write(command_block, result_block);
+ return SUCCESS;
+ }
+ }
+
+ /* get disk bumpf */
+ C = get_c0_cyl (command_block);
+ H = get_c0_hd (command_block);
+ S = get_c0_sector (command_block);
+ N = get_c0_N (command_block);
+
+ /* block timer to prevent interrupted system calls */
+ host_block_timer ();
+
+ switch (get_type_cmd (command_block))
+ {
+
+ case FDC_SPECIFY:
+#ifndef PROD
+ if (rflop_dbg & RFLOP_SPECIFY) {
+ OutputDebugString("Specify\n");
+ if (rflop_dbg & RFLOP_BREAK)
+ nt_rflop_break();
+ }
+#endif
+ break;
+
+
+ case FDC_SENSE_DRIVE_STATUS:
+
+#ifndef PROD
+ if (rflop_dbg & RFLOP_SENSEDRV)
+ OutputDebugString("Sense Drive Status\n");
+#endif
+ D = get_c7_drive (command_block);
+ put_r2_ST3_fault (result_block,0);
+ put_r2_ST3_ready (result_block,1);
+ put_r2_ST3_track_0 (result_block,(flp->last_head_seek == 0?1:0));
+ put_r2_ST3_two_sided (result_block,1);
+ put_r2_ST3_head_address (result_block,0);
+ put_r2_ST3_unit (result_block,D);
+ break;
+
+ /* RECALIBRATE and SEEK do not really return any results */
+ /* However, we return results here which are used by gfi.c */
+ /* to construct the results for any following SenseInterruptStatus command */
+ case FDC_RECALIBRATE:
+
+#ifndef PROD
+ if (rflop_dbg & RFLOP_RECAL)
+ OutputDebugString("Recalibrate\n");
+#endif
+ D = get_c5_drive (command_block);
+ put_r3_ST0 (result_block,0);
+ put_r1_ST0_int_code (result_block,0);
+ put_r1_ST0_seek_end (result_block,1);
+ put_r1_ST0_unit (result_block,D);
+ put_r3_PCN (result_block,0);
+ flp->last_seek = flp->last_head_seek = 0;
+ flp->C = 0;
+ break;
+
+ case FDC_SEEK:
+
+ D = get_c8_drive (command_block);
+ C = get_c8_new_cyl (command_block);
+
+#ifndef PROD
+ if (rflop_dbg & RFLOP_SEEK) {
+ sprintf(dump_buf, "Seek: D C = %d %d \n", D, C);
+ OutputDebugString(dump_buf);
+ if (rflop_dbg & RFLOP_BREAK)
+ nt_rflop_break();
+ }
+#endif
+
+ put_r3_ST0(result_block,0);
+ put_r1_ST0_head_address(result_block,1);
+ put_r1_ST0_seek_end(result_block,1);
+ put_r1_ST0_int_code(result_block,0);
+ put_r1_ST0_unit(result_block,D);
+ put_r3_PCN(result_block,C);
+ flp->last_seek = C;
+ flp->last_head_seek = min(flp->last_seek,flp->max_track);
+ flp->C = C;
+ break;
+
+ default:
+
+#ifndef PROD
+ sprintf(dump_buf, "Receive unsupported command: command = %d\n",
+ get_type_cmd(command_block));
+ OutputDebugString(dump_buf);
+#endif
+
+ put_r0_ST0 (result_block, 0);
+ put_r1_ST0_int_code (result_block, 2);
+
+ note_trace1 (GFI_VERBOSE,"FDC: Unimplemented command, type %d",
+ get_type_cmd (command_block));
+ }
+
+
+#ifndef PROD
+ if (io_verbose & GFI_VERBOSE) {
+ fprintf(trace_file,
+ "FDC: results %02x %02x %02x %02x %02x %02x %02x\n\n",
+ result_block[0], result_block[1], result_block[2],
+ result_block[3], result_block[4], result_block[5],
+ result_block[6]);
+ }
+#endif /* !PROD */
+
+ host_release_timer ();
+
+ return SUCCESS;
+}
+
+/********************************************************/
+
+/* turn the motor on */
+SHORT
+nt_rflop_drive_on IFN1(UTINY, drive)
+{
+ FLP flp = &floppy_data[drive];
+
+ note_trace0 (GFI_VERBOSE, "FDC: Drive on command");
+#ifndef PROD
+ if (rflop_dbg & RFLOP_DRIVE_ON) {
+ sprintf(dump_buf, "drive on: drive = %d\n", drive);
+ OutputDebugString(dump_buf);
+ if (rflop_dbg & RFLOP_BREAK)
+ nt_rflop_break();
+ }
+#endif
+
+ if (drive >= number_of_floppy)
+ {
+ note_trace1 (GFI_VERBOSE,
+ "FDC: Invalid drive %d accessed", drive);
+
+ return (FAILURE);
+ }
+
+ flp->motor_state = MOTOR_ON;
+
+ return (SUCCESS);
+}
+
+/********************************************************/
+
+/* turn the motor off */
+SHORT
+nt_rflop_drive_off IFN1(UTINY, drive)
+{
+ FLP flp = &floppy_data[drive];
+ note_trace0 (GFI_VERBOSE, "FDC: Drive off command");
+#ifndef PROD
+ if (rflop_dbg & RFLOP_DRIVE_OFF) {
+ sprintf(dump_buf, "drive off: drive = %d\n", drive);
+ OutputDebugString(dump_buf);
+ if (rflop_dbg & RFLOP_BREAK)
+ nt_rflop_break();
+ }
+#endif
+
+ if (drive >= number_of_floppy)
+ {
+ note_trace1 (GFI_VERBOSE,
+ "FDC: Invalid drive %d accessed", drive);
+
+ return (FAILURE);
+ }
+
+ flp->motor_state = MOTOR_OFF;
+
+ /* I believe the line below makes booting off of low density
+ * diskettes problematical, particularly after restarts.
+ * Make your own mind up, the DEC code does it, the Sparc
+ * doesn't (as at 11/9/92)
+// we have no reason to do so in NT. As far as change line concerned,
+// the file system will tell us "media has been changed" when we ask
+// it to do some real work.
+// flp->change_line_state = TRUE;
+ */
+
+ return (SUCCESS);
+}
+
+/********************************************************/
+
+/* set the data transfer rate
+ * This controls the "density" of the floppy: the rate MUST
+ * match the actual media density for the disk controller to
+ * be able to read the sectors.
+ */
+SHORT
+nt_rflop_rate IFN2(UTINY, drive, half_word, rate)
+{
+ short new_density;
+// basically, "set rate applied to every drive since we have
+// only one FDC(and mutiple drive).
+
+#if 0
+ FLP flp = &floppy_data[drive];
+
+ switch (rate)
+ {
+ /* 2.88M high-density floppies */
+ case DCR_RATE_1000:
+
+ flp->density_state = DENSITY_EXTENDED;
+ set_floppy_parms (flp);
+ break;
+
+ /* 1.2M or 1.44M high-density floppies */
+ case DCR_RATE_500:
+
+ flp->density_state = DENSITY_HIGH;
+ set_floppy_parms (flp);
+ break;
+
+ /* 360K or 720K low-density floppies */
+ case DCR_RATE_250:
+ case DCR_RATE_300:
+
+ flp->density_state = DENSITY_LOW;
+ set_floppy_parms (flp);
+ break;
+
+ /* crapola density passed */
+ default:
+
+ return FAILURE;
+ }
+ note_trace2 (GFI_VERBOSE, "FDC: Set rate %0x => density %d",
+ rate, flp->density_state);
+ /* read floppy's boot sector */
+ /* to determine the real density */
+// guess_media_density (drive);
+#endif
+#ifndef PROD
+ if (rflop_dbg & RFLOP_RATE) {
+ sprintf(dump_buf, "set rate: rate = %d\n", rate);
+ OutputDebugString(dump_buf);
+ if (rflop_dbg & RFLOP_BREAK)
+ nt_rflop_break();
+ }
+#endif
+
+ switch (rate) {
+ case DCR_RATE_1000:
+ new_density = DENSITY_EXTENDED;
+ break;
+ case DCR_RATE_500:
+ new_density = DENSITY_HIGH;
+ break;
+ case DCR_RATE_300:
+ case DCR_RATE_250:
+ new_density = DENSITY_LOW;
+ break;
+ default:
+ return FAILURE;
+
+ }
+ if (new_density != density_state) {
+ density_state = new_density;
+ density_changed = TRUE;
+ }
+ return SUCCESS;
+}
+
+
+/********************************************************/
+
+/* return the state of the change line */
+SHORT
+nt_rflop_change IFN1(UTINY, drive)
+{
+ FLP flp = &floppy_data[drive];
+ note_trace1 (GFI_VERBOSE, "FDC: change_line %c",
+ flp->change_line_state? 'T':'F');
+
+ // if fla has been reset or the current change line is on(no media),
+ // close the drive and reopen it. This is done because
+ // nt_floppy_media_check(IOCTL_DISK_CHECK_VERIFY) will continue
+ // to report media change even the users have a new disketter inserted.
+ //
+ if (fdc_reset || flp->change_line_state) {
+ fdc_reset = FALSE;
+ nt_floppy_close(drive);
+ }
+ get_drive_handle(drive, *pusCurrentPDB, FALSE);
+ flp->change_line_state = !nt_floppy_media_check(drive);
+
+#ifndef PROD
+ if (rflop_dbg & RFLOP_CHANGE) {
+ sprintf(dump_buf, "Check Change Line: line = %d\n", flp->change_line_state);
+ OutputDebugString(dump_buf);
+ if (rflop_dbg & RFLOP_BREAK)
+ nt_rflop_break();
+ }
+#endif
+
+ return(flp->change_line_state);
+}
+
+/********************************************************/
+
+/* return the type of the drive */
+SHORT
+nt_rflop_drive_type IFN1(UTINY, drive)
+{
+ FLP flp = &floppy_data[drive];
+
+
+/* setup base media type depending on drive type */
+// I don't understand why we have to do this stuff every time.
+ switch (flp->drive_type)
+ {
+ /* 5.25" drives */
+ case GFI_DRIVE_TYPE_360:
+ case GFI_DRIVE_TYPE_12:
+
+ flp->flop_type = GFI_DRIVE_TYPE_360;
+ break;
+
+ /* 3.5" drives */
+ case GFI_DRIVE_TYPE_720:
+ case GFI_DRIVE_TYPE_144:
+ case GFI_DRIVE_TYPE_288:
+
+ flp->flop_type = GFI_DRIVE_TYPE_720;
+ break;
+
+ default:
+ break;
+ }
+
+ set_floppy_parms(flp);
+ note_trace2 (GFI_VERBOSE, "FDC: flop_type %d density %d",
+ flp->flop_type, flp->drive_type - flp->flop_type);
+
+ return (flp->drive_type);
+}
+
+/********************************************************/
+
+/* close and reopen the device */
+SHORT
+nt_rflop_reset IFN2(FDC_RESULT_BLOCK *, result_block, UTINY, drive)
+{
+ FLP flp = &floppy_data[drive];
+
+ note_trace0 (GFI_VERBOSE, "FDC: Reset command");
+
+#ifndef PROD
+ if (rflop_dbg & RFLOP_RESET) {
+ OutputDebugString("reset\n");
+ if (rflop_dbg & RFLOP_BREAK)
+ nt_rflop_break();
+ }
+#endif
+ /* clear change line */
+ flp->change_line_state = FALSE;
+ fdc_reset = TRUE;
+
+ if (fdc_thread_handle) { // signal thread to exit
+ CloseHandle(fdc_thread_handle);
+ fdc_thread_handle = NULL;
+ }
+ return (SUCCESS);
+}
+
+
+// this is the independent thread which performs FDC operation.
+// this thread is not created from the beginning, instead, it was
+// created on demand.
+void fdc_thread(PFDC_PARMS fdc_parms)
+{
+ BYTE drive, fdc_command;
+ FDC_CMD_BLOCK * command_block;
+ BOOL auto_lock;
+ USHORT pdb;
+ command_block = fdc_parms->command_block;
+ auto_lock = fdc_parms->auto_lock;
+ pdb = fdc_parms->owner_pdb;
+ drive = get_type_drive(command_block);
+ fdc_command = get_type_cmd(command_block);
+ while (TRUE) {
+ // if there is media inserted, perform the operation
+ // and enter result phase.
+ if (get_drive_handle(drive, pdb, auto_lock) != INVALID_HANDLE_VALUE &&
+ nt_floppy_media_check(drive)) {
+ // force the file system to remount the volume
+ nt_floppy_close(drive);
+ // and then perform the operation
+ fdc_read_write (command_block, fdc_parms->result_block);
+ // raise an interrupt
+ fdc_command_completed(drive, fdc_command);
+ break;
+ }
+ // if reset happen, quit
+ if (fdc_thread_handle == NULL)
+ break;
+ }
+}
+
+SHORT
+fdc_read_write (
+FDC_CMD_BLOCK * command_block,
+FDC_RESULT_BLOCK * result_block
+)
+{
+
+ USHORT transfer_count; /* Surely counts cannot be negative? GM */
+ FLP flp;
+ BOOL failed = FALSE;
+ UTINY C, H, N, S, D, drive, fdc_command;
+ USHORT dma_size;
+ ULONG transfer_size;
+ ULONG transferred_size;
+ long transfer_start;
+ sys_addr dma_address;
+
+ drive = get_type_drive(command_block);
+ fdc_command = get_type_cmd(command_block);
+
+ /* get disk bumpf */
+ C = get_c0_cyl (command_block);
+ H = get_c0_hd (command_block);
+ S = get_c0_sector (command_block);
+ N = get_c0_N (command_block);
+
+ flp = &floppy_data[drive];
+ /* block timer to prevent interrupted system calls */
+ host_block_timer ();
+ if (fdc_command != FDC_FORMAT_TRACK) {
+ if ((density_changed || drive != last_drive) &&
+ guess_media_density(drive) != DENSITY_UNKNOWN) {
+ set_floppy_parms(flp);
+ density_changed = FALSE;
+ last_drive = drive;
+ }
+ if (density_state != flp->media_density) {
+ put_r0_ST0 (result_block, 0x40);
+ put_r0_ST1 (result_block, 0);
+ put_r1_ST1_no_address_mark (result_block,1);
+ put_r0_ST2 (result_block, 0);
+#ifndef PROD
+ sprintf(dump_buf, "density mismatch: %d <-> %d\n", density_state,
+ flp->media_density);
+ OutputDebugString(dump_buf);
+#endif
+ goto fdc_read_write_exit;
+ }
+ }
+
+
+ /*
+ * Do common setup processing, if read or write
+ */
+ if (fdc_command == FDC_READ_DATA ||
+ fdc_command == FDC_WRITE_DATA) {
+ /*
+ * Find out how much gunk to transfer
+ */
+ dma_enquire (DMA_DISKETTE_CHANNEL, &dma_address, &dma_size);
+ transfer_size = dma_size + 1;
+#ifndef PROD
+ if (transfer_size > BS_DISK_BUFFER_SIZE)
+ always_trace2("FDC: transfer size ( %d ) greater than disk buffer size %d\n", transfer_size, BS_DISK_BUFFER_SIZE);
+#endif /* PROD */
+ /* check params passed are DOS compatible */
+ if (! dos_compatible (flp, C, H, S, N) ||
+ density_state != flp->media_density) {
+ sprintf(dump_buf, "Incompatible DOS diskette, C H R N = %d %d %d %d\n",
+ C, H, S, N);
+ OutputDebugString(dump_buf);
+// do not pop up this annoy message because some applications are simply
+// "probing" the diskette. We just fail the call.
+// host_direct_access_error((ULONG) NOSUPPORT_FLOPPY);
+#ifndef PROD
+
+ if (!dos_compatible (flp, C, H, S, N)) {
+ note_trace0 (GFI_VERBOSE,
+ "Refused: not DOS compatible");
+ }
+ if (density_state != flp->media_density) {
+ note_trace0 (GFI_VERBOSE,
+ "Refused: density mismatch");
+ }
+#endif /* !PROD */
+ /* Sector not found or wrong size */
+ put_r0_ST0 (result_block,0x40);
+ put_r0_ST1 (result_block,0);
+ if (density_state != flp->media_density) {
+ put_r1_ST1_no_address_mark (result_block,1);
+ } else {
+ put_r1_ST1_no_data (result_block,1);
+ }
+ put_r0_ST2 (result_block,0);
+ goto fdc_read_write_exit;
+ }
+ /* work out start position on floppy and sector count */
+ transfer_start = dos_offset (flp, C, H, S);
+ transfer_count = (USHORT)(transfer_size / PC_BYTES_PER_SECTOR);
+#ifndef PROD
+ if (rflop_dbg & (RFLOP_READ | RFLOP_WRITE)) {
+ sprintf(dump_buf, "Read/Write Sector: start offset = 0x%lx\n",
+ transfer_start);
+ OutputDebugString(dump_buf);
+ sprintf(dump_buf, "Read/Write Sector: size = 0x%x bytes\n", transfer_size);
+ OutputDebugString(dump_buf);
+ }
+#endif
+
+ }
+
+ switch (fdc_command)
+ {
+ case FDC_READ_DATA:
+#ifndef PROD
+ if (rflop_dbg & RFLOP_READ) {
+ sprintf(dump_buf, "Read Sectors: C H R N = %d %d %d %d\n",
+ C, H, S, N);
+ OutputDebugString(dump_buf);
+ if (rflop_dbg & RFLOP_BREAK)
+ nt_rflop_break();
+ }
+#endif
+
+ if (!failed) {
+ transferred_size = nt_floppy_read(drive,
+ transfer_start,
+ transfer_size,
+ disk_buffer
+ );
+ if (transferred_size != transfer_size) {
+ last_error = GetLastError();
+ sprintf(dump_buf, "Read Error, code = %lx\n", last_error);
+ OutputDebugString(dump_buf);
+ failed = TRUE;
+ }
+ else {
+ dma_request (DMA_DISKETTE_CHANNEL,
+ (char *)disk_buffer, (USHORT)transfer_size);
+ }
+ }
+
+ if (failed){
+ put_r0_ST0 (result_block, 0x40);
+ put_r0_ST1 (result_block, 0);
+ put_r1_ST1_no_data (result_block, 1);
+ put_r0_ST2 (result_block, 0);
+ }
+ else {
+ put_r0_ST0 (result_block, 0x04);
+ put_r0_ST1 (result_block, 0);
+ put_r0_ST2 (result_block, 0);
+ put_r1_ST0_unit (result_block, drive);
+ put_r1_ST0_head_address(result_block, H);
+ }
+
+ flp->C = C;
+ flp->H = H;
+ flp->R = S;
+ flp->N = N;
+ update_chrn (flp,
+ (UTINY)(get_c0_MT(command_block)),
+ (UTINY)(get_c0_EOT(command_block)),
+ (UTINY)transfer_count
+ );
+ /* What should these really be? */
+ put_r0_cyl (result_block, flp->C);
+ put_r0_head (result_block, flp->H);
+ put_r0_sector (result_block, flp->R);
+ put_r0_N (result_block, flp->N);
+ break;
+
+ case FDC_WRITE_DATA:
+#ifndef PROD
+ if (rflop_dbg & RFLOP_WRITE) {
+ sprintf(dump_buf, "Write Sectors: C H R N = %d %d %d %d\n",
+ C, H, S, N);
+ OutputDebugString(dump_buf);
+ if (rflop_dbg & RFLOP_BREAK)
+ nt_rflop_break();
+ }
+#endif
+ if (!failed) {
+ /* copy from Intel space */
+ dma_request (DMA_DISKETTE_CHANNEL, (char *) disk_buffer,
+ (USHORT)transfer_size);
+ transferred_size = nt_floppy_write(drive,
+ transfer_start,
+ transfer_size,
+ disk_buffer
+ );
+ if (transferred_size != transfer_size) {
+ last_error = GetLastError();
+ sprintf(dump_buf, "Write Error, code = %lx\n", last_error);
+ OutputDebugString(dump_buf);
+ failed = TRUE;
+ }
+ }
+
+ /* Clear down result bytes */
+ put_r0_ST0 (result_block, 0);
+ put_r0_ST1 (result_block, 0);
+ put_r0_ST2 (result_block, 0);
+
+ if (failed)
+ {
+ put_r1_ST0_int_code (result_block, 1);
+
+ /* make sure we get the correct error for EROFS */
+ if (last_error == ERROR_WRITE_PROTECT)
+ put_r1_ST1_write_protected (result_block, 1);
+ else
+ put_r1_ST1_no_data (result_block, 1);
+ }
+ else
+ {
+ put_r1_ST0_head_address (result_block, H);
+ put_r1_ST0_unit(result_block, drive);
+ }
+
+ flp->C = C;
+ flp->H = H;
+ flp->R = S;
+ flp->N = N;
+
+ update_chrn (flp,
+ (UTINY)(get_c1_MT(command_block)),
+ (UTINY)(get_c1_EOT(command_block)),
+ (UTINY)transfer_count
+ );
+ put_r0_cyl (result_block, flp->C);
+ put_r0_head (result_block, flp->H);
+ put_r0_sector (result_block, flp->R);
+ put_r0_N (result_block, flp->N);
+ break;
+
+ case FDC_READ_TRACK:
+#ifndef PROD
+ if (rflop_dbg & RFLOP_READTRACK) {
+ OutputDebugString("Read Tracks\n");
+ if (rflop_dbg & RFLOP_BREAK)
+ nt_rflop_break();
+ }
+#endif
+
+ break;
+
+ case FDC_FORMAT_TRACK:
+
+ dma_enquire (DMA_DISKETTE_CHANNEL, &dma_address, &dma_size);
+ transfer_size = dma_size + 1;
+ /* copy from Intel space */
+ dma_request (DMA_DISKETTE_CHANNEL, (char *) disk_buffer,
+ (USHORT)transfer_size);
+
+ D = get_c8_drive(command_block);
+ H = get_c8_head(command_block);
+ flp = &floppy_data[D];
+#ifndef PROD
+ if (rflop_dbg & RFLOP_FORMAT) {
+ sprintf(dump_buf, "Format Track: C H Media = %d %d %d \n",
+ flp->last_seek, H, flp->flop_type + density_state);
+ OutputDebugString(dump_buf);
+ if (rflop_dbg & RFLOP_BREAK)
+ nt_rflop_break();
+ }
+#endif
+ if (!nt_floppy_format(D,
+ flp->last_seek,
+ H,
+ media_table[flp->flop_type + density_state]
+ )) {
+ last_error = GetLastError();
+ sprintf(dump_buf, "Format Error, code = %lx\n", last_error);
+ OutputDebugString(dump_buf);
+ failed = TRUE;
+ }
+ if (!failed) {
+ put_r0_ST0 (result_block, 0);
+ put_r0_ST1 (result_block, 0);
+ put_r0_ST2 (result_block, 0);
+ // C H R N are meaningless on formatting
+ }
+ else {
+ put_r0_ST0 (result_block, 0x40);
+ put_r1_ST0_head_address (result_block, H);
+ put_r1_ST0_unit(result_block, D);
+ put_r0_ST1 (result_block, 0);
+ if (last_error == ERROR_WRITE_PROTECT) {
+ put_r1_ST1_write_protected (result_block, 1);
+ }
+ put_r0_ST2 (result_block, 0);
+ }
+ break;
+
+ case FDC_READ_ID:
+
+ H = get_c4_head(command_block);
+ /* check if cylinder number massaging required */
+ if ((flp->flop_type + density_state) == GFI_DRIVE_TYPE_360)
+ {
+ /* 5.25" low density, 40 tracks */
+ C = (UTINY) (flp->last_seek / 2);
+ put_c0_cyl (result_block, C);
+
+ }
+ else
+ {
+ /* no massage required, 80 tracks */
+ C = (UTINY)flp->last_seek;
+ put_r0_cyl (result_block, C);
+
+ }
+ if (flp->C < flp->trks_per_disk) {
+ put_r1_ST0_unit(result_block, drive);
+ put_r1_ST0_head_address(result_block, H);
+ put_r0_head (result_block, H);
+ put_r0_sector (result_block, flp->R);
+ put_r0_N (result_block, flp->N);
+ C = flp->C;
+ put_r0_cyl(result_block, flp->C);
+ }
+ else
+ C = flp->trks_per_disk - 1;
+
+ put_r0_cyl(result_block, C);
+#ifndef PROD
+ if (rflop_dbg & RFLOP_READID) {
+ sprintf(dump_buf, "Read ID: C H R N = %d %d %d %d\n",
+ C, H, flp->R, flp->N);
+ OutputDebugString(dump_buf);
+ if (rflop_dbg & RFLOP_BREAK)
+ nt_rflop_break();
+ }
+#endif
+ }
+
+ if (failed)
+ density_changed = TRUE;
+fdc_read_write_exit:
+
+ return SUCCESS;
+
+}
+/********************************************************/
+
+/* INTERNALLY USED FUNCTIONS */
+
+/* In order to read the data on the floppy, the floppy controller must
+ * be set to the same density (rate) as was used to write the data.
+ * A mismatch in densities will cause read failures, and DOS uses these
+ * failures as a way to probe the diskette for the correct density.
+ *
+ * To emulate the floppy controller correctly, we must somehow
+ * guess the density of the media and produce fake "read failures" if the
+ * controller density doesn't match the media density.
+ *
+ * On the assumption that the operating system has already done this,
+ * and that we are looking at a DOS floppy, nt_flop.c can read the
+ * "total number of sectors" value from the boot sector and guess
+ * the density accordingly. There should be no need for this function
+ * if you have fairly direct access to the disk controller.
+ */
+ int probelist[] = { 720-1, 1440-1, 2400-1, 2880-1, 5760-1, 0-1};
+
+ SHORT
+guess_media_density IFN1(UTINY, drive)
+{
+ int total_sectors;
+ int *probe;
+ FLP flp;
+ ULONG transferred_size;
+
+ flp = &floppy_data[drive];
+ transferred_size = nt_floppy_read(drive,
+ 0L,
+ PC_BYTES_PER_SECTOR,
+ (PBYTE) disk_buffer
+ );
+
+ if (transferred_size != PC_BYTES_PER_SECTOR) {
+ last_error = GetLastError();
+ OutputDebugString("Unknown Media\n");
+ /* assume that the disk is unformatted */
+ return(flp->media_density = DENSITY_UNKNOWN);/* impossible value */
+ }
+
+
+ /* check for a DOS boot block
+ *
+ * AccessPC has shown that 0x55, 0xaa is not the only magic
+ * number in use, and it might be better to check the total_sectors
+ * number itself for a valid size. This algorithm is safe, but may
+ * do unnecessary disk reads if an different magic number is used.
+ */
+
+ /* the AA, 55 signature sometime doesn't work at all, It should
+ be done as DOS */
+
+ if ((disk_buffer[0] == 0x69 || disk_buffer[0] == 0xE9 ||
+ (disk_buffer[0] == 0xEB && disk_buffer[2] == 0x90)) &&
+ (disk_buffer[21] & 0xF0) == 0xF0 ) {
+ /* read total number of sectors, and thus deduce density
+ */
+ total_sectors = disk_buffer [20] * 256 + disk_buffer [19];
+ } else {
+ note_trace2 (GFI_VERBOSE,
+ "not a DOS boot block: magic = %02x %02x",
+ disk_buffer[510], disk_buffer[511]);
+
+ /* probe disk by reading last sectors for each size
+ * (in order) until the read fails.
+ */
+ total_sectors = 0;
+ for (probe=probelist; *probe != 0; probe++) {
+ transferred_size = nt_floppy_read(drive,
+ (*probe)*PC_BYTES_PER_SECTOR,
+ PC_BYTES_PER_SECTOR,
+ disk_buffer
+ );
+ if (transferred_size != PC_BYTES_PER_SECTOR)
+ break; /* out of the for loop */
+ total_sectors = (*probe) + 1;
+ }
+ }
+
+ switch (total_sectors)
+ {
+ case 0:
+ note_trace0( GFI_VERBOSE, "total_sectors = 0 - unformatted");
+ flp->media_density = DENSITY_UNKNOWN; /* impossible value */
+ break;
+
+ case 720:
+ case 1440:
+ flp->media_density = DENSITY_LOW;
+ break;
+
+ case 2400:
+ case 2880:
+ flp->media_density = DENSITY_HIGH;
+ break;
+
+ case 5760:
+ flp->media_density = DENSITY_EXTENDED;
+ break;
+
+ default:
+ note_trace1 (GFI_VERBOSE,
+ "total sectors = %d? Assume high density",
+ total_sectors);
+ flp->media_density = DENSITY_HIGH;
+ break;
+ }
+
+#ifndef PROD
+ note_trace1 (GFI_VERBOSE, "guess_media_density %d",
+ flp->media_density);
+ if (flp->media_density != density_state) {
+ note_trace0 (GFI_VERBOSE,
+ "media & controller densities are incompatible!\n");
+ }
+#endif /* !PROD */
+ return(flp->media_density);
+}
+
+/********************************************************/
+
+/*
+ * dos_offset() calculates the offset in bytes of the required sector
+ * from the start of the nt virtual disk file for a given track
+ * and sector. This maps the floppy data onto the nt file in an
+ * interleaved format with the data for each head adjacent for a
+ * given cylinder.
+ */
+
+int
+dos_offset IFN4(FLP, flp, UTINY, cyl, UTINY, hd, UTINY, sec)
+{
+ int ret;
+
+ ret = (((cyl * PC_HEADS_PER_DISKETTE * flp->secs_per_trk)
+ + (hd * flp->secs_per_trk)
+ + (sec - 1)) * PC_BYTES_PER_SECTOR) ;
+
+ note_trace1(GFI_VERBOSE, "Dos offset %d", ret);
+ return (ret);
+}
+
+/********************************************************/
+
+/*
+ * dos_compatible() returns TRUE if the command block's
+ * cylinder/head/sector is DOS-compatible
+ */
+
+BOOL
+dos_compatible IFN5(FLP, flp, UTINY, cyl, UTINY, hd, UTINY, sec, UTINY, n)
+{
+ BOOL ret;
+
+ ret = ((hd <= PC_HEADS_PER_DISKETTE)
+ && (cyl < flp->trks_per_disk)
+ && (sec <= flp->secs_per_trk)
+ && (n == PC_N_VALUE));
+
+ return (ret);
+}
+
+/********************************************************/
+
+VOID
+set_floppy_parms IFN1(FLP, flp)
+{
+ int index = flp->flop_type + density_state;
+
+ flp->secs_per_trk =
+ floppy_tksc [index].secs_per_trk;
+
+ flp->trks_per_disk =
+ floppy_tksc [index].trks_per_disk;
+
+ flp->max_track = flp->trks_per_disk - 1;
+ note_trace2(GFI_VERBOSE, "set_floppy_parms: secs_per_trk %d, trks_per_disk %d", flp->secs_per_trk, flp->trks_per_disk);
+
+}
+
+/********************************************************/
+
+
+#ifndef PROD
+VOID nt_rflop_break(VOID)
+{
+}
+
+#endif
+
+VOID update_chrn (
+FLP flp,
+UTINY mt,
+UTINY eot,
+UTINY sector_count
+)
+{
+ UTINY new_sector;
+
+#ifndef PROD
+ if (flp->C == break_cylinder &&
+ flp->H == break_head &&
+ flp->R == break_sector)
+ nt_rflop_break();
+#endif
+
+ new_sector = flp->R + sector_count - 1;
+ if (new_sector > eot && mt != 0) {
+ flp->H = 1;
+ new_sector >>= 1;
+ }
+ flp->R = (new_sector == eot) ? 1 : new_sector + 1;
+
+ if (mt != 0 && new_sector == eot) {
+ if(flp->H == 1)
+ flp->C++;
+ flp->H ^= 1;
+ }
+ else {
+ if (new_sector == eot)
+ flp->C++;
+ }
+}