Home » fdrawcmd » docs » IOCTL codes

fdrawcmd.sys


IOCTL Codes

Driver Version

The following code can be sent to either the driver's \\.\fdrawcmd device or the regular \\.\fdrawX devices:

IOCTL_FDRAWCMD_GET_VERSION get fdrawcmd.sys driver version

Controller Commands

The following map directly to floppy controller commands. In many cases the input buffer matches the data expected by the controller for each command, and the output buffer supplies an appropriate result.

IOCTL_FDCMD_CONFIGURE configure controller parameters
IOCTL_FDCMD_DUMPREG dump controller registers
IOCTL_FDCMD_FORMAT_TRACK format track
IOCTL_FDCMD_LOCK lock settings
IOCTL_FDCMD_PART_ID identify controller part id
IOCTL_FDCMD_PERPENDICULAR_MODE set perpendicular mode
IOCTL_FDCMD_READ_DATA read sectors
IOCTL_FDCMD_READ_DELETED_DATA read deleted sectors
IOCTL_FDCMD_READ_ID read next id header
IOCTL_FDCMD_READ_TRACK raw track read
IOCTL_FDCMD_RECALIBRATE recalibrate drive
IOCTL_FDCMD_RELATIVE_SEEK relative seek
IOCTL_FDCMD_SEEK absolute seek
IOCTL_FDCMD_SENSE_DRIVE_STATUS returns status register 3
IOCTL_FDCMD_SENSE_INT_STATUS sense interrupt status
IOCTL_FDCMD_SPECIFY set drive specification
IOCTL_FDCMD_VERIFY verify sectors
IOCTL_FDCMD_VERSION check for enhanced controller
IOCTL_FDCMD_WRITE_DATA write sectors
IOCTL_FDCMD_WRITE_DELETED_DATA write deleted sectors
IOCTL_FDCMD_FORMAT_AND_WRITE format and write in a single pass

Support for some commands may be missing from older hardware. Please consult the controller documentation (PDF) for further details.

Controller Functions

The following control other aspects of the controller:

IOCTL_FD_GET_RESULT read result bytes for last command
IOCTL_FD_GET_FDC_INFO get controller information
IOCTL_FD_GET_REMAIN_COUNT get remaining data count
IOCTL_FD_LOCK_FDC acquire exclusive controller access
IOCTL_FD_MOTOR_ON spin up drive motor
IOCTL_FD_MOTOR_OFF turn drive motor off
IOCTL_FD_RAW_READ_TRACK 2-drive raw track read
IOCTL_FD_RESET reset controller
IOCTL_FD_SET_MOTOR_TIMEOUT set motor idle timeout
IOCTL_FD_SET_DATA_RATE set data rate
IOCTL_FD_SET_DISK_CHECK control disk presence check
IOCTL_FD_SET_HEAD_SETTLE_TIME set head settle time
IOCTL_FD_SET_SECTOR_OFFSET sync to sector offset
IOCTL_FD_SET_SHORT_WRITE set short write length
IOCTL_FD_UNLOCK_FDC release previously locked controller
IOCTL_FD_WAIT_INDEX sync to index hole
IOCTL_FD_CHECK_DISK check for disk in drive
IOCTL_FD_GET_TRACK_TIME measure time for one disk rotation

Special Functions

This section covers compound operations that are either timing-sensitive or not easily achieved using other functionality provided by the driver.

IOCTL_FD_SCAN_TRACK scan layout of current track
IOCTL_FD_TIMED_SCAN_TRACK scan layout with times

IOCTL_FDRAWCMD_GET_VERSION

Description: Get the fdrawcmd.sys driver version

Input: none
Output: DWORD

The 32-bit value returned holds the dotted version number, from MSB to LSB. i.e. 0x01000013 is version 1.0.0.19.


IOCTL_FDCMD_CONFIGURE

Description: Configure controller parameters

Input: FD_CONFIGURE_PARAMS
Output: FD_CMD_RESULT (optional)


IOCTL_FDCMD_DUMPREG

Description: Controller register dump

Input: none
Output: FD_DUMPREG_RESULT


IOCTL_FDCMD_FORMAT_AND_WRITE

Description: Format track and write sector data in a single pass

