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 },
35 FloppyCreateController(PDRIVER_OBJECT DriverObject,
36 PFLOPPY_CONTROLLER_PARAMETERS ControllerParameters,
39 PCONTROLLER_OBJECT ControllerObject;
40 PFLOPPY_CONTROLLER_EXTENSION ControllerExtension;
41 PFLOPPY_DEVICE_EXTENSION DeviceExtension;
42 UNICODE_STRING DeviceName;
44 PDEVICE_OBJECT DeviceObject;
45 PCONFIGURATION_INFORMATION ConfigInfo;
46 LARGE_INTEGER Timeout;
49 PCONFIGURATION_INFORMATION Config;
50 DEVICE_DESCRIPTION DeviceDescription;
56 /* FIXME: Register port ranges with HAL */
57 MappedIrq = HalGetInterruptVector(Isa,
59 ControllerParameters->Vector,
60 ControllerParameters->Vector,
64 /* Create controller object for FDC */
65 ControllerObject = IoCreateController(sizeof(FLOPPY_CONTROLLER_EXTENSION));
66 if (ControllerObject == NULL)
68 DPRINT("Could not create controller object for controller %d\n",
73 /* FIXME: fill out controller data */
74 ControllerExtension = (PFLOPPY_CONTROLLER_EXTENSION)
75 ControllerObject->ControllerExtension;
76 ControllerExtension->Number = Index;
77 ControllerExtension->PortBase = ControllerParameters->PortBase;
78 ControllerExtension->Vector = MappedIrq;
79 KeInitializeEvent( &ControllerExtension->Event, SynchronizationEvent, FALSE );
80 ControllerExtension->Device = 0; // no active device
81 ControllerExtension->Irp = 0; // no active IRP
82 /* Initialize the spin lock in the controller extension */
83 KeInitializeSpinLock(&ControllerExtension->SpinLock);
84 ControllerExtension->IsrState = FloppyIsrDetect;
85 ControllerExtension->DpcState = FloppyDpcDetect;
87 /* Register an interrupt handler for this controller */
88 Status = IoConnectInterrupt(&ControllerExtension->Interrupt,
91 &ControllerExtension->SpinLock,
95 ControllerParameters->InterruptMode,
99 if (!NT_SUCCESS(Status))
101 DPRINT("Could not Connect Interrupt %d\n",
102 ControllerExtension->Vector);
103 goto controllercleanup;
107 /* setup DMA stuff for controller */
108 DeviceDescription.Version = DEVICE_DESCRIPTION_VERSION;
109 DeviceDescription.Master = FALSE;
110 DeviceDescription.ScatterGather = FALSE;
111 DeviceDescription.AutoInitialize = FALSE;
112 DeviceDescription.Dma32BitAddress = FALSE;
113 DeviceDescription.DmaChannel = ControllerParameters->DmaChannel;
114 DeviceDescription.InterfaceType = Isa;
115 // DeviceDescription.DmaWidth = Width8Bits;
116 ControllerExtension->AdapterObject = HalGetAdapter( &DeviceDescription, &MaxMapRegs );
117 if( ControllerExtension->AdapterObject == NULL )
119 DPRINT1( "Could not get adapter object\n" );
120 goto interruptcleanup;
124 /* Check for each possible drive and create devices for them */
125 for (DriveIdx = 0; DriveIdx < FLOPPY_MAX_DRIVES; DriveIdx++)
127 /* FIXME: try to identify the drive */
128 /* FIXME: create a device if it's there */
132 /* FIXME: Let's assume one drive and one controller for the moment */
133 RtlInitUnicodeStringFromLiteral(&DeviceName, L"\\Device\\Floppy0");
134 Status = IoCreateDevice(DriverObject,
135 sizeof(FLOPPY_DEVICE_EXTENSION),
138 FILE_REMOVABLE_MEDIA | FILE_FLOPPY_DISKETTE,
141 if (!NT_SUCCESS(Status))
143 goto interruptcleanup;
145 DeviceExtension = (PFLOPPY_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
146 DeviceExtension->DriveSelect = 0;
147 DeviceExtension->Controller = ControllerObject;
148 DeviceExtension->MediaType = ~0;
149 ControllerExtension->MotorOn = ~0;
151 ControllerExtension->Device = DeviceObject;
152 KeInitializeDpc( &ControllerExtension->MotorSpinupDpc,
153 FloppyMotorSpinupDpc,
155 KeInitializeDpc( &ControllerExtension->MotorSpindownDpc,
156 FloppyMotorSpindownDpc,
158 KeInitializeTimer( &ControllerExtension->SpinupTimer );
159 IoInitializeDpcRequest( DeviceObject, FloppyDpc );
160 // reset controller and wait for interrupt
161 DPRINT( "Controller Off\n" );
162 FloppyWriteDOR( ControllerExtension->PortBase, 0 );
163 // let controller reset for at least FLOPPY_RESET_TIME
164 KeStallExecutionProcessor( FLOPPY_RESET_TIME );
165 DPRINT( "Controller On\n" );
166 FloppyWriteDOR( ControllerExtension->PortBase, FLOPPY_DOR_ENABLE | FLOPPY_DOR_DMA );
167 // wait for interrupt now
168 Timeout.QuadPart = -10000000;
169 Status = KeWaitForSingleObject( &ControllerExtension->Event,
174 if( Status != STATUS_WAIT_0 )
176 DPRINT1( "Error: KeWaitForSingleObject returned: %x\n", Status );
179 // set for high speed mode
180 // FloppyWriteCCNTL( ControllerExtension->PortBase, FLOPPY_CCNTL_1MBIT );
181 // ok, so we have an FDC, now check for drives
182 // aparently the sense drive status command does not work on any FDC I can find
183 // so instead we will just have to assume a 1.44 meg 3.5 inch floppy. At some
184 // point we should get the bios disk parameters passed in to the kernel at boot
185 // and stored in the HARDWARE registry key for us to pick up here.
187 // turn on motor, wait for spinup time, and recalibrate the drive
188 FloppyWriteDOR( ControllerExtension->PortBase, FLOPPY_DRIVE0_ON );
189 Timeout.QuadPart = FLOPPY_MOTOR_SPINUP_TIME;
190 KeDelayExecutionThread( KernelMode, FALSE, &Timeout );
191 DPRINT( "MSTAT: %2x\n", FloppyReadMSTAT( ControllerExtension->PortBase ) );
192 FloppyWriteDATA( ControllerExtension->PortBase, FLOPPY_CMD_RECAL );
193 DPRINT( "MSTAT: %2x\n", FloppyReadMSTAT( ControllerExtension->PortBase ) );
194 KeStallExecutionProcessor( 10000 );
195 FloppyWriteDATA( ControllerExtension->PortBase, 0 ); // drive select
196 Timeout.QuadPart = FLOPPY_RECAL_TIMEOUT;
197 Status = KeWaitForSingleObject( &ControllerExtension->Event,
202 if( Status != STATUS_WAIT_0 )
204 DPRINT1( "Error: KeWaitForSingleObject returned: %x\n", Status );
207 if( ControllerExtension->St0 != FLOPPY_ST0_SEEKGD )
209 DbgPrint( "Floppy: error recalibrating drive, ST0: %2x\n", (DWORD)ControllerExtension->St0 );
212 DeviceExtension->Cyl = 0;
213 // drive is good, and it is now on track 0, turn off the motor
214 FloppyWriteDOR( ControllerExtension->PortBase, FLOPPY_DOR_ENABLE | FLOPPY_DOR_DMA );
215 /* Initialize the device */
216 DeviceObject->Flags = DeviceObject->Flags | DO_DIRECT_IO;
217 DeviceObject->AlignmentRequirement = FILE_512_BYTE_ALIGNMENT;
218 // change state machine, no interrupt expected
219 ControllerExtension->IsrState = FloppyIsrUnexpected;
220 Config = IoGetConfigurationInformation();
221 Config->FloppyCount++;
222 // call IoAllocateAdapterChannel, and wait for execution routine to be given the channel
224 Status = IoAllocateAdapterChannel( ControllerExtension->AdapterObject,
226 0x3000/PAGE_SIZE, // max track size is 12k
227 FloppyAdapterControl,
228 ControllerExtension );
229 if( !NT_SUCCESS( Status ) )
231 DPRINT1( "Error: IoAllocateAdapterChannel returned %x\n", Status );
235 Status = KeWaitForSingleObject( &ControllerExtension->Event,
241 if( Status != STATUS_WAIT_0 )
243 DPRINT1( "Error: KeWaitForSingleObject returned: %x\n", Status );
246 // Ok, we own the adapter object, from now on we can just IoMapTransfer, and not
247 // bother releasing the adapter ever.
249 DPRINT( "Floppy drive initialized\n" );
253 IoDeleteDevice( DeviceObject );
255 IoDisconnectInterrupt(ControllerExtension->Interrupt);
257 // turn off controller
258 FloppyWriteDOR( ControllerExtension->PortBase, 0 );
259 IoDeleteController(ControllerObject);
263 IO_ALLOCATION_ACTION STDCALL
264 FloppyExecuteSpindown(PDEVICE_OBJECT DeviceObject,
266 PVOID MapRegisterbase,
269 PFLOPPY_CONTROLLER_EXTENSION ControllerExtension= (PFLOPPY_CONTROLLER_EXTENSION)Context;
271 // turn off motor, and return
272 DPRINT( "Spinning down motor\n" );
273 ControllerExtension->MotorOn = ~0;
274 FloppyWriteDOR( ControllerExtension->PortBase,
275 FLOPPY_DOR_ENABLE | FLOPPY_DOR_DMA );
276 return DeallocateObject;
279 IO_ALLOCATION_ACTION STDCALL
280 FloppyExecuteReadWrite(PDEVICE_OBJECT DeviceObject,
282 PVOID MapRegisterbase,
285 PFLOPPY_DEVICE_EXTENSION DeviceExtension = (PFLOPPY_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
286 PFLOPPY_CONTROLLER_EXTENSION ControllerExtension = (PFLOPPY_CONTROLLER_EXTENSION)DeviceExtension->Controller->ControllerExtension;
287 LARGE_INTEGER Timeout;
288 BOOLEAN WriteToDevice;
289 DWORD Cyl, Sector, Head;
290 PIO_STACK_LOCATION Stk;
293 ControllerExtension->Irp = Irp = (PIRP)Context;
294 Stk = IoGetCurrentIrpStackLocation( Irp );
295 ControllerExtension->Device = DeviceObject;
296 Timeout.QuadPart = FLOPPY_MOTOR_SPINUP_TIME;
297 DPRINT( "FloppyExecuteReadWrite()\n" );
299 WriteToDevice = Stk->MajorFunction == IRP_MJ_WRITE ? TRUE : FALSE;
300 // verify drive is spun up and selected
301 if( ControllerExtension->MotorOn != DeviceExtension->DriveSelect )
303 // turn on and select drive, and allow it to spin up
304 // FloppyMotorSpinupDpc will restart this operation once motor is spun up
305 DPRINT( "Motor not on, turning it on now\n" );
306 FloppyWriteDOR( ControllerExtension->PortBase,
307 DeviceExtension->DriveSelect ? FLOPPY_DRIVE1_ON : FLOPPY_DRIVE0_ON );
308 // cancel possible spindown timer first
309 KeCancelTimer( &ControllerExtension->SpinupTimer );
310 KeSetTimerEx( &ControllerExtension->SpinupTimer,
313 &ControllerExtension->MotorSpinupDpc );
317 Timeout.QuadPart = FLOPPY_MOTOR_SPINDOWN_TIME;
318 // motor is already spinning, so reset the spindown timer
319 KeCancelTimer( &ControllerExtension->SpinupTimer );
320 KeSetTimer( &ControllerExtension->SpinupTimer,
322 &ControllerExtension->MotorSpindownDpc );
325 /* Handle disk change, doesn't work correctly */
326 if( FloppyReadDIR( ControllerExtension->PortBase ) & FLOPPY_DI_DSKCHNG )
328 // No disk is in the drive
329 DPRINT( "No disk is in the drive\n" );
330 Irp->IoStatus.Status = STATUS_MEDIA_CHANGED;
331 Irp->IoStatus.Information = 0;
332 IoCompleteRequest( Irp, 0 );
333 return DeallocateObject;
336 if( DeviceExtension->MediaType == ~0 )
338 // media is in disk, but we have not yet detected what kind it is,
340 // First, we need to recalibrate the drive though
341 ControllerExtension->IsrState = FloppyIsrRecal;
342 DPRINT( "Recalibrating drive\n" );
343 KeStallExecutionProcessor( 1000 );
344 FloppyWriteDATA( ControllerExtension->PortBase, FLOPPY_CMD_RECAL );
345 KeStallExecutionProcessor( 1000 );
346 FloppyWriteDATA( ControllerExtension->PortBase, DeviceExtension->DriveSelect );
349 // looks like we have media in the drive.... do the read
350 // first, calculate geometry for read
351 Sector = Stk->Parameters.Read.ByteOffset.u.LowPart / MediaTypes[DeviceExtension->MediaType].BytesPerSector;
352 // absolute sector right now
353 Cyl = Sector / MediaTypes[DeviceExtension->MediaType].SectorsPerTrack;
354 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 );
355 Head = Cyl % MediaTypes[DeviceExtension->MediaType].Heads;
356 DPRINT( "Head = %2x\n", Head );
357 // convert absolute cyl to relative
358 Cyl /= MediaTypes[DeviceExtension->MediaType].Heads;
359 // convert absolute sector to relative
360 Sector %= MediaTypes[DeviceExtension->MediaType].SectorsPerTrack;
361 Sector++; // track relative sector numbers are 1 based, not 0 based
362 DPRINT( "Cyl = %2x, Head = %2x, Sector = %2x\n", Cyl, Head, Sector );
364 // seek if we need to seek
365 if( DeviceExtension->Cyl != Cyl )
367 DPRINT( "Seeking...\n" );
368 ControllerExtension->IsrState = FloppyIsrDetect;
369 ControllerExtension->DpcState = FloppySeekDpc;
370 FloppyWriteDATA( ControllerExtension->PortBase, FLOPPY_CMD_SEEK );
371 KeStallExecutionProcessor( 100 );
372 FloppyWriteDATA( ControllerExtension->PortBase, DeviceExtension->DriveSelect );
373 KeStallExecutionProcessor( 100 );
374 FloppyWriteDATA( ControllerExtension->PortBase, Cyl );
377 //set up DMA and issue read command
378 Length = MediaTypes[DeviceExtension->MediaType].SectorsPerTrack - Sector + 1;
379 // number of sectors untill end of track
380 Length *= 512; // convert to bytes
381 if( Length > Stk->Parameters.Read.Length )
382 Length = Stk->Parameters.Read.Length;
383 DPRINT( "Sector: %d, Length: %d\n", Sector, Length );
384 ControllerExtension->TransferLength = Length;
385 IoMapTransfer( ControllerExtension->AdapterObject,
387 ControllerExtension->MapRegisterBase,
388 Irp->Tail.Overlay.DriverContext[0], // current va
391 ControllerExtension->IsrState = FloppyIsrReadWrite;
392 ControllerExtension->DpcState = FloppyDpcReadWrite;
394 FloppyWriteDATA( ControllerExtension->PortBase, WriteToDevice ? FLOPPY_CMD_WRITE : FLOPPY_CMD_READ );
395 KeStallExecutionProcessor( 100 );
396 FloppyWriteDATA( ControllerExtension->PortBase, ( Head << 2 ) | DeviceExtension->DriveSelect );
397 KeStallExecutionProcessor( 100 );
398 FloppyWriteDATA( ControllerExtension->PortBase, Cyl );
399 KeStallExecutionProcessor( 100 );
400 FloppyWriteDATA( ControllerExtension->PortBase, Head );
401 KeStallExecutionProcessor( 100 );
402 FloppyWriteDATA( ControllerExtension->PortBase, Sector );
403 KeStallExecutionProcessor( 100 );
404 FloppyWriteDATA( ControllerExtension->PortBase, MediaTypes[DeviceExtension->MediaType].SectorSizeCode );
405 KeStallExecutionProcessor( 100 );
406 FloppyWriteDATA( ControllerExtension->PortBase, MediaTypes[DeviceExtension->MediaType].SectorsPerTrack );
407 KeStallExecutionProcessor( 100 );
408 FloppyWriteDATA( ControllerExtension->PortBase, 0 );
409 KeStallExecutionProcessor( 100 );
410 FloppyWriteDATA( ControllerExtension->PortBase, 0xFF );
412 // eventually, the FDC will interrupt and we will read results then
417 FloppyDispatchOpenClose(PDEVICE_OBJECT DeviceObject,
420 DPRINT("FloppyDispatchOpenClose\n");
421 return STATUS_SUCCESS;
425 FloppyDispatchReadWrite(PDEVICE_OBJECT DeviceObject,
428 PFLOPPY_DEVICE_EXTENSION DeviceExtension = (PFLOPPY_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
429 PFLOPPY_CONTROLLER_EXTENSION ControllerExtension = (PFLOPPY_CONTROLLER_EXTENSION)DeviceExtension->Controller->ControllerExtension;
430 PIO_STACK_LOCATION Stk = IoGetCurrentIrpStackLocation( Irp );
433 if( Stk->Parameters.Read.ByteOffset.u.HighPart )
435 Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
436 Irp->IoStatus.Information = 0;
437 IoCompleteRequest( Irp, 1 );
438 return STATUS_INVALID_PARAMETER;
440 // store currentva in drivercontext
441 Irp->Tail.Overlay.DriverContext[0] = MmGetMdlVirtualAddress( Irp->MdlAddress );
442 DPRINT( "FloppyDispatchReadWrite: offset = %x, length = %x, va = %x\n",
443 Stk->Parameters.Read.ByteOffset.u.LowPart,
444 Stk->Parameters.Read.Length,
445 Irp->Tail.Overlay.DriverContext[0] );
447 Irp->IoStatus.Status = STATUS_SUCCESS;
448 Irp->IoStatus.Information = Stk->Parameters.Read.Length;
449 IoMarkIrpPending( Irp );
450 KeRaiseIrql( DISPATCH_LEVEL, &oldlvl );
451 IoAllocateController( ((PFLOPPY_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->Controller,
453 FloppyExecuteReadWrite,
455 KeLowerIrql( oldlvl );
456 DPRINT( "oldlvl = %x\n", oldlvl );
457 return STATUS_PENDING;
460 IO_ALLOCATION_ACTION STDCALL
461 FloppyAdapterControl(PDEVICE_OBJECT DeviceObject,
463 PVOID MapRegisterBase,
466 PFLOPPY_CONTROLLER_EXTENSION ControllerExtension = (PFLOPPY_CONTROLLER_EXTENSION)Context;
468 // just set the event, and return KeepObject
470 ControllerExtension->MapRegisterBase = MapRegisterBase;
471 KeSetEvent( &ControllerExtension->Event, 0, FALSE );
476 FloppyDispatchDeviceControl(PDEVICE_OBJECT DeviceObject,
479 PIO_STACK_LOCATION IrpStack;
480 ULONG ControlCode, InputLength, OutputLength;
483 DPRINT("FloppyDispatchDeviceControl\n");
485 IrpStack = IoGetCurrentIrpStackLocation(Irp);
486 ControlCode = IrpStack->Parameters.DeviceIoControl.IoControlCode;
487 InputLength = IrpStack->Parameters.DeviceIoControl.InputBufferLength;
488 OutputLength = IrpStack->Parameters.DeviceIoControl.OutputBufferLength;
492 case IOCTL_DISK_GET_DRIVE_GEOMETRY:
493 if (OutputLength < sizeof(DISK_GEOMETRY))
495 Status = STATUS_INVALID_PARAMETER;
499 PDISK_GEOMETRY Geometry = Irp->AssociatedIrp.SystemBuffer;
500 // FIXME: read the first sector of the diskette
501 Geometry->MediaType = F3_1Pt44_512;
502 Geometry->Cylinders.QuadPart = 80;
503 Geometry->TracksPerCylinder = 2 * 18;
504 Geometry->SectorsPerTrack = 18;
505 Geometry->BytesPerSector = 512;
506 Status = STATUS_SUCCESS;
507 Irp->IoStatus.Information = sizeof(DISK_GEOMETRY);
511 Status = STATUS_INVALID_DEVICE_REQUEST;
513 Irp->IoStatus.Status = Status;
514 IoCompleteRequest(Irp, NT_SUCCESS(Status) ? IO_DISK_INCREMENT : IO_NO_INCREMENT);
521 * This function initializes the driver, locates and claims
522 * hardware resources, and creates various NT objects needed
523 * to process I/O requests.
529 * IN PDRIVER_OBJECT DriverObject System allocated Driver Object
531 * IN PUNICODE_STRING RegistryPath Name of registry driver service
538 DriverEntry(IN PDRIVER_OBJECT DriverObject,
539 IN PUNICODE_STRING RegistryPath)
541 DPRINT("Floppy driver\n");
543 /* Export other driver entry points... */
544 DriverObject->MajorFunction[IRP_MJ_CREATE] = FloppyDispatchOpenClose;
545 DriverObject->MajorFunction[IRP_MJ_CLOSE] = FloppyDispatchOpenClose;
546 DriverObject->MajorFunction[IRP_MJ_READ] = FloppyDispatchReadWrite;
547 DriverObject->MajorFunction[IRP_MJ_WRITE] = FloppyDispatchReadWrite;
548 DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] =
549 FloppyDispatchDeviceControl;
551 /* Try to detect controller and abort if it fails */
552 if (!FloppyCreateController(DriverObject,
553 &ControllerParameters[0],
556 DPRINT("Could not find floppy controller\n");
557 return STATUS_NO_SUCH_DEVICE;
560 return STATUS_SUCCESS;