2 * FLOPPY.C - NEC-765/8272A floppy device driver
3 * written by Rex Jolliff
4 * with help from various other sources, including but not limited to:
5 * Art Baker's NT Device Driver Book, Linux Source, and the internet.
7 * Modification History:
8 * 08/19/98 RJJ Created.
9 * 01/31/01 PJS Heavy rewrite, most of code thrown out
12 * FIXME: get it working
13 * FIXME: add support for DMA hardware
14 * FIXME: should add support for floppy tape/zip devices
17 #include <ddk/ntddk.h>
24 FLOPPY_CONTROLLER_PARAMETERS ControllerParameters[FLOPPY_MAX_CONTROLLERS] =
26 {0x03f0, 6, 2, Latched}
27 // {0x0370, 6, 6, Latched}
30 const FLOPPY_MEDIA_TYPE MediaTypes[] = {
31 { 0x02, 80, 2, 18, 512 },
36 FloppyCreateController(PDRIVER_OBJECT DriverObject,
37 PFLOPPY_CONTROLLER_PARAMETERS ControllerParameters,
40 PCONTROLLER_OBJECT ControllerObject;
41 PFLOPPY_CONTROLLER_EXTENSION ControllerExtension;
42 PFLOPPY_DEVICE_EXTENSION DeviceExtension;
43 UNICODE_STRING DeviceName;
45 PDEVICE_OBJECT DeviceObject;
46 PCONFIGURATION_INFORMATION ConfigInfo;
47 LARGE_INTEGER Timeout;
50 PCONFIGURATION_INFORMATION Config;
51 DEVICE_DESCRIPTION DeviceDescription;
57 /* FIXME: Register port ranges with HAL */
58 MappedIrq = HalGetInterruptVector(Isa,
60 ControllerParameters->Vector,
61 ControllerParameters->Vector,
65 /* Create controller object for FDC */
66 ControllerObject = IoCreateController(sizeof(FLOPPY_CONTROLLER_EXTENSION));
67 if (ControllerObject == NULL)
69 DPRINT("Could not create controller object for controller %d\n",
74 /* FIXME: fill out controller data */
75 ControllerExtension = (PFLOPPY_CONTROLLER_EXTENSION)
76 ControllerObject->ControllerExtension;
77 ControllerExtension->Number = Index;
78 ControllerExtension->PortBase = ControllerParameters->PortBase;
79 ControllerExtension->Vector = MappedIrq;
80 KeInitializeEvent( &ControllerExtension->Event, SynchronizationEvent, FALSE );
81 ControllerExtension->Device = 0; // no active device
82 ControllerExtension->Irp = 0; // no active IRP
83 /* Initialize the spin lock in the controller extension */
84 KeInitializeSpinLock(&ControllerExtension->SpinLock);
85 ControllerExtension->IsrState = FloppyIsrDetect;
86 ControllerExtension->DpcState = FloppyDpcDetect;
88 /* Register an interrupt handler for this controller */
89 Status = IoConnectInterrupt(&ControllerExtension->Interrupt,
92 &ControllerExtension->SpinLock,
96 ControllerParameters->InterruptMode,
100 if (!NT_SUCCESS(Status))
102 DPRINT("Could not Connect Interrupt %d\n",
103 ControllerExtension->Vector);
104 goto controllercleanup;
108 /* setup DMA stuff for controller */
109 DeviceDescription.Version = DEVICE_DESCRIPTION_VERSION;
110 DeviceDescription.Master = FALSE;
111 DeviceDescription.ScatterGather = FALSE;
112 DeviceDescription.AutoInitialize = FALSE;
113 DeviceDescription.Dma32BitAddress = FALSE;
114 DeviceDescription.DmaChannel = ControllerParameters->DmaChannel;
115 DeviceDescription.InterfaceType = Isa;
116 // DeviceDescription.DmaWidth = Width8Bits;
117 ControllerExtension->AdapterObject = HalGetAdapter( &DeviceDescription, &MaxMapRegs );
118 if( ControllerExtension->AdapterObject == NULL )
120 DPRINT1( "Could not get adapter object\n" );
121 goto interruptcleanup;
125 /* Check for each possible drive and create devices for them */
126 for (DriveIdx = 0; DriveIdx < FLOPPY_MAX_DRIVES; DriveIdx++)
128 /* FIXME: try to identify the drive */
129 /* FIXME: create a device if it's there */
133 /* FIXME: Let's assume one drive and one controller for the moment */
134 RtlInitUnicodeStringFromLiteral(&DeviceName, L"\\Device\\Floppy0");
135 Status = IoCreateDevice(DriverObject,
136 sizeof(FLOPPY_DEVICE_EXTENSION),
139 FILE_REMOVABLE_MEDIA | FILE_FLOPPY_DISKETTE,
142 if (!NT_SUCCESS(Status))
144 goto interruptcleanup;
146 DeviceExtension = (PFLOPPY_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
147 DeviceExtension->DriveSelect = 0;
148 DeviceExtension->Controller = ControllerObject;
149 DeviceExtension->MediaType = ~0;
150 ControllerExtension->MotorOn = ~0;
152 ControllerExtension->Device = DeviceObject;
153 KeInitializeDpc( &ControllerExtension->MotorSpinupDpc,
154 FloppyMotorSpinupDpc,
156 KeInitializeDpc( &ControllerExtension->MotorSpindownDpc,
157 FloppyMotorSpindownDpc,
159 KeInitializeTimer( &ControllerExtension->SpinupTimer );
160 IoInitializeDpcRequest( DeviceObject, FloppyDpc );
161 // reset controller and wait for interrupt
162 DPRINT( "Controller Off\n" );
163 FloppyWriteDOR( ControllerExtension->PortBase, 0 );
164 // let controller reset for at least FLOPPY_RESET_TIME
165 KeStallExecutionProcessor( FLOPPY_RESET_TIME );
166 DPRINT( "Controller On\n" );
167 FloppyWriteDOR( ControllerExtension->PortBase, FLOPPY_DOR_ENABLE | FLOPPY_DOR_DMA );
168 // wait for interrupt now
169 Timeout.QuadPart = -10000000;
170 Status = KeWaitForSingleObject( &ControllerExtension->Event,
175 if( Status != STATUS_WAIT_0 )
177 DPRINT1( "Error: KeWaitForSingleObject returned: %x\n", Status );
180 // set for high speed mode
181 // FloppyWriteCCNTL( ControllerExtension->PortBase, FLOPPY_CCNTL_1MBIT );
183 // ok, so we have an FDC, now check for drives
184 // aparently the sense drive status command does not work on any FDC I can find
185 // so instead we will just have to assume a 1.44 meg 3.5 inch floppy. At some
186 // point we should get the bios disk parameters passed in to the kernel at boot
187 // and stored in the HARDWARE registry key for us to pick up here.
189 // turn on motor, wait for spinup time, and recalibrate the drive
190 FloppyWriteDOR( ControllerExtension->PortBase, FLOPPY_DRIVE0_ON );
191 Timeout.QuadPart = FLOPPY_MOTOR_SPINUP_TIME;
192 KeDelayExecutionThread( KernelMode, FALSE, &Timeout );
193 DPRINT( "MSTAT: %2x\n", FloppyReadMSTAT( ControllerExtension->PortBase ) );
194 FloppyWriteDATA( ControllerExtension->PortBase, FLOPPY_CMD_RECAL );
195 DPRINT( "MSTAT: %2x\n", FloppyReadMSTAT( ControllerExtension->PortBase ) );
196 KeStallExecutionProcessor( 10000 );
197 FloppyWriteDATA( ControllerExtension->PortBase, 0 ); // drive select
198 Timeout.QuadPart = FLOPPY_RECAL_TIMEOUT;
199 Status = KeWaitForSingleObject( &ControllerExtension->Event,
204 if( Status != STATUS_WAIT_0 )
206 DPRINT1( "Error: KeWaitForSingleObject returned: %x\n", Status );
209 if( ControllerExtension->St0 != FLOPPY_ST0_SEEKGD )
211 DbgPrint( "Floppy: error recalibrating drive, ST0: %2x\n", (DWORD)ControllerExtension->St0 );
212 goto interruptcleanup;
214 DeviceExtension->Cyl = 0;
215 // drive is good, and it is now on track 0, turn off the motor
216 FloppyWriteDOR( ControllerExtension->PortBase, FLOPPY_DOR_ENABLE | FLOPPY_DOR_DMA );
217 /* Initialize the device */
218 DeviceObject->Flags = DeviceObject->Flags | DO_DIRECT_IO;
219 DeviceObject->AlignmentRequirement = FILE_512_BYTE_ALIGNMENT;
220 // change state machine, no interrupt expected
221 ControllerExtension->IsrState = FloppyIsrUnexpected;
222 Config = IoGetConfigurationInformation();
223 Config->FloppyCount++;
224 // call IoAllocateAdapterChannel, and wait for execution routine to be given the channel
226 Status = IoAllocateAdapterChannel( ControllerExtension->AdapterObject,
228 0x3000/PAGE_SIZE, // max track size is 12k
229 FloppyAdapterControl,
230 ControllerExtension );
231 if( !NT_SUCCESS( Status ) )
233 DPRINT1( "Error: IoAllocateAdapterChannel returned %x\n", Status );
234 goto interruptcleanup;
237 Status = KeWaitForSingleObject( &ControllerExtension->Event,
243 if( Status != STATUS_WAIT_0 )
245 DPRINT1( "Error: KeWaitForSingleObject returned: %x\n", Status );
246 goto interruptcleanup;
248 // Ok, we own the adapter object, from now on we can just IoMapTransfer, and not
249 // bother releasing the adapter ever.
251 DPRINT( "Floppy drive initialized\n" );
255 IoDeleteDevice( DeviceObject );
257 IoDisconnectInterrupt(ControllerExtension->Interrupt);
259 // turn off controller
260 FloppyWriteDOR( ControllerExtension->PortBase, 0 );
261 IoDeleteController(ControllerObject);
266 IO_ALLOCATION_ACTION STDCALL
267 FloppyExecuteSpindown(PDEVICE_OBJECT DeviceObject,
269 PVOID MapRegisterbase,
272 PFLOPPY_CONTROLLER_EXTENSION ControllerExtension= (PFLOPPY_CONTROLLER_EXTENSION)Context;
274 // turn off motor, and return
275 DPRINT( "Spinning down motor\n" );
276 ControllerExtension->MotorOn = ~0;
277 FloppyWriteDOR( ControllerExtension->PortBase,
278 FLOPPY_DOR_ENABLE | FLOPPY_DOR_DMA );
279 return DeallocateObject;
282 IO_ALLOCATION_ACTION STDCALL
283 FloppyExecuteReadWrite(PDEVICE_OBJECT DeviceObject,
285 PVOID MapRegisterbase,
288 PFLOPPY_DEVICE_EXTENSION DeviceExtension = (PFLOPPY_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
289 PFLOPPY_CONTROLLER_EXTENSION ControllerExtension = (PFLOPPY_CONTROLLER_EXTENSION)DeviceExtension->Controller->ControllerExtension;
290 LARGE_INTEGER Timeout;
291 BOOLEAN WriteToDevice;
292 DWORD Cyl, Sector, Head;
293 PIO_STACK_LOCATION Stk;
296 ControllerExtension->Irp = Irp = (PIRP)Context;
297 Stk = IoGetCurrentIrpStackLocation( Irp );
298 ControllerExtension->Device = DeviceObject;
299 Timeout.QuadPart = FLOPPY_MOTOR_SPINUP_TIME;
300 DPRINT( "FloppyExecuteReadWrite()\n" );
302 WriteToDevice = Stk->MajorFunction == IRP_MJ_WRITE ? TRUE : FALSE;
303 // verify drive is spun up and selected
304 if( ControllerExtension->MotorOn != DeviceExtension->DriveSelect )
306 // turn on and select drive, and allow it to spin up
307 // FloppyMotorSpinupDpc will restart this operation once motor is spun up
308 DPRINT( "Motor not on, turning it on now\n" );
309 FloppyWriteDOR( ControllerExtension->PortBase,
310 DeviceExtension->DriveSelect ? FLOPPY_DRIVE1_ON : FLOPPY_DRIVE0_ON );
311 // cancel possible spindown timer first
312 KeCancelTimer( &ControllerExtension->SpinupTimer );
313 KeSetTimerEx( &ControllerExtension->SpinupTimer,
316 &ControllerExtension->MotorSpinupDpc );
320 Timeout.QuadPart = FLOPPY_MOTOR_SPINDOWN_TIME;
321 // motor is already spinning, so reset the spindown timer
322 KeCancelTimer( &ControllerExtension->SpinupTimer );
323 KeSetTimer( &ControllerExtension->SpinupTimer,
325 &ControllerExtension->MotorSpindownDpc );
327 // verify media content
328 if( FloppyReadDIR( ControllerExtension->PortBase ) & FLOPPY_DI_DSKCHNG )
330 // No disk is in the drive
331 DPRINT( "No disk is in the drive\n" );
332 Irp->IoStatus.Status = STATUS_NO_MEDIA;
333 Irp->IoStatus.Information = 0;
334 IoCompleteRequest( Irp, 0 );
335 return DeallocateObject;
337 if( DeviceExtension->MediaType == ~0 )
339 // media is in disk, but we have not yet detected what kind it is,
341 // First, we need to recalibrate the drive though
342 ControllerExtension->IsrState = FloppyIsrRecal;
343 DPRINT( "Recalibrating drive\n" );
344 KeStallExecutionProcessor( 1000 );
345 FloppyWriteDATA( ControllerExtension->PortBase, FLOPPY_CMD_RECAL );
346 KeStallExecutionProcessor( 1000 );
347 FloppyWriteDATA( ControllerExtension->PortBase, DeviceExtension->DriveSelect );
350 // looks like we have media in the drive.... do the read
351 // first, calculate geometry for read
352 Sector = Stk->Parameters.Read.ByteOffset.u.LowPart / MediaTypes[DeviceExtension->MediaType].BytesPerSector;
353 // absolute sector right now
354 Cyl = Sector / MediaTypes[DeviceExtension->MediaType].SectorsPerTrack;
355 DPRINT( "Sector = %x, Offset = %x, Cyl = %x, Heads = %x MediaType = %x\n", Sector, Stk->Parameters.Read.ByteOffset.u.LowPart, (DWORD)Cyl, (DWORD)MediaTypes[DeviceExtension->MediaType].Heads, (DWORD)DeviceExtension->MediaType );
356 Head = Cyl % MediaTypes[DeviceExtension->MediaType].Heads;
357 DPRINT( "Head = %2x\n", Head );
358 // convert absolute cyl to relative
359 Cyl /= MediaTypes[DeviceExtension->MediaType].Heads;
360 // convert absolute sector to relative
361 Sector %= MediaTypes[DeviceExtension->MediaType].SectorsPerTrack;
362 Sector++; // track relative sector numbers are 1 based, not 0 based
363 DPRINT( "Cyl = %2x, Head = %2x, Sector = %2x\n", Cyl, Head, Sector );
365 // seek if we need to seek
366 if( DeviceExtension->Cyl != Cyl )
368 DPRINT( "Seeking...\n" );
369 ControllerExtension->IsrState = FloppyIsrDetect;
370 ControllerExtension->DpcState = FloppySeekDpc;
371 FloppyWriteDATA( ControllerExtension->PortBase, FLOPPY_CMD_SEEK );
372 KeStallExecutionProcessor( 100 );
373 FloppyWriteDATA( ControllerExtension->PortBase, DeviceExtension->DriveSelect );
374 KeStallExecutionProcessor( 100 );
375 FloppyWriteDATA( ControllerExtension->PortBase, Cyl );
378 //set up DMA and issue read command
379 Length = MediaTypes[DeviceExtension->MediaType].SectorsPerTrack - Sector + 1;
380 // number of sectors untill end of track
381 Length *= 512; // convert to bytes
382 if( Length > Stk->Parameters.Read.Length )
383 Length = Stk->Parameters.Read.Length;
384 DPRINT( "Sector: %d, Length: %d\n", Sector, Length );
385 ControllerExtension->TransferLength = Length;
386 IoMapTransfer( ControllerExtension->AdapterObject,
388 ControllerExtension->MapRegisterBase,
389 Irp->Tail.Overlay.DriverContext[0], // current va
392 ControllerExtension->IsrState = FloppyIsrReadWrite;
393 ControllerExtension->DpcState = FloppyDpcReadWrite;
395 FloppyWriteDATA( ControllerExtension->PortBase, WriteToDevice ? FLOPPY_CMD_WRITE : FLOPPY_CMD_READ );
396 KeStallExecutionProcessor( 100 );
397 FloppyWriteDATA( ControllerExtension->PortBase, ( Head << 2 ) | DeviceExtension->DriveSelect );
398 KeStallExecutionProcessor( 100 );
399 FloppyWriteDATA( ControllerExtension->PortBase, Cyl );
400 KeStallExecutionProcessor( 100 );
401 FloppyWriteDATA( ControllerExtension->PortBase, Head );
402 KeStallExecutionProcessor( 100 );
403 FloppyWriteDATA( ControllerExtension->PortBase, Sector );
404 KeStallExecutionProcessor( 100 );
405 FloppyWriteDATA( ControllerExtension->PortBase, MediaTypes[DeviceExtension->MediaType].SectorSizeCode );
406 KeStallExecutionProcessor( 100 );
407 FloppyWriteDATA( ControllerExtension->PortBase, MediaTypes[DeviceExtension->MediaType].SectorsPerTrack );
408 KeStallExecutionProcessor( 100 );
409 FloppyWriteDATA( ControllerExtension->PortBase, 0 );
410 KeStallExecutionProcessor( 100 );
411 FloppyWriteDATA( ControllerExtension->PortBase, 0xFF );
413 // eventually, the FDC will interrupt and we will read results then
418 FloppyDispatchOpenClose(PDEVICE_OBJECT DeviceObject,
421 DPRINT("FloppyDispatchOpenClose\n");
422 return STATUS_SUCCESS;
426 FloppyDispatchReadWrite(PDEVICE_OBJECT DeviceObject,
429 PFLOPPY_DEVICE_EXTENSION DeviceExtension = (PFLOPPY_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
430 PFLOPPY_CONTROLLER_EXTENSION ControllerExtension = (PFLOPPY_CONTROLLER_EXTENSION)DeviceExtension->Controller->ControllerExtension;
431 PIO_STACK_LOCATION Stk = IoGetCurrentIrpStackLocation( Irp );
434 if( Stk->Parameters.Read.ByteOffset.u.HighPart )
436 Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
437 Irp->IoStatus.Information = 0;
438 IoCompleteRequest( Irp, 1 );
439 return STATUS_INVALID_PARAMETER;
441 // store currentva in drivercontext
442 Irp->Tail.Overlay.DriverContext[0] = MmGetMdlVirtualAddress( Irp->MdlAddress );
443 DPRINT( "FloppyDispatchReadWrite: offset = %x, length = %x, va = %x\n",
444 Stk->Parameters.Read.ByteOffset.u.LowPart,
445 Stk->Parameters.Read.Length,
446 Irp->Tail.Overlay.DriverContext[0] );
448 Irp->IoStatus.Status = STATUS_SUCCESS;
449 Irp->IoStatus.Information = Stk->Parameters.Read.Length;
450 IoMarkIrpPending( Irp );
451 KeRaiseIrql( DISPATCH_LEVEL, &oldlvl );
452 IoAllocateController( ((PFLOPPY_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->Controller,
454 FloppyExecuteReadWrite,
456 KeLowerIrql( oldlvl );
457 DPRINT( "oldlvl = %x\n", oldlvl );
458 return STATUS_PENDING;
461 IO_ALLOCATION_ACTION STDCALL
462 FloppyAdapterControl(PDEVICE_OBJECT DeviceObject,
464 PVOID MapRegisterBase,
467 PFLOPPY_CONTROLLER_EXTENSION ControllerExtension = (PFLOPPY_CONTROLLER_EXTENSION)Context;
469 // just set the event, and return KeepObject
471 ControllerExtension->MapRegisterBase = MapRegisterBase;
472 KeSetEvent( &ControllerExtension->Event, 0, FALSE );
477 FloppyDispatchDeviceControl(PDEVICE_OBJECT DeviceObject,
480 PIO_STACK_LOCATION IrpStack;
481 ULONG ControlCode, InputLength, OutputLength;
484 DPRINT("FloppyDispatchDeviceControl\n");
486 IrpStack = IoGetCurrentIrpStackLocation(Irp);
487 ControlCode = IrpStack->Parameters.DeviceIoControl.IoControlCode;
488 InputLength = IrpStack->Parameters.DeviceIoControl.InputBufferLength;
489 OutputLength = IrpStack->Parameters.DeviceIoControl.OutputBufferLength;
493 case IOCTL_DISK_GET_DRIVE_GEOMETRY:
494 if (OutputLength < sizeof(DISK_GEOMETRY))
496 Status = STATUS_INVALID_PARAMETER;
500 PDISK_GEOMETRY Geometry = Irp->AssociatedIrp.SystemBuffer;
501 // FIXME: read the first sector of the diskette
502 Geometry->MediaType = F3_1Pt44_512;
503 Geometry->Cylinders.QuadPart = 80;
504 Geometry->TracksPerCylinder = 2 * 18;
505 Geometry->SectorsPerTrack = 18;
506 Geometry->BytesPerSector = 512;
507 Status = STATUS_SUCCESS;
508 Irp->IoStatus.Information = sizeof(DISK_GEOMETRY);
512 Status = STATUS_INVALID_DEVICE_REQUEST;
514 Irp->IoStatus.Status = Status;
515 IoCompleteRequest(Irp, NT_SUCCESS(Status) ? IO_DISK_INCREMENT : IO_NO_INCREMENT);
522 * This function initializes the driver, locates and claims
523 * hardware resources, and creates various NT objects needed
524 * to process I/O requests.
530 * IN PDRIVER_OBJECT DriverObject System allocated Driver Object
532 * IN PUNICODE_STRING RegistryPath Name of registry driver service
539 DriverEntry(IN PDRIVER_OBJECT DriverObject,
540 IN PUNICODE_STRING RegistryPath)
542 DPRINT("Floppy driver\n");
544 /* Export other driver entry points... */
545 DriverObject->MajorFunction[IRP_MJ_CREATE] = FloppyDispatchOpenClose;
546 DriverObject->MajorFunction[IRP_MJ_CLOSE] = FloppyDispatchOpenClose;
547 DriverObject->MajorFunction[IRP_MJ_READ] = FloppyDispatchReadWrite;
548 DriverObject->MajorFunction[IRP_MJ_WRITE] = FloppyDispatchReadWrite;
549 DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] =
550 FloppyDispatchDeviceControl;
552 /* Try to detect controller and abort if it fails */
553 if (!FloppyCreateController(DriverObject,
554 &ControllerParameters[0],
557 DPRINT("Could not find floppy controller\n");
558 return STATUS_NO_SUCH_DEVICE;
561 return STATUS_SUCCESS;