Input: FD_FORMAT_PARAMS (variable size)
Output: FD_CMD_RESULT (optional)

The size of the input buffer depends on the number of sectors in the track. The FD_FORMAT_PARAMS structure should be immediately followed by a list of FD_ID_HEADER structures, with each header followed by the data to be written to its data field.

This command requires an 82078 or later FDC, with unpredictable results if used on older controllers. Use IOCTL_FD_GET_FDC_INFO to determine whether a suitable controller version is present.


IOCTL_FDCMD_FORMAT_TRACK

Description: Format track

Input: FD_FORMAT_PARAMS (variable size)
Output: FD_CMD_RESULT (optional)

The size of the input buffer depends on the number of sectors in the track. The FD_FORMAT_PARAMS structure should be immediately followed by a block of FD_ID_HEADER structures.


IOCTL_FDCMD_LOCK

Description: Protect configure settings from reset

Input: FD_LOCK_PARAMS
Output: FD_LOCK_RESULT (optional)


IOCTL_FDCMD_PART_ID

Description: Check for an enhanced floppy controller

Input: none
Output: BYTE


IOCTL_FDCMD_PERPENDICULAR_MODE

Description: Set a drive to use perpendicular mode

Input: FD_PERPENDICULAR_PARAMS
Output: FD_CMD_RESULT (optional)


IOCTL_FDCMD_READ_DATA

Description: Read sectors

Input: FD_READ_WRITE_PARAMS
Output: Buffer for received data

For a typical read, set the input structure as follows:

Use IOCTL_FD_GET_RESULT to determine the failure point in multi-sector reads. The sector value will hold the sector number with the problem.


IOCTL_FDCMD_READ_DELETED_DATA

Description: Read deleted sectors

Input: FD_READ_WRITE_PARAMS
Output: Buffer for received data


IOCTL_FDCMD_READ_ID

Description: Read the next encountered ID header

Input: FD_READ_ID_PARAMS
Output: FD_CMD_RESULT


IOCTL_FDCMD_READ_TRACK

Description: Raw track read

Input: FD_READ_WRITE_PARAMS
Output: Buffer for received data


IOCTL_FDCMD_RECALIBRATE

Description: Recalibrate drive head position

Input: none
Output: FD_INTERRUPT_STATUS (optional)


IOCTL_FDCMD_RELATIVE_SEEK

Description: Seek relative to current head location

Input: FD_RELATIVE_SEEK_PARAMS
Output: FD_INTERRUPT_STATUS (optional)


IOCTL_FDCMD_SEEK

Description: Seek head to absolute location

Input: FD_SEEK_PARAMS
Output: FD_INTERRUPT_STATUS (optional)

For backwards compatibility, the input buffer may also be a single BYTE holding the cylinder number. When used, the head value is taken to be 0.


IOCTL_FDCMD_SENSE_DRIVE_STATUS

Description: Read status register 3

Input: FD_SENSE_PARAMS
Output: FD_DRIVE_STATUS


IOCTL_FDCMD_SENSE_INT_STATUS

Description: Sense interrupt status

Input: none
Output: FD_INTERRUPT_STATUS


IOCTL_FDCMD_SPECIFY

Description: Set drive specification

Input: FD_SPECIFY_PARAMS
Output: FD_CMD_RESULT (optional)


IOCTL_FDCMD_VERIFY

Description: Verify sectors

Input: FD_READ_WRITE_PARAMS
Output: FD_CMD_RESULT (optional)

Input parameters are the same as IOCTL_FDCMD_READ_DATA, except the eot value holds the number of sectors to verify.


IOCTL_FDCMD_VERSION

Description: Read controller version

Input: none
Output: BYTE

To read the driver version, use IOCTL_FDRAWCMD_GET_VERSION instead.


IOCTL_FDCMD_WRITE_DATA

Description: Write normal sectors

Input: FD_READ_WRITE_PARAMS
Output: Buffer containing data to write

See IOCTL_FDCMD_READ_DATA for typical input parameter values.


IOCTL_FDCMD_WRITE_DELETED_DATA

Description: Write deleted sectors

Input: FD_READ_WRITE_PARAMS
Output: Buffer containing data to write


