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, 6, 2, 6, LevelSensitive, 0xffff}
27 // {0x0370, 6, 6, 6, LevelSensitive, 0xffff},
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;
54 /* FIXME: Register port ranges and interrupts with HAL */
56 /* Create controller object for FDC */
57 ControllerObject = IoCreateController(sizeof(FLOPPY_CONTROLLER_EXTENSION));
58 if (ControllerObject == NULL)
60 DPRINT("Could not create controller object for controller %d\n",
65 /* FIXME: fill out controller data */
66 ControllerExtension = (PFLOPPY_CONTROLLER_EXTENSION)
67 ControllerObject->ControllerExtension;
68 ControllerExtension->Number = Index;
69 ControllerExtension->PortBase = ControllerParameters->PortBase;
70 ControllerExtension->Vector = ControllerParameters->Vector;
71 KeInitializeEvent( &ControllerExtension->Event, SynchronizationEvent, FALSE );
72 ControllerExtension->Device = 0; // no active device
73 ControllerExtension->Irp = 0; // no active IRP
74 /* Initialize the spin lock in the controller extension */
75 KeInitializeSpinLock(&ControllerExtension->SpinLock);
76 ControllerExtension->IsrState = FloppyIsrDetect;
77 ControllerExtension->DpcState = FloppyDpcDetect;
79 /* Register an interrupt handler for this controller */
80 Status = IoConnectInterrupt(&ControllerExtension->Interrupt,
83 &ControllerExtension->SpinLock,
84 ControllerExtension->Vector,
85 ControllerParameters->IrqL,
86 ControllerParameters->SynchronizeIrqL,
87 ControllerParameters->InterruptMode,
89 ControllerParameters->Affinity,
91 if (!NT_SUCCESS(Status))
93 DPRINT("Could not Connect Interrupt %d\n",
94 ControllerExtension->Vector);
95 goto controllercleanup;
99 /* setup DMA stuff for controller */
100 DeviceDescription.Version = DEVICE_DESCRIPTION_VERSION;
101 DeviceDescription.Master = FALSE;
102 DeviceDescription.ScatterGather = FALSE;
103 DeviceDescription.AutoInitialize = FALSE;
104 DeviceDescription.Dma32BitAddress = FALSE;
105 DeviceDescription.DmaChannel = ControllerParameters->DmaChannel;
106 DeviceDescription.InterfaceType = Isa;
107 // DeviceDescription.DmaWidth = Width8Bits;
108 ControllerExtension->AdapterObject = HalGetAdapter( &DeviceDescription, &MaxMapRegs );
109 if( ControllerExtension->AdapterObject == NULL )
111 DPRINT1( "Could not get adapter object\n" );
112 goto interruptcleanup;
116 /* Check for each possible drive and create devices for them */
117 for (DriveIdx = 0; DriveIdx < FLOPPY_MAX_DRIVES; DriveIdx++)
119 /* FIXME: try to identify the drive */
120 /* FIXME: create a device if it's there */
124 /* FIXME: Let's assume one drive and one controller for the moment */
125 RtlInitUnicodeStringFromLiteral(&DeviceName, L"\\Device\\Floppy0");
126 Status = IoCreateDevice(DriverObject,
127 sizeof(FLOPPY_DEVICE_EXTENSION),
130 FILE_REMOVABLE_MEDIA | FILE_FLOPPY_DISKETTE,
133 if (!NT_SUCCESS(Status))
135 goto interruptcleanup;
137 DeviceExtension = (PFLOPPY_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
138 DeviceExtension->DriveSelect = 0;
139 DeviceExtension->Controller = ControllerObject;
140 DeviceExtension->MediaType = ~0;
141 ControllerExtension->MotorOn = ~0;
143 ControllerExtension->Device = DeviceObject;
144 KeInitializeDpc( &ControllerExtension->MotorSpinupDpc,
145 FloppyMotorSpinupDpc,
147 KeInitializeDpc( &ControllerExtension->MotorSpindownDpc,
148 FloppyMotorSpindownDpc,
150 KeInitializeTimer( &ControllerExtension->SpinupTimer );
151 IoInitializeDpcRequest( DeviceObject, FloppyDpc );
152 // reset controller and wait for interrupt
153 DPRINT( "Controller Off\n" );
154 FloppyWriteDOR( ControllerExtension->PortBase, 0 );
155 // let controller reset for at least FLOPPY_RESET_TIME
156 KeStallExecutionProcessor( FLOPPY_RESET_TIME );
157 DPRINT( "Controller On\n" );
158 FloppyWriteDOR( ControllerExtension->PortBase, FLOPPY_DOR_ENABLE | FLOPPY_DOR_DMA );
159 // wait for interrupt now
160 Timeout.QuadPart = -10000000;
161 Status = KeWaitForSingleObject( &ControllerExtension->Event,
166 if( Status != STATUS_WAIT_0 )
168 DPRINT1( "Error: KeWaitForSingleObject returned: %x\n", Status );
171 // set for high speed mode
172 // FloppyWriteCCNTL( ControllerExtension->PortBase, FLOPPY_CCNTL_1MBIT );
174 // ok, so we have an FDC, now check for drives
175 // aparently the sense drive status command does not work on any FDC I can find
176 // so instead we will just have to assume a 1.44 meg 3.5 inch floppy. At some
177 // point we should get the bios disk parameters passed in to the kernel at boot
178 // and stored in the HARDWARE registry key for us to pick up here.
180 // turn on motor, wait for spinup time, and recalibrate the drive
181 FloppyWriteDOR( ControllerExtension->PortBase, FLOPPY_DRIVE0_ON );
182 Timeout.QuadPart = FLOPPY_MOTOR_SPINUP_TIME;
183 KeDelayExecutionThread( KernelMode, FALSE, &Timeout );
184 DPRINT( "MSTAT: %2x\n", FloppyReadMSTAT( ControllerExtension->PortBase ) );
185 FloppyWriteDATA( ControllerExtension->PortBase, FLOPPY_CMD_RECAL );
186 DPRINT( "MSTAT: %2x\n", FloppyReadMSTAT( ControllerExtension->PortBase ) );
187 KeStallExecutionProcessor( 10000 );
188 FloppyWriteDATA( ControllerExtension->PortBase, 0 ); // drive select
189 Timeout.QuadPart = FLOPPY_RECAL_TIMEOUT;
190 Status = KeWaitForSingleObject( &ControllerExtension->Event,
195 if( Status != STATUS_WAIT_0 )
197 DPRINT1( "Error: KeWaitForSingleObject returned: %x\n", Status );
200 if( ControllerExtension->St0 != FLOPPY_ST0_SEEKGD )
202 DbgPrint( "Floppy: error recalibrating drive, ST0: %2x\n", (DWORD)ControllerExtension->St0 );
203 goto interruptcleanup;
205 DeviceExtension->Cyl = 0;
206 // drive is good, and it is now on track 0, turn off the motor
207 FloppyWriteDOR( ControllerExtension->PortBase, FLOPPY_DOR_ENABLE | FLOPPY_DOR_DMA );
208 /* Initialize the device */
209 DeviceObject->Flags = DeviceObject->Flags | DO_DIRECT_IO;
210 DeviceObject->AlignmentRequirement = FILE_512_BYTE_ALIGNMENT;
211 // change state machine, no interrupt expected
212 ControllerExtension->IsrState = FloppyIsrUnexpected;
213 Config = IoGetConfigurationInformation();
214 Config->FloppyCount++;
215 // call IoAllocateAdapterChannel, and wait for execution routine to be given the channel
217 Status = IoAllocateAdapterChannel( ControllerExtension->AdapterObject,
219 0x3000/PAGE_SIZE, // max track size is 12k
220 FloppyAdapterControl,
221 ControllerExtension );
222 if( !NT_SUCCESS( Status ) )
224 DPRINT1( "Error: IoAllocateAdapterChannel returned %x\n", Status );
225 goto interruptcleanup;
228 Status = KeWaitForSingleObject( &ControllerExtension->Event,
234 if( Status != STATUS_WAIT_0 )
236 DPRINT1( "Error: KeWaitForSingleObject returned: %x\n", Status );
237 goto interruptcleanup;
239 // Ok, we own the adapter object, from now on we can just IoMapTransfer, and not
240 // bother releasing the adapter ever.
242 DPRINT( "Floppy drive initialized\n" );
246 IoDeleteDevice( DeviceObject );
248 IoDisconnectInterrupt(ControllerExtension->Interrupt);
250 // turn off controller
251 FloppyWriteDOR( ControllerExtension->PortBase, 0 );
252 IoDeleteController(ControllerObject);
257 IO_ALLOCATION_ACTION STDCALL
258 FloppyExecuteSpindown(PDEVICE_OBJECT DeviceObject,
260 PVOID MapRegisterbase,
263 PFLOPPY_CONTROLLER_EXTENSION ControllerExtension= (PFLOPPY_CONTROLLER_EXTENSION)Context;
265 // turn off motor, and return
266 DPRINT( "Spinning down motor\n" );
267 ControllerExtension->MotorOn = ~0;
268 FloppyWriteDOR( ControllerExtension->PortBase,
269 FLOPPY_DOR_ENABLE | FLOPPY_DOR_DMA );
270 return DeallocateObject;
273 IO_ALLOCATION_ACTION STDCALL
274 FloppyExecuteReadWrite(PDEVICE_OBJECT DeviceObject,
276 PVOID MapRegisterbase,
279 PFLOPPY_DEVICE_EXTENSION DeviceExtension = (PFLOPPY_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
280 PFLOPPY_CONTROLLER_EXTENSION ControllerExtension = (PFLOPPY_CONTROLLER_EXTENSION)DeviceExtension->Controller->ControllerExtension;
281 LARGE_INTEGER Timeout;
282 BOOLEAN WriteToDevice;
283 DWORD Cyl, Sector, Head;
284 PIO_STACK_LOCATION Stk;
287 ControllerExtension->Irp = Irp = (PIRP)Context;
288 Stk = IoGetCurrentIrpStackLocation( Irp );
289 ControllerExtension->Device = DeviceObject;
290 Timeout.QuadPart = FLOPPY_MOTOR_SPINUP_TIME;
291 DPRINT( "FloppyExecuteReadWrite()\n" );
293 WriteToDevice = Stk->MajorFunction == IRP_MJ_WRITE ? TRUE : FALSE;
294 // verify drive is spun up and selected
295 if( ControllerExtension->MotorOn != DeviceExtension->DriveSelect )
297 // turn on and select drive, and allow it to spin up
298 // FloppyMotorSpinupDpc will restart this operation once motor is spun up
299 DPRINT( "Motor not on, turning it on now\n" );
300 FloppyWriteDOR( ControllerExtension->PortBase,
301 DeviceExtension->DriveSelect ? FLOPPY_DRIVE1_ON : FLOPPY_DRIVE0_ON );
302 // cancel possible spindown timer first
303 KeCancelTimer( &ControllerExtension->SpinupTimer );
304 KeSetTimerEx( &ControllerExtension->SpinupTimer,
307 &ControllerExtension->MotorSpinupDpc );
311 Timeout.QuadPart = FLOPPY_MOTOR_SPINDOWN_TIME;
312 // motor is already spinning, so reset the spindown timer
313 KeCancelTimer( &ControllerExtension->SpinupTimer );
314 KeSetTimer( &ControllerExtension->SpinupTimer,
316 &ControllerExtension->MotorSpindownDpc );
318 // verify media content
319 if( FloppyReadDIR( ControllerExtension->PortBase ) & FLOPPY_DI_DSKCHNG )
321 // No disk is in the drive
322 DPRINT( "No disk is in the drive\n" );
323 Irp->IoStatus.Status = STATUS_NO_MEDIA;
324 Irp->IoStatus.Information = 0;
325 IoCompleteRequest( Irp, 0 );
326 return DeallocateObject;
328 if( DeviceExtension->MediaType == ~0 )
330 // media is in disk, but we have not yet detected what kind it is,
332 // First, we need to recalibrate the drive though
333 ControllerExtension->IsrState = FloppyIsrRecal;
334 DPRINT( "Recalibrating drive\n" );
335 KeStallExecutionProcessor( 1000 );
336 FloppyWriteDATA( ControllerExtension->PortBase, FLOPPY_CMD_RECAL );
337 KeStallExecutionProcessor( 1000 );
338 FloppyWriteDATA( ControllerExtension->PortBase, DeviceExtension->DriveSelect );
341 // looks like we have media in the drive.... do the read
342 // first, calculate geometry for read
343 Sector = Stk->Parameters.Read.ByteOffset.u.LowPart / MediaTypes[DeviceExtension->MediaType].BytesPerSector;
344 // absolute sector right now
345 Cyl = Sector / MediaTypes[DeviceExtension->MediaType].SectorsPerTrack;
346 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 );
347 Head = Cyl % MediaTypes[DeviceExtension->MediaType].Heads;
348 DPRINT( "Head = %2x\n", Head );
349 // convert absolute cyl to relative
350 Cyl /= MediaTypes[DeviceExtension->MediaType].Heads;
351 // convert absolute sector to relative
352 Sector %= MediaTypes[DeviceExtension->MediaType].SectorsPerTrack;
353 Sector++; // track relative sector numbers are 1 based, not 0 based
354 DPRINT( "Cyl = %2x, Head = %2x, Sector = %2x\n", Cyl, Head, Sector );
356 // seek if we need to seek
357 if( DeviceExtension->Cyl != Cyl )
359 DPRINT( "Seeking...\n" );
360 ControllerExtension->IsrState = FloppyIsrDetect;
361 ControllerExtension->DpcState = FloppySeekDpc;
362 FloppyWriteDATA( ControllerExtension->PortBase, FLOPPY_CMD_SEEK );
363 KeStallExecutionProcessor( 100 );
364 FloppyWriteDATA( ControllerExtension->PortBase, DeviceExtension->DriveSelect );
365 KeStallExecutionProcessor( 100 );
366 FloppyWriteDATA( ControllerExtension->PortBase, Cyl );
369 //set up DMA and issue read command
370 Length = MediaTypes[DeviceExtension->MediaType].SectorsPerTrack - Sector + 1;
371 // number of sectors untill end of track
372 Length *= 512; // convert to bytes
373 if( Length > Stk->Parameters.Read.Length )
374 Length = Stk->Parameters.Read.Length;
375 DPRINT( "Sector: %d, Length: %d\n", Sector, Length );
376 ControllerExtension->TransferLength = Length;
377 IoMapTransfer( ControllerExtension->AdapterObject,
379 ControllerExtension->MapRegisterBase,
380 Irp->Tail.Overlay.DriverContext[0], // current va
383 ControllerExtension->IsrState = FloppyIsrReadWrite;
384 ControllerExtension->DpcState = FloppyDpcReadWrite;
386 FloppyWriteDATA( ControllerExtension->PortBase, WriteToDevice ? FLOPPY_CMD_WRITE : FLOPPY_CMD_READ );
387 KeStallExecutionProcessor( 100 );
388 FloppyWriteDATA( ControllerExtension->PortBase, ( Head << 2 ) | DeviceExtension->DriveSelect );
389 KeStallExecutionProcessor( 100 );
390 FloppyWriteDATA( ControllerExtension->PortBase, Cyl );
391 KeStallExecutionProcessor( 100 );
392 FloppyWriteDATA( ControllerExtension->PortBase, Head );
393 KeStallExecutionProcessor( 100 );
394 FloppyWriteDATA( ControllerExtension->PortBase, Sector );
395 KeStallExecutionProcessor( 100 );
396 FloppyWriteDATA( ControllerExtension->PortBase, MediaTypes[DeviceExtension->MediaType].SectorSizeCode );
397 KeStallExecutionProcessor( 100 );
398 FloppyWriteDATA( ControllerExtension->PortBase, MediaTypes[DeviceExtension->MediaType].SectorsPerTrack );
399 KeStallExecutionProcessor( 100 );
400 FloppyWriteDATA( ControllerExtension->PortBase, 0 );
401 KeStallExecutionProcessor( 100 );
402 FloppyWriteDATA( ControllerExtension->PortBase, 0xFF );
404 // eventually, the FDC will interrupt and we will read results then
409 FloppyDispatchOpenClose(PDEVICE_OBJECT DeviceObject,
412 DPRINT("FloppyDispatchOpenClose\n");
413 return STATUS_SUCCESS;
417 FloppyDispatchReadWrite(PDEVICE_OBJECT DeviceObject,
420 PFLOPPY_DEVICE_EXTENSION DeviceExtension = (PFLOPPY_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
421 PFLOPPY_CONTROLLER_EXTENSION ControllerExtension = (PFLOPPY_CONTROLLER_EXTENSION)DeviceExtension->Controller->ControllerExtension;
422 PIO_STACK_LOCATION Stk = IoGetCurrentIrpStackLocation( Irp );
425 if( Stk->Parameters.Read.ByteOffset.u.HighPart )
427 Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
428 Irp->IoStatus.Information = 0;
429 IoCompleteRequest( Irp, 1 );
430 return STATUS_INVALID_PARAMETER;
432 // store currentva in drivercontext
433 Irp->Tail.Overlay.DriverContext[0] = MmGetMdlVirtualAddress( Irp->MdlAddress );
434 DPRINT( "FloppyDispatchReadWrite: offset = %x, length = %x, va = %x\n",
435 Stk->Parameters.Read.ByteOffset.u.LowPart,
436 Stk->Parameters.Read.Length,
437 Irp->Tail.Overlay.DriverContext[0] );
439 Irp->IoStatus.Status = STATUS_SUCCESS;
440 Irp->IoStatus.Information = Stk->Parameters.Read.Length;
441 IoMarkIrpPending( Irp );
442 KeRaiseIrql( DISPATCH_LEVEL, &oldlvl );
443 IoAllocateController( ((PFLOPPY_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->Controller,
445 FloppyExecuteReadWrite,
447 KeLowerIrql( oldlvl );
448 DPRINT( "oldlvl = %x\n", oldlvl );
449 return STATUS_PENDING;
452 IO_ALLOCATION_ACTION STDCALL
453 FloppyAdapterControl(PDEVICE_OBJECT DeviceObject,
455 PVOID MapRegisterBase,
458 PFLOPPY_CONTROLLER_EXTENSION ControllerExtension = (PFLOPPY_CONTROLLER_EXTENSION)Context;
460 // just set the event, and return KeepObject
462 ControllerExtension->MapRegisterBase = MapRegisterBase;
463 KeSetEvent( &ControllerExtension->Event, 0, FALSE );
468 FloppyDispatchDeviceControl(PDEVICE_OBJECT DeviceObject,
471 PIO_STACK_LOCATION IrpStack;
472 ULONG ControlCode, InputLength, OutputLength;
475 DPRINT("FloppyDispatchDeviceControl\n");
477 IrpStack = IoGetCurrentIrpStackLocation(Irp);
478 ControlCode = IrpStack->Parameters.DeviceIoControl.IoControlCode;
479 InputLength = IrpStack->Parameters.DeviceIoControl.InputBufferLength;
480 OutputLength = IrpStack->Parameters.DeviceIoControl.OutputBufferLength;
484 case IOCTL_DISK_GET_DRIVE_GEOMETRY:
485 if (OutputLength < sizeof(DISK_GEOMETRY))
487 Status = STATUS_INVALID_PARAMETER;
491 PDISK_GEOMETRY Geometry = Irp->AssociatedIrp.SystemBuffer;
492 // FIXME: read the first sector of the diskette
493 Geometry->MediaType = F3_1Pt44_512;
494 Geometry->Cylinders.QuadPart = 80;
495 Geometry->TracksPerCylinder = 2 * 18;
496 Geometry->SectorsPerTrack = 18;
497 Geometry->BytesPerSector = 512;
498 Status = STATUS_SUCCESS;
499 Irp->IoStatus.Information = sizeof(DISK_GEOMETRY);
503 Status = STATUS_INVALID_DEVICE_REQUEST;
505 Irp->IoStatus.Status = Status;
506 IoCompleteRequest(Irp, NT_SUCCESS(Status) ? IO_DISK_INCREMENT : IO_NO_INCREMENT);
513 * This function initializes the driver, locates and claims
514 * hardware resources, and creates various NT objects needed
515 * to process I/O requests.
521 * IN PDRIVER_OBJECT DriverObject System allocated Driver Object
523 * IN PUNICODE_STRING RegistryPath Name of registry driver service
530 DriverEntry(IN PDRIVER_OBJECT DriverObject,
531 IN PUNICODE_STRING RegistryPath)
533 DPRINT("Floppy driver\n");
535 /* Export other driver entry points... */
536 DriverObject->MajorFunction[IRP_MJ_CREATE] = FloppyDispatchOpenClose;
537 DriverObject->MajorFunction[IRP_MJ_CLOSE] = FloppyDispatchOpenClose;
538 DriverObject->MajorFunction[IRP_MJ_READ] = FloppyDispatchReadWrite;
539 DriverObject->MajorFunction[IRP_MJ_WRITE] = FloppyDispatchReadWrite;
540 DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] =
541 FloppyDispatchDeviceControl;
543 /* Try to detect controller and abort if it fails */
544 if (!FloppyCreateController(DriverObject,
545 &ControllerParameters[0],
548 DPRINT("Could not find floppy controller\n");
549 return STATUS_NO_SUCH_DEVICE;
552 return STATUS_SUCCESS;