mirror of
https://github.com/reactos/reactos.git
synced 2024-10-18 15:07:43 +00:00
524 lines
12 KiB
C
524 lines
12 KiB
C
|
/*
|
||
|
vfddrv.c
|
||
|
|
||
|
Virtual Floppy Drive for Windows NT platform
|
||
|
Kernel mode driver: miscellaneous driver functions
|
||
|
|
||
|
Copyright (C) 2003-2005 Ken Kato
|
||
|
*/
|
||
|
|
||
|
#include "imports.h"
|
||
|
#include "vfddrv.h"
|
||
|
#include "vfddbg.h"
|
||
|
|
||
|
//
|
||
|
// driver reinitialize routine
|
||
|
// -- create a drive letter for each device
|
||
|
//
|
||
|
#ifdef __cplusplus
|
||
|
extern "C"
|
||
|
#endif // __cplusplus
|
||
|
static VOID
|
||
|
NTAPI
|
||
|
VfdReinitialize(
|
||
|
IN PDRIVER_OBJECT DriverObject,
|
||
|
IN PVOID Context,
|
||
|
IN ULONG Count);
|
||
|
|
||
|
//
|
||
|
// specify code segment
|
||
|
//
|
||
|
#ifdef ALLOC_PRAGMA
|
||
|
#pragma alloc_text(INIT, DriverEntry)
|
||
|
#pragma alloc_text(PAGE, VfdReinitialize)
|
||
|
#pragma alloc_text(PAGE, VfdUnloadDriver)
|
||
|
#pragma alloc_text(PAGE, VfdCreateClose)
|
||
|
#pragma alloc_text(PAGE, VfdCopyUnicode)
|
||
|
#pragma alloc_text(PAGE, VfdFreeUnicode)
|
||
|
#endif // ALLOC_PRAGMA
|
||
|
|
||
|
//
|
||
|
// operating system version
|
||
|
//
|
||
|
#ifndef __REACTOS__
|
||
|
extern ULONG OsMajorVersion = 0;
|
||
|
extern ULONG OsMinorVersion = 0;
|
||
|
extern ULONG OsBuildNumber = 0;
|
||
|
#else
|
||
|
ULONG OsMajorVersion = 0;
|
||
|
ULONG OsMinorVersion = 0;
|
||
|
ULONG OsBuildNumber = 0;
|
||
|
#endif
|
||
|
|
||
|
//
|
||
|
// Trace level flag
|
||
|
//
|
||
|
#if DBG
|
||
|
#ifndef __REACTOS__
|
||
|
extern ULONG TraceFlags = (ULONG)-1;
|
||
|
#else
|
||
|
ULONG TraceFlags = (ULONG)-1;
|
||
|
#endif
|
||
|
#endif // DBG
|
||
|
|
||
|
//
|
||
|
// Driver Entry routine
|
||
|
//
|
||
|
NTSTATUS
|
||
|
NTAPI
|
||
|
DriverEntry (
|
||
|
IN PDRIVER_OBJECT DriverObject,
|
||
|
IN PUNICODE_STRING RegistryPath)
|
||
|
{
|
||
|
NTSTATUS status;
|
||
|
PVFD_DRIVER_EXTENSION driver_extension;
|
||
|
ULONG number_of_devices = VFD_DEFAULT_DEVICES;
|
||
|
|
||
|
ASSERT(DriverObject);
|
||
|
|
||
|
// Get operating system version
|
||
|
|
||
|
PsGetVersion(&OsMajorVersion, &OsMinorVersion, &OsBuildNumber, NULL);
|
||
|
|
||
|
#ifdef VFD_PNP
|
||
|
#define VFD_PNP_TAG "(Plug & Play version)"
|
||
|
#else
|
||
|
#define VFD_PNP_TAG
|
||
|
#endif
|
||
|
|
||
|
VFDTRACE(0, ("[VFD] %s %s" VFD_PNP_TAG "\n",
|
||
|
VFD_PRODUCT_NAME, VFD_DRIVER_VERSION_STR));
|
||
|
|
||
|
VFDTRACE(0,
|
||
|
("[VFD] Running on Windows NT %lu.%lu build %lu\n",
|
||
|
OsMajorVersion, OsMinorVersion, OsBuildNumber));
|
||
|
|
||
|
VFDTRACE(0,
|
||
|
("[VFD] Build Target Environment: %d\n", VER_PRODUCTBUILD));
|
||
|
|
||
|
#ifdef VFD_PNP
|
||
|
|
||
|
// Create device_extension for the driver object to store driver specific
|
||
|
// information. Device specific information are stored in device extension
|
||
|
// for each device object.
|
||
|
|
||
|
status = IoAllocateDriverObjectExtension(
|
||
|
DriverObject,
|
||
|
VFD_DRIVER_EXTENSION_ID,
|
||
|
sizeof(VFD_DRIVER_EXTENSION),
|
||
|
&driver_extension);
|
||
|
|
||
|
if(!NT_SUCCESS(status)) {
|
||
|
VFDTRACE(0, ("[VFD] IoAllocateDriverObjectExtension - %s\n",
|
||
|
GetStatusName(status)));
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
#else // VFD_PNP
|
||
|
|
||
|
// Windows NT doesn't have the IoAllocateDriverObjectExtension
|
||
|
// function and I think there's little point in making a non-PnP
|
||
|
// driver incompatible with Windows NT.
|
||
|
|
||
|
driver_extension = (PVFD_DRIVER_EXTENSION)ExAllocatePoolWithTag(
|
||
|
PagedPool, sizeof(VFD_DRIVER_EXTENSION), VFD_POOL_TAG);
|
||
|
|
||
|
if (!driver_extension) {
|
||
|
VFDTRACE(0, ("[VFD] failed to allocate the driver extension.\n"));
|
||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
||
|
}
|
||
|
|
||
|
#endif // VFD_PNP
|
||
|
|
||
|
RtlZeroMemory(driver_extension, sizeof(VFD_DRIVER_EXTENSION));
|
||
|
|
||
|
//
|
||
|
// Copy the registry path into the driver extension so we can use it later
|
||
|
//
|
||
|
if (VfdCopyUnicode(&(driver_extension->RegistryPath), RegistryPath)) {
|
||
|
|
||
|
//
|
||
|
// Read config values from the registry
|
||
|
//
|
||
|
RTL_QUERY_REGISTRY_TABLE params[3];
|
||
|
ULONG default_devs = VFD_DEFAULT_DEVICES;
|
||
|
#if DBG
|
||
|
ULONG default_trace = (ULONG)-1;
|
||
|
#endif
|
||
|
|
||
|
RtlZeroMemory(params, sizeof(params));
|
||
|
|
||
|
VFDTRACE(0, ("[VFD] Registry Path: %ws\n",
|
||
|
driver_extension->RegistryPath.Buffer));
|
||
|
|
||
|
params[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
||
|
params[0].Name = VFD_REG_DEVICE_NUMBER;
|
||
|
params[0].EntryContext = &number_of_devices;
|
||
|
params[0].DefaultType = REG_DWORD;
|
||
|
params[0].DefaultData = &default_devs;
|
||
|
params[0].DefaultLength = sizeof(ULONG);
|
||
|
|
||
|
#if DBG
|
||
|
params[1].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
||
|
params[1].Name = VFD_REG_TRACE_FLAGS;
|
||
|
params[1].EntryContext = &TraceFlags;
|
||
|
params[1].DefaultType = REG_DWORD;
|
||
|
params[1].DefaultData = &default_trace;
|
||
|
params[1].DefaultLength = sizeof(ULONG);
|
||
|
#endif // DBG
|
||
|
|
||
|
status = RtlQueryRegistryValues(
|
||
|
RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
|
||
|
driver_extension->RegistryPath.Buffer,
|
||
|
params, NULL, NULL);
|
||
|
|
||
|
if (!NT_SUCCESS(status) ||
|
||
|
number_of_devices == 0 ||
|
||
|
number_of_devices > VFD_MAXIMUM_DEVICES) {
|
||
|
number_of_devices = VFD_DEFAULT_DEVICES;
|
||
|
}
|
||
|
|
||
|
VFDTRACE(0,("[VFD] NumberOfDevices = %lu\n", number_of_devices));
|
||
|
VFDTRACE(0,("[VFD] TraceFlags = 0x%08x\n", TraceFlags));
|
||
|
}
|
||
|
else {
|
||
|
VFDTRACE(0, ("[VFD] failed to allocate the registry path buffer.\n"));
|
||
|
// this error is not fatal
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Create VFD device objects
|
||
|
//
|
||
|
do {
|
||
|
#ifdef VFD_PNP
|
||
|
status = VfdCreateDevice(DriverObject, NULL);
|
||
|
#else // VFD_PNP
|
||
|
status = VfdCreateDevice(DriverObject, driver_extension);
|
||
|
#endif // VFD_PNP
|
||
|
|
||
|
if (!NT_SUCCESS(status)) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
while (driver_extension->NumberOfDevices < number_of_devices);
|
||
|
|
||
|
if (!driver_extension->NumberOfDevices) {
|
||
|
|
||
|
// Failed to create even one device
|
||
|
|
||
|
VfdFreeUnicode(&(driver_extension->RegistryPath));
|
||
|
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
// Setup dispatch table
|
||
|
|
||
|
DriverObject->MajorFunction[IRP_MJ_CREATE] = VfdCreateClose;
|
||
|
DriverObject->MajorFunction[IRP_MJ_CLOSE] = VfdCreateClose;
|
||
|
DriverObject->MajorFunction[IRP_MJ_READ] = VfdReadWrite;
|
||
|
DriverObject->MajorFunction[IRP_MJ_WRITE] = VfdReadWrite;
|
||
|
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = VfdDeviceControl;
|
||
|
|
||
|
#ifdef VFD_PNP
|
||
|
DriverObject->MajorFunction[IRP_MJ_PNP] = VfdPlugAndPlay;
|
||
|
DriverObject->MajorFunction[IRP_MJ_POWER] = VfdPowerControl;
|
||
|
DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = VfdSystemControl;
|
||
|
DriverObject->DriverExtension->AddDevice = VfdAddDevice;
|
||
|
#endif // VFDPNP
|
||
|
|
||
|
DriverObject->DriverUnload = VfdUnloadDriver;
|
||
|
|
||
|
// Register the driver reinitialize routine to be called
|
||
|
// *after* the DriverEntry routine returns
|
||
|
|
||
|
IoRegisterDriverReinitialization(
|
||
|
DriverObject, VfdReinitialize, NULL);
|
||
|
|
||
|
VFDTRACE(VFDINFO,
|
||
|
("[VFD] driver initialized with %lu devices.\n",
|
||
|
driver_extension->NumberOfDevices));
|
||
|
|
||
|
return STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Driver unload routine
|
||
|
// Cleans up the device objects and other resources
|
||
|
//
|
||
|
VOID
|
||
|
NTAPI
|
||
|
VfdUnloadDriver (
|
||
|
IN PDRIVER_OBJECT DriverObject)
|
||
|
{
|
||
|
PDEVICE_OBJECT device_object;
|
||
|
PVFD_DRIVER_EXTENSION driver_extension;
|
||
|
|
||
|
VFDTRACE(VFDINFO, ("[VFD] VfdUnloadDriver - IN\n"));
|
||
|
|
||
|
device_object = DriverObject->DeviceObject;
|
||
|
|
||
|
#ifdef VFD_PNP
|
||
|
driver_extension = IoGetDriverObjectExtension(
|
||
|
DriverObject, VFD_DRIVER_EXTENSION_ID);
|
||
|
#else
|
||
|
if (device_object && device_object->DeviceExtension) {
|
||
|
driver_extension =
|
||
|
((PDEVICE_EXTENSION)(device_object->DeviceExtension))->DriverExtension;
|
||
|
}
|
||
|
else {
|
||
|
driver_extension = NULL;
|
||
|
}
|
||
|
#endif // VFD_PNP
|
||
|
|
||
|
//
|
||
|
// Delete all remaining device objects
|
||
|
//
|
||
|
while (device_object) {
|
||
|
|
||
|
PDEVICE_OBJECT next_device = device_object->NextDevice;
|
||
|
|
||
|
VfdDeleteDevice(device_object);
|
||
|
|
||
|
device_object = next_device;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Release the driver extension and the registry path buffer
|
||
|
//
|
||
|
if (driver_extension) {
|
||
|
|
||
|
if (driver_extension->RegistryPath.Buffer) {
|
||
|
VFDTRACE(0, ("[VFD] Releasing the registry path buffer\n"));
|
||
|
ExFreePool(driver_extension->RegistryPath.Buffer);
|
||
|
}
|
||
|
|
||
|
#ifndef VFD_PNP
|
||
|
// The system takes care of freeing the driver extension
|
||
|
// allocated with IoAllocateDriverObjectExtension in a PnP driver.
|
||
|
VFDTRACE(0, ("[VFD] Releasing the driver extension\n"));
|
||
|
ExFreePool(driver_extension);
|
||
|
#endif // VFD_PNP
|
||
|
}
|
||
|
|
||
|
VFDTRACE(VFDINFO, ("[VFD] VfdUnloadDriver - OUT\n"));
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// IRP_MJ_CREATE and IRP_MJ_CLOSE handler
|
||
|
// Really nothing to do here...
|
||
|
//
|
||
|
NTSTATUS
|
||
|
NTAPI
|
||
|
VfdCreateClose (
|
||
|
IN PDEVICE_OBJECT DeviceObject,
|
||
|
IN PIRP Irp)
|
||
|
{
|
||
|
#if DBG
|
||
|
if (DeviceObject && DeviceObject->DeviceExtension &&
|
||
|
((PDEVICE_EXTENSION)DeviceObject->DeviceExtension)->DeviceName.Buffer) {
|
||
|
|
||
|
VFDTRACE(VFDINFO, ("[VFD] %-40s %ws\n",
|
||
|
GetMajorFuncName(IoGetCurrentIrpStackLocation(Irp)->MajorFunction),
|
||
|
((PDEVICE_EXTENSION)DeviceObject->DeviceExtension)->DeviceName.Buffer));
|
||
|
}
|
||
|
else {
|
||
|
VFDTRACE(VFDINFO, ("[VFD] %-40s %p\n",
|
||
|
GetMajorFuncName(IoGetCurrentIrpStackLocation(Irp)->MajorFunction),
|
||
|
DeviceObject));
|
||
|
}
|
||
|
#endif // DBG
|
||
|
|
||
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
||
|
Irp->IoStatus.Information = FILE_OPENED;
|
||
|
|
||
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||
|
|
||
|
return STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Called after the DriverEntry routine has returned
|
||
|
// (Re)Create a persistent drive letter for each device
|
||
|
//
|
||
|
VOID
|
||
|
NTAPI
|
||
|
VfdReinitialize(
|
||
|
IN PDRIVER_OBJECT DriverObject,
|
||
|
IN PVOID Context,
|
||
|
IN ULONG Count)
|
||
|
{
|
||
|
PDEVICE_OBJECT device_object;
|
||
|
PDEVICE_EXTENSION device_extension;
|
||
|
|
||
|
UNREFERENCED_PARAMETER(Context);
|
||
|
UNREFERENCED_PARAMETER(Count);
|
||
|
|
||
|
VFDTRACE(VFDINFO, ("[VFD] VfdReinitialize - IN\n"));
|
||
|
|
||
|
device_object = DriverObject->DeviceObject;
|
||
|
|
||
|
while (device_object) {
|
||
|
device_extension = (PDEVICE_EXTENSION)device_object->DeviceExtension;
|
||
|
|
||
|
#ifdef VFD_MOUNT_MANAGER
|
||
|
if (OsMajorVersion >= 5) {
|
||
|
// Windows 2000 / XP
|
||
|
// Notify the mount manager of a VFD volume arrival
|
||
|
VfdMountMgrNotifyVolume(device_extension);
|
||
|
|
||
|
if (device_extension->DriveLetter) {
|
||
|
// Create a drive letter via the mount manager.
|
||
|
// The mount manager may have created a drive letter
|
||
|
// in response to the volume arrival notification above.
|
||
|
// In that case, the following call just fails.
|
||
|
VfdMountMgrMountPoint(
|
||
|
device_extension, device_extension->DriveLetter);
|
||
|
// ignoring the error for it is not fatal here
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
#endif // VFD_MOUNT_MANAGER
|
||
|
{
|
||
|
// Windows NT style drive letter assignment
|
||
|
// Simply create a symbolic link here
|
||
|
if (device_extension->DriveLetter) {
|
||
|
VfdSetLink(
|
||
|
device_extension, device_extension->DriveLetter);
|
||
|
// ignoring the error for it is not fatal here
|
||
|
}
|
||
|
}
|
||
|
|
||
|
device_object = device_object->NextDevice;
|
||
|
}
|
||
|
|
||
|
VFDTRACE(VFDINFO, ("[VFD] VfdReinitialize - OUT\n"));
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Device dedicated thread routine
|
||
|
// Dispatch read, write and device I/O request
|
||
|
// redirected from the driver dispatch routines
|
||
|
//
|
||
|
VOID
|
||
|
NTAPI
|
||
|
VfdDeviceThread (
|
||
|
IN PVOID ThreadContext)
|
||
|
{
|
||
|
PDEVICE_OBJECT device_object;
|
||
|
PDEVICE_EXTENSION device_extension;
|
||
|
PLIST_ENTRY request;
|
||
|
PIRP irp;
|
||
|
PIO_STACK_LOCATION io_stack;
|
||
|
|
||
|
ASSERT(ThreadContext != NULL);
|
||
|
|
||
|
device_object = (PDEVICE_OBJECT)ThreadContext;
|
||
|
|
||
|
device_extension = (PDEVICE_EXTENSION)device_object->DeviceExtension;
|
||
|
|
||
|
KeSetPriorityThread(KeGetCurrentThread(), LOW_REALTIME_PRIORITY);
|
||
|
|
||
|
for (;;) {
|
||
|
// wait for the request event to be signalled
|
||
|
KeWaitForSingleObject(
|
||
|
&device_extension->RequestEvent,
|
||
|
Executive,
|
||
|
KernelMode,
|
||
|
FALSE,
|
||
|
NULL);
|
||
|
|
||
|
// terminate request ?
|
||
|
if (device_extension->TerminateThread) {
|
||
|
VFDTRACE(0, ("[VFD] Exitting the I/O thread\n"));
|
||
|
PsTerminateSystemThread(STATUS_SUCCESS);
|
||
|
}
|
||
|
|
||
|
// perform requested tasks
|
||
|
|
||
|
while ((request = ExInterlockedRemoveHeadList(
|
||
|
&device_extension->ListHead,
|
||
|
&device_extension->ListLock)) != NULL)
|
||
|
{
|
||
|
irp = CONTAINING_RECORD(request, IRP, Tail.Overlay.ListEntry);
|
||
|
|
||
|
io_stack = IoGetCurrentIrpStackLocation(irp);
|
||
|
|
||
|
irp->IoStatus.Information = 0;
|
||
|
|
||
|
switch (io_stack->MajorFunction) {
|
||
|
case IRP_MJ_READ:
|
||
|
VfdReadData(device_extension, irp,
|
||
|
io_stack->Parameters.Read.Length,
|
||
|
&io_stack->Parameters.Read.ByteOffset);
|
||
|
break;
|
||
|
|
||
|
case IRP_MJ_WRITE:
|
||
|
VfdWriteData(device_extension, irp,
|
||
|
io_stack->Parameters.Write.Length,
|
||
|
&io_stack->Parameters.Write.ByteOffset);
|
||
|
break;
|
||
|
|
||
|
case IRP_MJ_DEVICE_CONTROL:
|
||
|
VfdIoCtlThread(device_extension, irp,
|
||
|
io_stack->Parameters.DeviceIoControl.IoControlCode);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
// This shouldn't happen...
|
||
|
VFDTRACE(0,
|
||
|
("[VFD] %s passed to the I/O thread\n",
|
||
|
GetMajorFuncName(io_stack->MajorFunction)));
|
||
|
|
||
|
irp->IoStatus.Status = STATUS_DRIVER_INTERNAL_ERROR;
|
||
|
}
|
||
|
|
||
|
IoCompleteRequest(irp,
|
||
|
(CCHAR)(NT_SUCCESS(irp->IoStatus.Status) ?
|
||
|
IO_DISK_INCREMENT : IO_NO_INCREMENT));
|
||
|
|
||
|
#ifdef VFD_PNP
|
||
|
IoReleaseRemoveLock(&device_extension->RemoveLock, irp);
|
||
|
#endif // VFD_PNP
|
||
|
} // while
|
||
|
} // for (;;)
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Copy a UNICODE_STRING adding a trailing NULL characer
|
||
|
//
|
||
|
PWSTR VfdCopyUnicode(
|
||
|
PUNICODE_STRING dst,
|
||
|
PUNICODE_STRING src)
|
||
|
{
|
||
|
RtlZeroMemory(dst, sizeof(UNICODE_STRING));
|
||
|
|
||
|
dst->MaximumLength =
|
||
|
(USHORT)(src->MaximumLength + sizeof(UNICODE_NULL));
|
||
|
|
||
|
dst->Buffer = (PWSTR)ExAllocatePoolWithTag(
|
||
|
PagedPool, dst->MaximumLength, VFD_POOL_TAG);
|
||
|
|
||
|
if(dst->Buffer) {
|
||
|
dst->Length = src->Length;
|
||
|
RtlZeroMemory(dst->Buffer, dst->MaximumLength);
|
||
|
|
||
|
if (src->Length) {
|
||
|
RtlCopyMemory(dst->Buffer, src->Buffer, src->Length);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return dst->Buffer;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Free a UNICODE_STRING buffer
|
||
|
//
|
||
|
VOID VfdFreeUnicode(
|
||
|
PUNICODE_STRING str)
|
||
|
{
|
||
|
if (str->Buffer) {
|
||
|
ExFreePool(str->Buffer);
|
||
|
}
|
||
|
RtlZeroMemory(str, sizeof(UNICODE_STRING));
|
||
|
}
|