IOCTL_FD_CHECK_DISK

Description: Checks whether a disk is present in the drive

Input: none
Output: none (only return status)

The request fails with ERROR_NO_MEDIA_IN_DRIVE if no disk is present, otherwise it completes successfully.


IOCTL_FD_GET_FDC_INFO

Description: Get controller information

Input: none
Output: FD_FDC_INFO


IOCTL_FD_GET_REMAIN_COUNT

Description: Get the remain count for the last command

Input: none
Output: DWORD

This function retrieves the remaining data count for the last command. In normal cases this will be zero, but may be non-zero if the command was terminated abnormally.


IOCTL_FD_GET_RESULT

Description: Get the result bytes from last command

Input: none
Output: FD_CMD_RESULT


IOCTL_FD_GET_TRACK_TIME

Description: Measure the time for one disk rotation

Input: none
Output: DWORD

The value returned is the time for a single disk rotation, in microseconds. The setup and measurement process for each request takes 4 disk rotations.


IOCTL_FD_LOCK_FDC

Description: Acquire exclusive controller access

Input: none
Output: none

This function allows exclusive access to the controller to be held even when the drive is idle. When locked, attempts to use another drive on the same controller will fail.

The drive lock can be acquired multiple times, but each must have a corresponding IOCTL_FD_UNLOCK_FDC.


IOCTL_FD_MOTOR_OFF

Description: Turn motor off

Input: none
Output: none

The main use of this function is to allow immediate access to another drive on the same controller, without waiting for the motor-off idle timeout to expire.

Like other requests, it is only processed when the previous request has completed. This means you can't use it to interrupt in-progress commands for various copy protection tricks!


IOCTL_FD_MOTOR_ON

Description: Spins up the drive motor

Input: none
Output: none

By default, the drive motor is automatically started before any commands that need it. IOCTL_FD_MOTOR_ON starts the motor and allows a 750ms settle-time before completing the request. If the motor is already running the function completes immediately.

The motor is stopped a short time after the drive becomes idle, and the controller is released for other programs. The default idle timeout is 1 second, but it can be extended using IOCTL_FD_SET_MOTOR_TIMEOUT. Also note that the 750ms spin-up delay does not count against the timeout value.


IOCTL_FD_RAW_READ_TRACK

Decription: Perform a raw track read using 2 drives

Input: FD_RAW_READ_PARAMS
Output: Buffer for received data

This function uses the 2-drive raw track reading technique discovered by Vincent Joguin, as implemented in his Disk2FDI utility. It requires a second drive to be present, containing a 1.44M formatted disk (can be write-protected). The main drive holds the double-density disk to raw-read from.

Before using this function, select the data rate to be used by the read using IOCTL_FD_SET_DATA_RATE. This can be zero for 500Kbps, which will return both clock and data bits from the source disk. A value of two for 250Kbps will return only data or clock bits, depending on how the controller syncs to the data stream on the main drive.

In the function parameters, the flags value should be FD_OPTION_MFM for the PC-formatted disk, and head is the physical head to raw-read from. The size parameter determines how much data to read from the main disk. As the starting point for the read is semi-random, you'll need to read 16-32K to give a long enough bitstream to recover the required information. It may also be necessary to retry if a long enough stream can't be found. Some controllers may not support 32K reading - use IOCTL_FD_GET_REMAIN_COUNT to ensure that the remain count is zero after the read, indicating the controller completed your full request.

500Kbps reading of standard-format Amiga disks is quite reliable, as the original disks are written in a single pass. Most other disks (including PC disks) are formatted in one pass, but have data written to sectors afterwards. This separate writing creates seams in the bitstream where the new writing started and stopped in the bitstream. The noise around these splice points will disturb the controller sync during a raw reads, causing it to return patches of zero bytes instead of the real data. For this reason 500Kbps reading is generally not possible.

250Kbps reading is supported by more controllers, and the only way to read disks containing the splice points described above. This reading is still not a straightforward task, particularly since you only have data bits to work with. Assumptions must be made about patterns in the data, such as the A1A1A1 sequence used by address marks, and it's also necessary to check data CRCs to ensure the stream was complete enough. On top of this you'll also need to reassmble the order of sectors on the track, which is a topic beyond what I can describe here!


