[VFD] Import the VFD project (Virtual Floppy Drive) which allows creating virtual
[reactos.git] / modules / rosapps / drivers / vfd / vfddrv.c
1 /*
2 vfddrv.c
3
4 Virtual Floppy Drive for Windows NT platform
5 Kernel mode driver: miscellaneous driver functions
6
7 Copyright (C) 2003-2005 Ken Kato
8 */
9
10 #include "imports.h"
11 #include "vfddrv.h"
12 #include "vfddbg.h"
13
14 //
15 // driver reinitialize routine
16 // -- create a drive letter for each device
17 //
18 #ifdef __cplusplus
19 extern "C"
20 #endif // __cplusplus
21 static VOID
22 NTAPI
23 VfdReinitialize(
24 IN PDRIVER_OBJECT DriverObject,
25 IN PVOID Context,
26 IN ULONG Count);
27
28 //
29 // specify code segment
30 //
31 #ifdef ALLOC_PRAGMA
32 #pragma alloc_text(INIT, DriverEntry)
33 #pragma alloc_text(PAGE, VfdReinitialize)
34 #pragma alloc_text(PAGE, VfdUnloadDriver)
35 #pragma alloc_text(PAGE, VfdCreateClose)
36 #pragma alloc_text(PAGE, VfdCopyUnicode)
37 #pragma alloc_text(PAGE, VfdFreeUnicode)
38 #endif // ALLOC_PRAGMA
39
40 //
41 // operating system version
42 //
43 #ifndef __REACTOS__
44 extern ULONG OsMajorVersion = 0;
45 extern ULONG OsMinorVersion = 0;
46 extern ULONG OsBuildNumber = 0;
47 #else
48 ULONG OsMajorVersion = 0;
49 ULONG OsMinorVersion = 0;
50 ULONG OsBuildNumber = 0;
51 #endif
52
53 //
54 // Trace level flag
55 //
56 #if DBG
57 #ifndef __REACTOS__
58 extern ULONG TraceFlags = (ULONG)-1;
59 #else
60 ULONG TraceFlags = (ULONG)-1;
61 #endif
62 #endif // DBG
63
64 //
65 // Driver Entry routine
66 //
67 NTSTATUS
68 NTAPI
69 DriverEntry (
70 IN PDRIVER_OBJECT DriverObject,
71 IN PUNICODE_STRING RegistryPath)
72 {
73 NTSTATUS status;
74 PVFD_DRIVER_EXTENSION driver_extension;
75 ULONG number_of_devices = VFD_DEFAULT_DEVICES;
76
77 ASSERT(DriverObject);
78
79 // Get operating system version
80
81 PsGetVersion(&OsMajorVersion, &OsMinorVersion, &OsBuildNumber, NULL);
82
83 #ifdef VFD_PNP
84 #define VFD_PNP_TAG "(Plug & Play version)"
85 #else
86 #define VFD_PNP_TAG
87 #endif
88
89 VFDTRACE(0, ("[VFD] %s %s" VFD_PNP_TAG "\n",
90 VFD_PRODUCT_NAME, VFD_DRIVER_VERSION_STR));
91
92 VFDTRACE(0,
93 ("[VFD] Running on Windows NT %lu.%lu build %lu\n",
94 OsMajorVersion, OsMinorVersion, OsBuildNumber));
95
96 VFDTRACE(0,
97 ("[VFD] Build Target Environment: %d\n", VER_PRODUCTBUILD));
98
99 #ifdef VFD_PNP
100
101 // Create device_extension for the driver object to store driver specific
102 // information. Device specific information are stored in device extension
103 // for each device object.
104
105 status = IoAllocateDriverObjectExtension(
106 DriverObject,
107 VFD_DRIVER_EXTENSION_ID,
108 sizeof(VFD_DRIVER_EXTENSION),
109 &driver_extension);
110
111 if(!NT_SUCCESS(status)) {
112 VFDTRACE(0, ("[VFD] IoAllocateDriverObjectExtension - %s\n",
113 GetStatusName(status)));
114 return status;
115 }
116
117 #else // VFD_PNP
118
119 // Windows NT doesn't have the IoAllocateDriverObjectExtension
120 // function and I think there's little point in making a non-PnP
121 // driver incompatible with Windows NT.
122
123 driver_extension = (PVFD_DRIVER_EXTENSION)ExAllocatePoolWithTag(
124 PagedPool, sizeof(VFD_DRIVER_EXTENSION), VFD_POOL_TAG);
125
126 if (!driver_extension) {
127 VFDTRACE(0, ("[VFD] failed to allocate the driver extension.\n"));
128 return STATUS_INSUFFICIENT_RESOURCES;
129 }
130
131 #endif // VFD_PNP
132
133 RtlZeroMemory(driver_extension, sizeof(VFD_DRIVER_EXTENSION));
134
135 //
136 // Copy the registry path into the driver extension so we can use it later
137 //
138 if (VfdCopyUnicode(&(driver_extension->RegistryPath), RegistryPath)) {
139
140 //
141 // Read config values from the registry
142 //
143 RTL_QUERY_REGISTRY_TABLE params[3];
144 ULONG default_devs = VFD_DEFAULT_DEVICES;
145 #if DBG
146 ULONG default_trace = (ULONG)-1;
147 #endif
148
149 RtlZeroMemory(params, sizeof(params));
150
151 VFDTRACE(0, ("[VFD] Registry Path: %ws\n",
152 driver_extension->RegistryPath.Buffer));
153
154 params[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
155 params[0].Name = VFD_REG_DEVICE_NUMBER;
156 params[0].EntryContext = &number_of_devices;
157 params[0].DefaultType = REG_DWORD;
158 params[0].DefaultData = &default_devs;
159 params[0].DefaultLength = sizeof(ULONG);
160
161 #if DBG
162 params[1].Flags = RTL_QUERY_REGISTRY_DIRECT;
163 params[1].Name = VFD_REG_TRACE_FLAGS;
164 params[1].EntryContext = &TraceFlags;
165 params[1].DefaultType = REG_DWORD;
166 params[1].DefaultData = &default_trace;
167 params[1].DefaultLength = sizeof(ULONG);
168 #endif // DBG
169
170 status = RtlQueryRegistryValues(
171 RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
172 driver_extension->RegistryPath.Buffer,
173 params, NULL, NULL);
174
175 if (!NT_SUCCESS(status) ||
176 number_of_devices == 0 ||
177 number_of_devices > VFD_MAXIMUM_DEVICES) {
178 number_of_devices = VFD_DEFAULT_DEVICES;
179 }
180
181 VFDTRACE(0,("[VFD] NumberOfDevices = %lu\n", number_of_devices));
182 VFDTRACE(0,("[VFD] TraceFlags = 0x%08x\n", TraceFlags));
183 }
184 else {
185 VFDTRACE(0, ("[VFD] failed to allocate the registry path buffer.\n"));
186 // this error is not fatal
187 }
188
189 //
190 // Create VFD device objects
191 //
192 do {
193 #ifdef VFD_PNP
194 status = VfdCreateDevice(DriverObject, NULL);
195 #else // VFD_PNP
196 status = VfdCreateDevice(DriverObject, driver_extension);
197 #endif // VFD_PNP
198
199 if (!NT_SUCCESS(status)) {
200 break;
201 }
202 }
203 while (driver_extension->NumberOfDevices < number_of_devices);
204
205 if (!driver_extension->NumberOfDevices) {
206
207 // Failed to create even one device
208
209 VfdFreeUnicode(&(driver_extension->RegistryPath));
210
211 return status;
212 }
213
214 // Setup dispatch table
215
216 DriverObject->MajorFunction[IRP_MJ_CREATE] = VfdCreateClose;
217 DriverObject->MajorFunction[IRP_MJ_CLOSE] = VfdCreateClose;
218 DriverObject->MajorFunction[IRP_MJ_READ] = VfdReadWrite;
219 DriverObject->MajorFunction[IRP_MJ_WRITE] = VfdReadWrite;
220 DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = VfdDeviceControl;
221
222 #ifdef VFD_PNP
223 DriverObject->MajorFunction[IRP_MJ_PNP] = VfdPlugAndPlay;
224 DriverObject->MajorFunction[IRP_MJ_POWER] = VfdPowerControl;
225 DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = VfdSystemControl;
226 DriverObject->DriverExtension->AddDevice = VfdAddDevice;
227 #endif // VFDPNP
228
229 DriverObject->DriverUnload = VfdUnloadDriver;
230
231 // Register the driver reinitialize routine to be called
232 // *after* the DriverEntry routine returns
233
234 IoRegisterDriverReinitialization(
235 DriverObject, VfdReinitialize, NULL);
236
237 VFDTRACE(VFDINFO,
238 ("[VFD] driver initialized with %lu devices.\n",
239 driver_extension->NumberOfDevices));
240
241 return STATUS_SUCCESS;
242 }
243
244 //
245 // Driver unload routine
246 // Cleans up the device objects and other resources
247 //
248 VOID
249 NTAPI
250 VfdUnloadDriver (
251 IN PDRIVER_OBJECT DriverObject)
252 {
253 PDEVICE_OBJECT device_object;
254 PVFD_DRIVER_EXTENSION driver_extension;
255
256 VFDTRACE(VFDINFO, ("[VFD] VfdUnloadDriver - IN\n"));
257
258 device_object = DriverObject->DeviceObject;
259
260 #ifdef VFD_PNP
261 driver_extension = IoGetDriverObjectExtension(
262 DriverObject, VFD_DRIVER_EXTENSION_ID);
263 #else
264 if (device_object && device_object->DeviceExtension) {
265 driver_extension =
266 ((PDEVICE_EXTENSION)(device_object->DeviceExtension))->DriverExtension;
267 }
268 else {
269 driver_extension = NULL;
270 }
271 #endif // VFD_PNP
272
273 //
274 // Delete all remaining device objects
275 //
276 while (device_object) {
277
278 PDEVICE_OBJECT next_device = device_object->NextDevice;
279
280 VfdDeleteDevice(device_object);
281
282 device_object = next_device;
283 }
284
285 //
286 // Release the driver extension and the registry path buffer
287 //
288 if (driver_extension) {
289
290 if (driver_extension->RegistryPath.Buffer) {
291 VFDTRACE(0, ("[VFD] Releasing the registry path buffer\n"));
292 ExFreePool(driver_extension->RegistryPath.Buffer);
293 }
294
295 #ifndef VFD_PNP
296 // The system takes care of freeing the driver extension
297 // allocated with IoAllocateDriverObjectExtension in a PnP driver.
298 VFDTRACE(0, ("[VFD] Releasing the driver extension\n"));
299 ExFreePool(driver_extension);
300 #endif // VFD_PNP
301 }
302
303 VFDTRACE(VFDINFO, ("[VFD] VfdUnloadDriver - OUT\n"));
304 }
305
306 //
307 // IRP_MJ_CREATE and IRP_MJ_CLOSE handler
308 // Really nothing to do here...
309 //
310 NTSTATUS
311 NTAPI
312 VfdCreateClose (
313 IN PDEVICE_OBJECT DeviceObject,
314 IN PIRP Irp)
315 {
316 #if DBG
317 if (DeviceObject && DeviceObject->DeviceExtension &&
318 ((PDEVICE_EXTENSION)DeviceObject->DeviceExtension)->DeviceName.Buffer) {
319
320 VFDTRACE(VFDINFO, ("[VFD] %-40s %ws\n",
321 GetMajorFuncName(IoGetCurrentIrpStackLocation(Irp)->MajorFunction),
322 ((PDEVICE_EXTENSION)DeviceObject->DeviceExtension)->DeviceName.Buffer));
323 }
324 else {
325 VFDTRACE(VFDINFO, ("[VFD] %-40s %p\n",
326 GetMajorFuncName(IoGetCurrentIrpStackLocation(Irp)->MajorFunction),
327 DeviceObject));
328 }
329 #endif // DBG
330
331 Irp->IoStatus.Status = STATUS_SUCCESS;
332 Irp->IoStatus.Information = FILE_OPENED;
333
334 IoCompleteRequest(Irp, IO_NO_INCREMENT);
335
336 return STATUS_SUCCESS;
337 }
338
339 //
340 // Called after the DriverEntry routine has returned
341 // (Re)Create a persistent drive letter for each device
342 //
343 VOID
344 NTAPI
345 VfdReinitialize(
346 IN PDRIVER_OBJECT DriverObject,
347 IN PVOID Context,
348 IN ULONG Count)
349 {
350 PDEVICE_OBJECT device_object;
351 PDEVICE_EXTENSION device_extension;
352
353 UNREFERENCED_PARAMETER(Context);
354 UNREFERENCED_PARAMETER(Count);
355
356 VFDTRACE(VFDINFO, ("[VFD] VfdReinitialize - IN\n"));
357
358 device_object = DriverObject->DeviceObject;
359
360 while (device_object) {
361 device_extension = (PDEVICE_EXTENSION)device_object->DeviceExtension;
362
363 #ifdef VFD_MOUNT_MANAGER
364 if (OsMajorVersion >= 5) {
365 // Windows 2000 / XP
366 // Notify the mount manager of a VFD volume arrival
367 VfdMountMgrNotifyVolume(device_extension);
368
369 if (device_extension->DriveLetter) {
370 // Create a drive letter via the mount manager.
371 // The mount manager may have created a drive letter
372 // in response to the volume arrival notification above.
373 // In that case, the following call just fails.
374 VfdMountMgrMountPoint(
375 device_extension, device_extension->DriveLetter);
376 // ignoring the error for it is not fatal here
377 }
378 }
379 else
380 #endif // VFD_MOUNT_MANAGER
381 {
382 // Windows NT style drive letter assignment
383 // Simply create a symbolic link here
384 if (device_extension->DriveLetter) {
385 VfdSetLink(
386 device_extension, device_extension->DriveLetter);
387 // ignoring the error for it is not fatal here
388 }
389 }
390
391 device_object = device_object->NextDevice;
392 }
393
394 VFDTRACE(VFDINFO, ("[VFD] VfdReinitialize - OUT\n"));
395 }
396
397 //
398 // Device dedicated thread routine
399 // Dispatch read, write and device I/O request
400 // redirected from the driver dispatch routines
401 //
402 VOID
403 NTAPI
404 VfdDeviceThread (
405 IN PVOID ThreadContext)
406 {
407 PDEVICE_OBJECT device_object;
408 PDEVICE_EXTENSION device_extension;
409 PLIST_ENTRY request;
410 PIRP irp;
411 PIO_STACK_LOCATION io_stack;
412
413 ASSERT(ThreadContext != NULL);
414
415 device_object = (PDEVICE_OBJECT)ThreadContext;
416
417 device_extension = (PDEVICE_EXTENSION)device_object->DeviceExtension;
418
419 KeSetPriorityThread(KeGetCurrentThread(), LOW_REALTIME_PRIORITY);
420
421 for (;;) {
422 // wait for the request event to be signalled
423 KeWaitForSingleObject(
424 &device_extension->RequestEvent,
425 Executive,
426 KernelMode,
427 FALSE,
428 NULL);
429
430 // terminate request ?
431 if (device_extension->TerminateThread) {
432 VFDTRACE(0, ("[VFD] Exitting the I/O thread\n"));
433 PsTerminateSystemThread(STATUS_SUCCESS);
434 }
435
436 // perform requested tasks
437
438 while ((request = ExInterlockedRemoveHeadList(
439 &device_extension->ListHead,
440 &device_extension->ListLock)) != NULL)
441 {
442 irp = CONTAINING_RECORD(request, IRP, Tail.Overlay.ListEntry);
443
444 io_stack = IoGetCurrentIrpStackLocation(irp);
445
446 irp->IoStatus.Information = 0;
447
448 switch (io_stack->MajorFunction) {
449 case IRP_MJ_READ:
450 VfdReadData(device_extension, irp,
451 io_stack->Parameters.Read.Length,
452 &io_stack->Parameters.Read.ByteOffset);
453 break;
454
455 case IRP_MJ_WRITE:
456 VfdWriteData(device_extension, irp,
457 io_stack->Parameters.Write.Length,
458 &io_stack->Parameters.Write.ByteOffset);
459 break;
460
461 case IRP_MJ_DEVICE_CONTROL:
462 VfdIoCtlThread(device_extension, irp,
463 io_stack->Parameters.DeviceIoControl.IoControlCode);
464 break;
465
466 default:
467 // This shouldn't happen...
468 VFDTRACE(0,
469 ("[VFD] %s passed to the I/O thread\n",
470 GetMajorFuncName(io_stack->MajorFunction)));
471
472 irp->IoStatus.Status = STATUS_DRIVER_INTERNAL_ERROR;
473 }
474
475 IoCompleteRequest(irp,
476 (CCHAR)(NT_SUCCESS(irp->IoStatus.Status) ?
477 IO_DISK_INCREMENT : IO_NO_INCREMENT));
478
479 #ifdef VFD_PNP
480 IoReleaseRemoveLock(&device_extension->RemoveLock, irp);
481 #endif // VFD_PNP
482 } // while
483 } // for (;;)
484 }
485
486 //
487 // Copy a UNICODE_STRING adding a trailing NULL characer
488 //
489 PWSTR VfdCopyUnicode(
490 PUNICODE_STRING dst,
491 PUNICODE_STRING src)
492 {
493 RtlZeroMemory(dst, sizeof(UNICODE_STRING));
494
495 dst->MaximumLength =
496 (USHORT)(src->MaximumLength + sizeof(UNICODE_NULL));
497
498 dst->Buffer = (PWSTR)ExAllocatePoolWithTag(
499 PagedPool, dst->MaximumLength, VFD_POOL_TAG);
500
501 if(dst->Buffer) {
502 dst->Length = src->Length;
503 RtlZeroMemory(dst->Buffer, dst->MaximumLength);
504
505 if (src->Length) {
506 RtlCopyMemory(dst->Buffer, src->Buffer, src->Length);
507 }
508 }
509
510 return dst->Buffer;
511 }
512
513 //
514 // Free a UNICODE_STRING buffer
515 //
516 VOID VfdFreeUnicode(
517 PUNICODE_STRING str)
518 {
519 if (str->Buffer) {
520 ExFreePool(str->Buffer);
521 }
522 RtlZeroMemory(str, sizeof(UNICODE_STRING));
523 }