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
;