IOCTL_FD_RESET

Description: Reset controller

Input: none
Output: none

After the reset, the controller is reinitialised using the previously set data rate and configure/specify parameters. As the controller track register will have also been reset, a recalibrate will also be performed before the next command.


IOCTL_FD_SET_DATA_RATE

Description: Set drive data rate

Input: BYTE
Output: none

The input value should be 0 for 500Kbps (high-density 1.44M), 1 for 300Kbps (double-density for 5.25" drives), 2 for 250Kbps (double-density 720K for 3" and 3.5" drives), or 3 for 1Mbps (extra-density, for 2.88M drives).


IOCTL_FD_SET_DISK_CHECK

Description:Control disk presence checking

Input: BYTE
Output: BYTE (optional)

By default, the driver uses the disk change line to ensure a disk is present before commands that need one. This works well with modern 3.5" drives, but causes problems with older drives (including 3" drives) that lack the change line.

A zero input value disables the disk check, and a non-zero value enables it. You may optionally provide an output buffer to receive the previous value.


IOCTL_FD_SET_MOTOR_TIMEOUT

Description: Set motor-off timeout for idle controller

Input: BYTE
Output: none

The input value should be 1, 2 or 3, to set the timeout for 1-3 seconds.


IOCTL_FD_SCAN_TRACK

Description: Returns a list of sector headers for a given track

Input: FD_SCAN_PARAMS
Output: FD_SCAN_RESULT (variable size)

On success, the count member of FD_SCAN_RESULT contains the number of headers found. A count of zero indicates either an unformatted track or that there were no sectors found matching the current data rate / encoding. The headers are returned in the order they are encountered, beginning from the index hole.

The output buffer must be large enough to hold the scanned number of sector headers, which may not be known in advance. Space for 32 headers will be enough in most situations, but more is recommended when dealing with unknown disks.


IOCTL_FD_SET_SECTOR_OFFSET

Description: Synchronise to a specific sector

Input: FD_SECTOR_OFFSET_PARAMS
Output: none

When dealing with duplicate sector IDs, read/writes would normally apply to the next one the controller encountered. This function allows a specific sector offset to be given, to ensure the correct one is used. Use 0 to synchronise to the first sector, 1 to the second, etc.


IOCTL_FD_SET_HEAD_SETTLE_TIME

Description: Set the head settle time after seeks

Input: BYTE
Output: none

Sets a new head settle time, in milliseconds.


IOCTL_FD_SET_SHORT_WRITE

Description: Set a write threshold before interrupting the controller

Input: FD_SHORT_WRITE_PARAMS
Output: none

To support writing a number of copy-protection schemes, it's necessary to interrupt the controller part way through a write command. This function sets the number of bytes to write before interrupting the controller, and applies to all write and format commands.

To interrupt sector writes, simply set the number of bytes to stop after. If the count is exactly the sector size, the write will terminate before the CRC is written to disk, generating a sector with specific data but a CRC error. Using a smaller count allows around 6K of known data to be written to 8K sectors.

To interrupt formats you need to allow 4 bytes for each sector to write. Remember that stopping immediately after the final sector will be just before the ID header CRC is written, and also before the data address mark and the data field itself.

In both cases, the controller requires a small amount of time between accepting the final data and it being written to disk. The finetune parameter in the structure allows an additional delay (in microseconds) for this data to be processed. My own experiments showed that 86us was required in 250Kbps MFM mode, with an additional 32us for each byte to allow beyond that.


IOCTL_FD_TIMED_SCAN_TRACK

Description: Returns a list of sector headers with times

Input: FD_SCAN_PARAMS
Output: FD_TIMED_SCAN_RESULT (variable size)

Details are as for IOCTL_FD_SCAN_TRACK, except that the results include a time for each sector, relative to the index hole.


IOCTL_FD_UNLOCK_FDC

Decription: Release exclusive access to the controller

Input: none
Output: none

Release the controller lock set with IOCTL_FD_LOCK_FDC


IOCTL_FD_WAIT_INDEX

Description: Synchronise with the index hole on the disk

Input: none
Output: none


Simon Owen
simon@simonowen.com