diff options
author | Yaniv Kamay <ykamay@redhat.com> | 2009-11-04 15:56:13 +0200 |
---|---|---|
committer | Yaniv Kamay <ykamay@redhat.com> | 2009-11-04 15:56:13 +0200 |
commit | 05d99dbcb757c9d1d98803b393fb696de8d74d7c (patch) | |
tree | c48aad35f2c69839b31bee597f2f1330b3ecd999 |
fresh start
-rw-r--r-- | win/makefile | 7 | ||||
-rw-r--r-- | win/sources | 11 | ||||
-rw-r--r-- | win/vdi_port.c | 1223 | ||||
-rw-r--r-- | win/vdi_port.rc | 29 | ||||
-rw-r--r-- | win/vdiport.inf | 53 |
5 files changed, 1323 insertions, 0 deletions
diff --git a/win/makefile b/win/makefile new file mode 100644 index 0000000..9c985f5 --- /dev/null +++ b/win/makefile @@ -0,0 +1,7 @@ +#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the driver components of the Windows NT DDK
+#
+
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/win/sources b/win/sources new file mode 100644 index 0000000..8b6ce11 --- /dev/null +++ b/win/sources @@ -0,0 +1,11 @@ +TARGETNAME=vdiport
+TARGETPATH=obj
+TARGETTYPE=DRIVER
+
+INCLUDES=$(SPICE_COMMON_DIR)
+
+TARGETLIBS= $(TARGETLIBS) $(DDK_LIB_PATH)\uuid.lib \
+ $(DDK_LIB_PATH)\wdmsec.lib
+
+SOURCES= vdi_port.c \
+ vdi_port.rc
diff --git a/win/vdi_port.c b/win/vdi_port.c new file mode 100644 index 0000000..bc97e81 --- /dev/null +++ b/win/vdi_port.c @@ -0,0 +1,1223 @@ +/* + Copyright (C) 2009 Red Hat, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdarg.h> +#include <wdm.h> +#include <ntstrsafe.h> +#include <wdmguid.h> + +#include "vdi_dev.h" + +#define USE_REMOVE_LOCK + +#define DEBUG_PRINT(arg) DebugPrint arg + +#define DEVICE_NAME L"\\Device\\VDIPort" +#define DOS_DEVICE_NAME L"\\DosDevices\\VDIPort" +#define DRIVER_NAME "VDIPort" +#define TEMP_BUFFER_SIZE 1024 + +#define VDIPORT_ALLOC_TAG 'pidv' + +#define DBG_LEVEL 1 + +#define MIN(a, b) (((a) > (b)) ? (b) : (a)) + +#define VDIPORT_ERR_ERROR -1 +#define VDIPORT_ERR_RESET -2 +#define VDIPORT_ERR_IO_ERROR -3 + +typedef enum { + PASSIVE, + STARTING, + STARTED, + STOPPING, + STOPED, + SURPRISE, + REMOVING, + REMOVED, +} PnPState; + +typedef struct VDIPortExt { + DEVICE_OBJECT *physical; + DEVICE_OBJECT *device; + UNICODE_STRING interface_name; + DEVICE_OBJECT *lower_dev; + PnPState pnp_state; + PnPState previos_pnp_state; + BUS_INTERFACE_STANDARD bus_interface; + PKINTERRUPT interrupt; + PUCHAR io_base; + PULONG notify_port; + PULONG connection_port; + PULONG irq_port; + ULONG mem_length; + VDIPortRam *ram; + UINT32 generation; + UINT32 read_pos; + PKEVENT user_event; + KSPIN_LOCK event_lock; + LONG exclusive_gurd; +#ifdef USE_REMOVE_LOCK + IO_REMOVE_LOCK remove_lock; +#endif +} VDIPortExt; + +NTSTATUS DriverEntry(IN PDRIVER_OBJECT driver, IN PUNICODE_STRING registry_path); +NTSTATUS VDIPortAddDevice(IN PDRIVER_OBJECT device, IN PDEVICE_OBJECT physical); +NTSTATUS VDIPortCreate(IN PDEVICE_OBJECT device, IN PIRP irp); +NTSTATUS VDIPortClose(IN PDEVICE_OBJECT device, IN PIRP irp); +NTSTATUS VDIPortDispatchPnP(IN PDEVICE_OBJECT device, IN PIRP irp); +NTSTATUS VDIPortDeviceControl(IN PDEVICE_OBJECT device, IN PIRP irp); +NTSTATUS VDIPortSystemControl(IN PDEVICE_OBJECT device, IN PIRP irp); +NTSTATUS VDIPortPower(IN PDEVICE_OBJECT device, IN PIRP irp); +VOID VDIPortUnload(IN PDRIVER_OBJECT driver); + +NTSTATUS VDIPortRead(IN PDEVICE_OBJECT device, IN PIRP irp); +NTSTATUS VDIPortWrite(IN PDEVICE_OBJECT device, IN PIRP irp); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (INIT, DriverEntry) +#pragma alloc_text (PAGE, VDIPortUnload) +#pragma alloc_text (PAGE, VDIPortAddDevice) +#pragma alloc_text (PAGE, VDIPortCreate) +#pragma alloc_text (PAGE, VDIPortClose) +#pragma alloc_text (PAGE, VDIPortDispatchPnP) +#endif + + +//c77a1bf4-6901-4aae-9ca5-ff3b984a7598 +static GUID GUID_DEVINTERFACE_VDI_PORT = {0xc77a1bf4, 0x6901, 0x4aae, + { 0x9c, 0xa5, 0xff, 0x3b, 0x98, 0x4a, 0x75, 0x98}}; + +static GUID GUID_SYSCLASS = {0x4d36e97d, 0xe325, 0x11ce, + { 0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18}}; + + +static void DebugPrint(int level, const char *message, ...) +{ + NTSTATUS status; + UCHAR buf[1024]; + va_list ap; + + if (level > DBG_LEVEL) { + return; + } + va_start(ap, message); + status = RtlStringCbVPrintfA(buf, sizeof(buf), message, ap); + if(!NT_SUCCESS(status)) { + KdPrint((DRIVER_NAME": sprintf error")); + } else { + KdPrint((DRIVER_NAME":%s", buf)); + } + va_end(ap); +} + +static void RingCleanup(VDIPortExt *ext) +{ + VDIPortRing *ring = &ext->ram->output; + int do_notify = FALSE; + + while (!RING_IS_EMPTY(ring)) { + VDIPortPacket *packet; + int notify = FALSE; + packet = RING_CONS_ITEM(ring); + if (packet->gen < ext->generation || (packet->gen - ext->generation) > (1U << 31)) { + RING_POP(ring, notify); + do_notify = notify || do_notify; + } else { + break; + } + } + if (do_notify) { + WRITE_PORT_ULONG(ext->notify_port, 0); + } + ext->read_pos = 0; +} + +NTSTATUS DriverEntry(IN PDRIVER_OBJECT driver, IN PUNICODE_STRING registry_path) +{ + DEBUG_PRINT((3, "%s: start\n", __FUNCTION__)); + + driver->MajorFunction[IRP_MJ_PNP] = VDIPortDispatchPnP; + driver->MajorFunction[IRP_MJ_CREATE]= VDIPortCreate; + driver->MajorFunction[IRP_MJ_CLOSE] = VDIPortClose; + driver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = VDIPortDeviceControl; + driver->MajorFunction[IRP_MJ_READ] = VDIPortRead; + driver->MajorFunction[IRP_MJ_WRITE] = VDIPortWrite; + driver->MajorFunction[IRP_MJ_POWER] = VDIPortPower; + //driver->MajorFunction[IRP_MJ_CLEANUP]= ; + driver->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = VDIPortSystemControl; + driver->DriverExtension->AddDevice = VDIPortAddDevice; + driver->DriverUnload = VDIPortUnload; + + DEBUG_PRINT((4, "%s: done\n", __FUNCTION__)); + return STATUS_SUCCESS; +} + +VOID VDIPortUnload(IN PDRIVER_OBJECT driver) +{ + PAGED_CODE(); + DEBUG_PRINT((3, "%s start\n", __FUNCTION__)); + + DEBUG_PRINT((4, "%s done\n", __FUNCTION__)); +} + +static NTSTATUS InitializeBusInterface(VDIPortExt *ext) +{ + KEVENT event; + NTSTATUS status; + PIRP irp; + IO_STATUS_BLOCK status_block; + PIO_STACK_LOCATION stack; + PDEVICE_OBJECT device; + + PAGED_CODE(); + + DEBUG_PRINT((6, "%s: start\n", __FUNCTION__)); + + KeInitializeEvent(&event, NotificationEvent, FALSE); + + device = IoGetAttachedDeviceReference(ext->device); + + irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP, device, NULL, + 0, NULL, &event, &status_block); + if (irp == NULL) { + status = STATUS_INSUFFICIENT_RESOURCES; + goto error_1; + } + + stack = IoGetNextIrpStackLocation(irp); + stack->MinorFunction = IRP_MN_QUERY_INTERFACE; + stack->Parameters.QueryInterface.InterfaceType = (LPGUID)&GUID_BUS_INTERFACE_STANDARD; + stack->Parameters.QueryInterface.Size = sizeof(BUS_INTERFACE_STANDARD); + stack->Parameters.QueryInterface.Version = 1; + stack->Parameters.QueryInterface.Interface = (PINTERFACE)&ext->bus_interface; + stack->Parameters.QueryInterface.InterfaceSpecificData = NULL; + irp->IoStatus.Status = STATUS_NOT_SUPPORTED; + + status = IoCallDriver(device, irp); + if (status == STATUS_PENDING) { + status = KeWaitForSingleObject( &event, Executive, KernelMode, FALSE, NULL); + ASSERT(NT_SUCCESS(status)); + status = status_block.Status; + } + +error_1: + ObDereferenceObject(device); + + DEBUG_PRINT((7, "%s: done\n", __FUNCTION__)); + return status; +} + +NTSTATUS VDIPortAddDevice(IN PDRIVER_OBJECT driver, IN PDEVICE_OBJECT physical_device) +{ + NTSTATUS status; + DEVICE_OBJECT *device; + DEVICE_OBJECT *lower_dev; + VDIPortExt *ext; + UNICODE_STRING name; + UNICODE_STRING win32_name; + + PAGED_CODE(); + + DEBUG_PRINT((3, "%s: start\n", __FUNCTION__)); + RtlInitUnicodeString(&name, DEVICE_NAME); + status = IoCreateDevice(driver, sizeof(*ext), &name, FILE_DEVICE_UNKNOWN, + FILE_DEVICE_SECURE_OPEN, TRUE, &device); + + if (!NT_SUCCESS(status)) { + DEBUG_PRINT((0, "%s: create device failed %u\n", __FUNCTION__, status)); + return status; + } + + RtlInitUnicodeString(&win32_name, DOS_DEVICE_NAME ); + IoCreateSymbolicLink(&win32_name, &name); + + ext = device->DeviceExtension; + RtlZeroMemory(ext, sizeof(*ext)); + ext->device = device; + ext->physical = physical_device; + ext->pnp_state = PASSIVE; + KeInitializeSpinLock(&ext->event_lock); +#ifdef USE_REMOVE_LOCK + IoInitializeRemoveLock(&ext->remove_lock, VDIPORT_ALLOC_TAG, 0, 0); +#endif + + device->Flags |= DO_POWER_PAGABLE | DO_BUFFERED_IO; + + lower_dev = IoAttachDeviceToDeviceStack(device, physical_device); + + if (!lower_dev) { + DEBUG_PRINT((0, "%s: attach device failed %u\n", __FUNCTION__)); + goto error_1; + } + + ext->lower_dev = lower_dev; + + status = InitializeBusInterface(ext); + if (!NT_SUCCESS(status)) { + DEBUG_PRINT((0, "%s: init bus interface failed %u\n", __FUNCTION__, status)); + goto error_2; + } + + status = IoRegisterDeviceInterface(physical_device, &GUID_DEVINTERFACE_VDI_PORT, + NULL, &ext->interface_name); + + if (!NT_SUCCESS(status)) { + DEBUG_PRINT((0, "%s: register interface failed %u\n", __FUNCTION__, status)); + goto error_2; + } + + device->Flags &= ~DO_DEVICE_INITIALIZING; + + DEBUG_PRINT((4, "%s: done\n", __FUNCTION__)); + + return STATUS_SUCCESS; + +error_2: + IoDetachDevice(ext->lower_dev); + +error_1: + + IoDeleteDevice(device); + return status; +} + +static NTSTATUS IoIncrement(VDIPortExt *ext) +{ +#ifdef USE_REMOVE_LOCK + return IoAcquireRemoveLock(&ext->remove_lock, NULL); +#else + return STATUS_SUCCESS; +#endif +} + +static void IoDecrement(VDIPortExt *ext) +{ +#ifdef USE_REMOVE_LOCK + IoReleaseRemoveLock(&ext->remove_lock, NULL); +#endif +} + +NTSTATUS VDIPortCreate(IN PDEVICE_OBJECT device, IN PIRP irp) +{ + PIO_STACK_LOCATION stack; + VDIPortExt *ext; + NTSTATUS status; + LONG gurd_val; + + PAGED_CODE(); + + DEBUG_PRINT((3, "%s: start\n", __FUNCTION__)); + + ext = (VDIPortExt *)device->DeviceExtension; + status = IoIncrement(ext); + if (!NT_SUCCESS(status)) { + DEBUG_PRINT((0, "%s: io increment failed\n", __FUNCTION__)); + goto error_1; + } + + stack = IoGetCurrentIrpStackLocation(irp); + + if (stack->FileObject->FileName.Length != 0) { + DEBUG_PRINT((0, "%s: has name\n", __FUNCTION__)); + status = STATUS_INVALID_PARAMETER; + goto error_2; + } + + gurd_val = InterlockedExchange(&ext->exclusive_gurd, 1); + if (gurd_val) { + DEBUG_PRINT((0, "%s: device is busy\n", __FUNCTION__)); + status = STATUS_DEVICE_BUSY; + goto error_2; + } + +#ifndef USE_REMOVE_LOCK + if (ext->pnp_state == REMOVED) { + DEBUG_PRINT((0, "%s: REMOVED\n", __FUNCTION__)); + status = STATUS_NO_SUCH_DEVICE ; + goto error_2; + } +#endif + + ext->generation = READ_PORT_ULONG(ext->connection_port); + RingCleanup(ext); + +error_2: + IoDecrement(ext); + +error_1: + irp->IoStatus.Information = 0; + irp->IoStatus.Status = status; + IoCompleteRequest(irp, IO_NO_INCREMENT); + DEBUG_PRINT((4, "%s: done\n", __FUNCTION__)); + return status; +} + +static void SetUserEvent(VDIPortExt *ext, PKEVENT user_event) +{ + if (ext->user_event) { + PKEVENT old_event; + KIRQL irql; + + KeAcquireSpinLock(&ext->event_lock, &irql); + old_event = ext->user_event; + ext->user_event = NULL; + KeReleaseSpinLock(&ext->event_lock, irql); + ObDereferenceObject(old_event); + } + if ((ext->user_event = user_event)) { + ext->ram->int_mask = ~0; + } else { + ext->ram->int_mask = 0; + } + WRITE_PORT_ULONG(ext->irq_port, 0); +} + +NTSTATUS VDIPortClose(IN PDEVICE_OBJECT device, IN PIRP irp) +{ + PIO_STACK_LOCATION stack; + VDIPortExt *ext; + NTSTATUS status; + LONG gurd_val; + + PAGED_CODE(); + + DEBUG_PRINT((3, "%s: start\n", __FUNCTION__)); + stack = IoGetCurrentIrpStackLocation(irp); + ext = (VDIPortExt *)device->DeviceExtension; + status = IoIncrement(ext); + ASSERT(NT_SUCCESS(status)); + SetUserEvent(ext, NULL); + WRITE_PORT_ULONG(ext->connection_port, 0); + + status = STATUS_SUCCESS; + irp->IoStatus.Information = 0; + irp->IoStatus.Status = status; + IoCompleteRequest(irp, IO_NO_INCREMENT); + if ((gurd_val = InterlockedExchange(&ext->exclusive_gurd, 0)) == 0) { + DEBUG_PRINT((0, "%s: gurd val was zero\n", __FUNCTION__)); + } + IoDecrement(ext); + DEBUG_PRINT((4, "%s: done\n", __FUNCTION__)); + return status; +} + +typedef struct ScheduleContext { + PIO_WORKITEM item; + VOID *arg1; + VOID *arg2; +} ScheduleContext; + + +#ifdef ALLOC_PRAGMA +static NTSTATUS Prob(VDIPortExt *ext, PIRP irp); +#pragma alloc_text (PAGE, Prob) +#endif +static NTSTATUS Prob(VDIPortExt *ext, PIRP irp) +{ + NTSTATUS status = STATUS_SUCCESS; + PCI_COMMON_CONFIG pci_config; + ULONG n; + + PAGED_CODE(); + + DEBUG_PRINT((9, "%s: start\n", __FUNCTION__)); + + n = ext->bus_interface.GetBusData(ext->bus_interface.Context, PCI_WHICHSPACE_CONFIG, + &pci_config, 0, sizeof(pci_config)); + + if (n != sizeof(pci_config)) { + DEBUG_PRINT((0, "%s: read pci config failed\n", __FUNCTION__)); + return STATUS_INVALID_DEVICE_REQUEST; + } + + if (pci_config.VendorID != REDHAT_PCI_VENDOR_ID || + pci_config.DeviceID != VDI_PORT_DEVICE_ID) { + DEBUG_PRINT((0, "%s: invalid pci device. vendor 0x%x id 0x%x\n", + __FUNCTION__, + (ULONG)pci_config.VendorID, + (ULONG)pci_config.DeviceID)); + return STATUS_DEVICE_DOES_NOT_EXIST; + } + if (pci_config.RevisionID != VDI_PORT_REVISION) { + DEBUG_PRINT((0, "%s: bad revision 0x%x expect 0x%x\n", + __FUNCTION__, + (ULONG)pci_config.RevisionID, + VDI_PORT_REVISION)); + return STATUS_DEVICE_PROTOCOL_ERROR; + } + + return STATUS_SUCCESS; + +} + +static VOID DcpForIsr(IN PKDPC dpc, IN PDEVICE_OBJECT device, + IN PIRP irp, IN PVOID context) +{ + VDIPortExt *ext = (VDIPortExt *)context; + KIRQL irql; + + UINT32 pending = InterlockedExchange(&ext->ram->int_pending, 0); + KeAcquireSpinLock(&ext->event_lock, &irql); + if (ext->user_event) { + KeSetEvent(ext->user_event, IO_MOUSE_INCREMENT, FALSE); + } + KeReleaseSpinLock(&ext->event_lock, irql); + ext->ram->int_mask = ~0; + WRITE_PORT_ULONG(ext->irq_port, 0); +} + +BOOLEAN InterruptHandler(IN PKINTERRUPT interrupt, IN PVOID context) +{ + VDIPortExt *ext = (VDIPortExt *)context; + + if (!(ext->ram->int_pending & ext->ram->int_mask)) { + return FALSE; + } + ext->ram->int_mask = 0; + WRITE_PORT_ULONG(ext->irq_port, 0); + IoRequestDpc(ext->device, NULL, ext); + return TRUE; +} + +#ifdef ALLOC_PRAGMA +static void ReleaseHardwareRes(VDIPortExt *ext); +#pragma alloc_text (PAGE, ReleaseHardwareRes) +#endif +static void ReleaseHardwareRes(VDIPortExt *ext) +{ + PAGED_CODE(); + + if (ext->ram) { + MmUnmapIoSpace(ext->ram, ext->mem_length); + ext->ram = NULL; + ext->mem_length = 0; + } + if (ext->interrupt) { + IoDisconnectInterrupt(ext->interrupt); + ext->interrupt = NULL; + } +} + +#ifdef ALLOC_PRAGMA +static NTSTATUS InitHwResources(VDIPortExt *ext, PIRP irp); +#pragma alloc_text (PAGE, InitHwResources) +#endif +NTSTATUS InitHwResources(VDIPortExt *ext, PIRP irp) +{ + PCM_PARTIAL_RESOURCE_LIST resource_list; + PCM_PARTIAL_RESOURCE_DESCRIPTOR descriptor; + PIO_STACK_LOCATION stack; + BOOLEAN io_present = FALSE; + BOOLEAN mem_present = FALSE; + BOOLEAN int_present = FALSE; + NTSTATUS status; + UINT32 i; + + PAGED_CODE(); + + DEBUG_PRINT((9, "%s: start\n", __FUNCTION__)); + + stack = IoGetCurrentIrpStackLocation(irp); + + if (!stack->Parameters.StartDevice.AllocatedResourcesTranslated) { + return STATUS_DEVICE_CONFIGURATION_ERROR; + } + + resource_list = + &stack->Parameters.StartDevice.AllocatedResourcesTranslated->List[0].PartialResourceList; + + descriptor = &resource_list->PartialDescriptors[0]; + + for (i = 0; i < resource_list->Count; i++, descriptor++) { + switch (descriptor->Type) { + case CmResourceTypePort: + DEBUG_PRINT((11, "%s: io base %x length %d\n", + __FUNCTION__, + descriptor->u.Port.Start.LowPart, + descriptor->u.Port.Length)); + + if (io_present || descriptor->u.Port.Length < VDI_PORT_IO_RANGE_SIZE) { + status = STATUS_DEVICE_CONFIGURATION_ERROR; + goto error; + } + ext->io_base = (PUCHAR)ULongToPtr(descriptor->u.Port.Start.LowPart); + ext->connection_port = (PULONG)(ext->io_base + VDI_PORT_IO_CONNECTION); + ext->notify_port = (PULONG)(ext->io_base + VDI_PORT_IO_NOTIFY); + ext->irq_port = (PULONG)(ext->io_base + VDI_PORT_IO_UPDATE_IRQ); + + io_present = TRUE; + break; + + case CmResourceTypeMemory: + DEBUG_PRINT((11, "%s: mem base 0x%llx length %u\n", + __FUNCTION__, + (UINT64)descriptor->u.Memory.Start.QuadPart, + descriptor->u.Memory.Length)); + + if (mem_present || descriptor->u.Memory.Length < sizeof(VDIPortRam)) { + status = STATUS_DEVICE_CONFIGURATION_ERROR; + goto error; + } + + ext->mem_length = descriptor->u.Memory.Length, + ext->ram = (VDIPortRam *)MmMapIoSpace(descriptor->u.Memory.Start, ext->mem_length, + MmCached); + if (ext->ram->magic != VDI_PORT_MAGIC) { + DEBUG_PRINT((0, "%s: bad magic 0x%x 0x%x\n", __FUNCTION__, ext->ram->magic, + VDI_PORT_MAGIC)); + status = STATUS_DEVICE_CONFIGURATION_ERROR; + goto error; + } + ext->ram->int_mask = 0; + mem_present = TRUE; + break; + + case CmResourceTypeInterrupt: + DEBUG_PRINT((11, "%s: interrupt vector 0x%x level 0x%x affinity 0x%x\n", + __FUNCTION__, + descriptor->u.Interrupt.Vector, + descriptor->u.Interrupt.Level, + (ULONG)descriptor->u.Interrupt.Affinity)); + + if (int_present) { + DEBUG_PRINT((0, "%s: interrupt is present\n", __FUNCTION__)); + status = STATUS_DEVICE_CONFIGURATION_ERROR; + goto error; + } + ASSERT(!(descriptor->Flags & CM_RESOURCE_INTERRUPT_LATCHED)); + + IoInitializeDpcRequest(ext->device, DcpForIsr); + status = IoConnectInterrupt(&ext->interrupt, + InterruptHandler, + ext, + NULL, + descriptor->u.Interrupt.Vector, + (UCHAR)descriptor->u.Interrupt.Level, + (UCHAR)descriptor->u.Interrupt.Level, + LevelSensitive, + TRUE, + descriptor->u.Interrupt.Affinity, + FALSE); + if (!NT_SUCCESS(status)) { + DEBUG_PRINT((0, "%s: connect interrupt failed\n", __FUNCTION__)); + goto error; + } + int_present = TRUE; + break; + + default: + DEBUG_PRINT((11, "%s: other type\n", __FUNCTION__)); + break; + } + } + + if (!int_present || !mem_present || !io_present) { + status = STATUS_DEVICE_CONFIGURATION_ERROR; + goto error; + } + + return STATUS_SUCCESS; + +error: + ReleaseHardwareRes(ext); + return STATUS_DEVICE_CONFIGURATION_ERROR; +} + +static VOID StartDevice(IN PDEVICE_OBJECT device, ScheduleContext *context) +{ + PIO_STACK_LOCATION stack; + NTSTATUS status; + VDIPortExt *ext; + PIRP irp; + + PAGED_CODE(); + + DEBUG_PRINT((6, "%s: start\n", __FUNCTION__)); + + ext = (VDIPortExt *) device->DeviceExtension; + irp = (PIRP)context->arg1; + stack = IoGetCurrentIrpStackLocation(irp); + + status = Prob(ext, irp); + if(NT_SUCCESS(status)) { + status = InitHwResources(ext, irp); + } + + if(NT_SUCCESS(status)) { + ext->pnp_state = STARTED; + IoSetDeviceInterfaceState(&ext->interface_name, TRUE); + } else { + ext->pnp_state = PASSIVE; + } + + irp->IoStatus.Status = status; + IoCompleteRequest(irp, IO_NO_INCREMENT); + + IoDecrement(ext); + + IoFreeWorkItem(context->item); + ExFreePool(context); + DEBUG_PRINT((7, "%s: done 0x%x\n", __FUNCTION__, status)); + return; +} + +NTSTATUS ScheduleWorkItem(PDEVICE_OBJECT device, PIO_WORKITEM_ROUTINE callback, + VOID *arg1, VOID *arg2) +{ + PIO_WORKITEM item = NULL; + ScheduleContext *context; + + context = ExAllocatePoolWithTag(NonPagedPool, sizeof(ScheduleContext), + VDIPORT_ALLOC_TAG); + + if (!context) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + item = IoAllocateWorkItem(device); + + if (!item) { + ExFreePoolWithTag(context, VDIPORT_ALLOC_TAG); + return STATUS_INSUFFICIENT_RESOURCES; + } + + context->item = item; + context->arg1 = arg1; + context->arg2 = arg2; + + IoQueueWorkItem(item, callback, DelayedWorkQueue, context); + + return STATUS_SUCCESS; +} + +static NTSTATUS StartCompletion(IN PDEVICE_OBJECT device, IN PIRP irp, IN PVOID context) +{ + NTSTATUS status; + + status = ScheduleWorkItem(device, StartDevice, irp, NULL); + if(!NT_SUCCESS(status)) { + irp->IoStatus.Status = status; + IoDecrement((VDIPortExt *)device->DeviceExtension); + return STATUS_CONTINUE_COMPLETION; + } + return STATUS_MORE_PROCESSING_REQUIRED; +} + + +static NTSTATUS CallNext(VDIPortExt *ext, IN PIRP irp) +{ + irp->IoStatus.Status = STATUS_SUCCESS; + IoSkipCurrentIrpStackLocation(irp); + IoDecrement(ext); + return IoCallDriver(ext->lower_dev, irp); +} + +NTSTATUS VDIPortDispatchPnP(IN PDEVICE_OBJECT device, IN PIRP irp) +{ + PIO_STACK_LOCATION stack; + VDIPortExt *ext; + NTSTATUS status; + + PAGED_CODE(); + + DEBUG_PRINT((3, "%s: start\n", __FUNCTION__)); + + stack = IoGetCurrentIrpStackLocation(irp); + ext = device->DeviceExtension; + status = IoIncrement(ext); + + if (!NT_SUCCESS(status)) { + DEBUG_PRINT((0, "%s: o increment failed\n", __FUNCTION__)); + irp->IoStatus.Status = status; + IoCompleteRequest(irp, IO_NO_INCREMENT); + IoDecrement(ext); + return status; + } + +#ifndef USE_REMOVE_LOCK + if (ext->pnp_state == REMOVED) { + DEBUG_PRINT((0, "%s: removed\n", __FUNCTION__)); + irp->IoStatus.Status = STATUS_NO_SUCH_DEVICE; + IoCompleteRequest(irp, IO_NO_INCREMENT); + IoDecrement(ext); + return STATUS_NO_SUCH_DEVICE; + } +#endif + + switch (stack->MinorFunction) { + case IRP_MN_START_DEVICE: + DEBUG_PRINT((5, "%s: start device\n", __FUNCTION__)); + ext->pnp_state = STARTING; + IoMarkIrpPending(irp); + IoCopyCurrentIrpStackLocationToNext(irp); + IoSetCompletionRoutine(irp, StartCompletion, ext, TRUE, TRUE, TRUE); + IoCallDriver(ext->lower_dev, irp); + return STATUS_PENDING; + case IRP_MN_QUERY_STOP_DEVICE: + DEBUG_PRINT((5, "%s: query stop\n", __FUNCTION__)); + ext->previos_pnp_state = ext->pnp_state; + ext->pnp_state = STOPPING; + return CallNext(ext, irp); + case IRP_MN_CANCEL_STOP_DEVICE: + DEBUG_PRINT((5, "%s: cancel stop\n", __FUNCTION__)); + ext->pnp_state = ext->previos_pnp_state; + return CallNext(ext, irp); + case IRP_MN_STOP_DEVICE: + DEBUG_PRINT((5, "%s: stop\n", __FUNCTION__)); + ext->pnp_state = STOPED; + return CallNext(ext, irp); + case IRP_MN_QUERY_REMOVE_DEVICE: + DEBUG_PRINT((5, "%s: query remove\n", __FUNCTION__)); + ext->previos_pnp_state = ext->pnp_state; + ext->pnp_state = REMOVING; + return CallNext(ext, irp); + case IRP_MN_CANCEL_REMOVE_DEVICE: + DEBUG_PRINT((5, "%s: cancel remove\n", __FUNCTION__)); + ext->pnp_state = ext->previos_pnp_state; + return CallNext(ext, irp); + case IRP_MN_SURPRISE_REMOVAL: + DEBUG_PRINT((5, "%s: surprise remove\n", __FUNCTION__)); + ext->pnp_state = SURPRISE; + ReleaseHardwareRes(ext); + return CallNext(ext, irp); + case IRP_MN_REMOVE_DEVICE: { + NTSTATUS status; + + DEBUG_PRINT((5, "%s: remove\n", __FUNCTION__)); +#ifdef USE_REMOVE_LOCK + IoReleaseRemoveLockAndWait(&ext->remove_lock, NULL); +#endif + ext->pnp_state = REMOVED; + ReleaseHardwareRes(ext); + KeFlushQueuedDpcs(); + IoSetDeviceInterfaceState(&ext->interface_name, FALSE); + irp->IoStatus.Status = STATUS_SUCCESS; + IoSkipCurrentIrpStackLocation (irp); + status = IoCallDriver(ext->lower_dev, irp); + IoDetachDevice(ext->lower_dev); + IoDeleteDevice(device); + return status; + } + default: + DEBUG_PRINT((5, "%s: other \n", __FUNCTION__, (UINT32)stack->MinorFunction)); + irp->IoStatus.Status = STATUS_SUCCESS; + IoSkipCurrentIrpStackLocation(irp); + IoDecrement(ext); + return IoCallDriver(ext->lower_dev, irp); + } +} + +static LONG ReadFromDev(VDIPortExt *ext, ULONG n, UINT8 *buf) +{ + VDIPortRing *ring = &ext->ram->output; + LONG actual_read = 0; + int do_notify = FALSE; + LONG error = 0; + + DEBUG_PRINT((6, "%s: start\n", __FUNCTION__)); + //todo: read all current ext->generation data before VDIPORT_ERR_RESET for reliable improvement + // also require change in generation handling in WriteToDev maybe separate generation + if (ext->ram->generation != ext->generation) { + DEBUG_PRINT((1, "%s: rest from %u to %u\n", __FUNCTION__, ext->generation, + ext->ram->generation)); + ext->generation = ext->ram->generation; + RingCleanup(ext); + return VDIPORT_ERR_RESET; + } + while (n) { + VDIPortPacket *packet; + int notify = FALSE; + UINT32 now; + int wait; + + RING_CONS_WAIT(ring, wait); + if (wait) { + break; + } + packet = RING_CONS_ITEM(ring); + if (packet->gen != ext->generation) { + if (!actual_read) { + if (ext->ram->generation == ext->generation) { + DEBUG_PRINT((0, "%s: bad packet packet %u ext %u dev %u\n", + __FUNCTION__, + packet->gen, + ext->generation, + ext->ram->generation)); + error = VDIPORT_ERR_IO_ERROR; + } else if (ext->ram->generation != ext->generation) { + DEBUG_PRINT((1, "%s: rest from %u to %u\n", __FUNCTION__, ext->generation, + ext->ram->generation)); + ext->generation = ext->ram->generation; + RingCleanup(ext); + error = VDIPORT_ERR_RESET; + } + } + break; + } + if (packet->size > sizeof(packet->data)) { + DEBUG_PRINT((0, "%s: bad packet size\n", __FUNCTION__, packet->size)); + return VDIPORT_ERR_IO_ERROR; + } + now = MIN(n, packet->size - ext->read_pos); + memcpy(buf, packet->data + ext->read_pos, now); + n -= now; + buf += now; + actual_read += now; + if ((ext->read_pos += now) == packet->size) { + ext->read_pos = 0; + RING_POP(ring, notify); + do_notify = do_notify || notify; + } + } + if (do_notify) { + WRITE_PORT_ULONG(ext->notify_port, 0); + } + DEBUG_PRINT((7, "%s: done\n", __FUNCTION__)); + return actual_read ? actual_read : error; +} + +static LONG WriteToDev(VDIPortExt *ext, ULONG n, UINT8 *buf) +{ + VDIPortRing *ring = &ext->ram->input; + LONG actual_write = 0; + int do_notify = FALSE; + + DEBUG_PRINT((6, "%s: start\n", __FUNCTION__)); + if (ext->ram->generation != ext->generation) { + DEBUG_PRINT((1, "%s: rest from %u to %u\n", __FUNCTION__, ext->generation, + ext->ram->generation)); + ext->generation = ext->ram->generation; + RingCleanup(ext); + return VDIPORT_ERR_RESET; + } + + while (n) { + VDIPortPacket *packet; + int notify = FALSE; + int wait; + + RING_PROD_WAIT(ring, wait); + if (wait) { + break; + } + + packet = RING_PROD_ITEM(ring); + packet->gen = ext->generation; + packet->size = MIN(n, sizeof(packet->data)); + memcpy(packet->data, buf, packet->size); + n -= packet->size; + buf += packet->size; + actual_write += packet->size; + RING_PUSH(ring, notify); + do_notify = do_notify || notify; + } + + if (do_notify) { + WRITE_PORT_ULONG(ext->notify_port, 0); + } + + DEBUG_PRINT((6, "%s: done\n", __FUNCTION__)); + return actual_write; +} + +NTSTATUS VDIPortRead(IN PDEVICE_OBJECT device, IN PIRP irp) +{ + PIO_STACK_LOCATION stack; + VDIPortExt *ext; + NTSTATUS status; + UINT8 *buf; + ULONG n; + LONG r; + + DEBUG_PRINT((3, "%s:\n", __FUNCTION__)); + ext = device->DeviceExtension; + status = IoIncrement(ext); + ASSERT(NT_SUCCESS(status)); + stack = IoGetCurrentIrpStackLocation(irp); + + if (ext->pnp_state != STARTED) { + DEBUG_PRINT((0, "%s: not active\n", __FUNCTION__)); + } + + n = stack->Parameters.Read.Length; + buf = irp->AssociatedIrp.SystemBuffer; + + if ((r = ReadFromDev(ext, n, buf)) < 0) { + switch (r) { + case VDIPORT_ERR_RESET: + DEBUG_PRINT((1, "%s: STATUS_CONNECTION_INVALID\n", __FUNCTION__)); + status = STATUS_CONNECTION_INVALID; + break; + case VDIPORT_ERR_IO_ERROR: + DEBUG_PRINT((0, "%s: STATUS_IO_DEVICE_ERROR\n", __FUNCTION__)); + status = STATUS_IO_DEVICE_ERROR; + break; + default: + DEBUG_PRINT((0, "%s: STATUS_UNSUCCESSFUL\n", __FUNCTION__)); + status = STATUS_UNSUCCESSFUL; + }; + irp->IoStatus.Information = 0; + } else { + irp->IoStatus.Information = r; + status = STATUS_SUCCESS; + } + + irp->IoStatus.Status = status; + IoCompleteRequest(irp, IO_NO_INCREMENT); + IoDecrement(ext); + + DEBUG_PRINT((4, "%s: done\n", __FUNCTION__)); + return status; +} + +NTSTATUS VDIPortWrite(IN PDEVICE_OBJECT device, IN PIRP irp) +{ + PIO_STACK_LOCATION stack; + VDIPortExt *ext; + NTSTATUS status; + UINT8 *buf; + ULONG n; + LONG r; + + DEBUG_PRINT((3, "%s:\n", __FUNCTION__)); + ext = device->DeviceExtension; + status = IoIncrement(ext); + ASSERT(NT_SUCCESS(status)); + stack = IoGetCurrentIrpStackLocation(irp); + + if (ext->pnp_state != STARTED) { + DEBUG_PRINT((0, "%s: not active\n", __FUNCTION__)); + } + + n = stack->Parameters.Write.Length; + buf = irp->AssociatedIrp.SystemBuffer; + if ((r = WriteToDev(ext, n, buf)) < 0) { + switch (r) { + case VDIPORT_ERR_RESET: + DEBUG_PRINT((1, "%s: STATUS_CONNECTION_INVALID\n", __FUNCTION__)); + status = STATUS_CONNECTION_INVALID; + break; + case VDIPORT_ERR_IO_ERROR: + DEBUG_PRINT((0, "%s: STATUS_IO_DEVICE_ERROR\n", __FUNCTION__)); + status = STATUS_IO_DEVICE_ERROR; + break; + default: + DEBUG_PRINT((0, "%s: STATUS_UNSUCCESSFUL\n", __FUNCTION__)); + status = STATUS_UNSUCCESSFUL; + }; + irp->IoStatus.Information = 0; + } else { + irp->IoStatus.Information = r; + status = STATUS_SUCCESS; + } + + irp->IoStatus.Status = status; + IoCompleteRequest(irp, IO_NO_INCREMENT); + IoDecrement(ext); + + DEBUG_PRINT((4, "%s: done\n", __FUNCTION__)); + return status; +} + +#define FIRST_AVAIL_IO_FUNC 0x800 +#define VDI_PORT_CTL_SET_EVENT_FUNC FIRST_AVAIL_IO_FUNC + +#define IOCTL_VDI_PORT_SET_EVENT \ + CTL_CODE(FILE_DEVICE_UNKNOWN, VDI_PORT_CTL_SET_EVENT_FUNC, METHOD_BUFFERED, FILE_ANY_ACCESS) + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (PAGE, VDIPortDeviceControl) +#endif +NTSTATUS VDIPortDeviceControl(IN PDEVICE_OBJECT device, IN PIRP irp) +{ + PIO_STACK_LOCATION stack; + VDIPortExt *ext; + NTSTATUS status; + ULONG input_len; + PCHAR input_buf; + + PAGED_CODE(); + + DEBUG_PRINT((3, "%s: start\n", __FUNCTION__)); + + if (KeGetCurrentIrql() != PASSIVE_LEVEL) { + DEBUG_PRINT((0, "%s: not passive\n", __FUNCTION__)); + } + + ext = device->DeviceExtension; + status = IoIncrement(ext); + ASSERT(NT_SUCCESS(status)); + stack = IoGetCurrentIrpStackLocation(irp); + input_len = stack->Parameters.DeviceIoControl.InputBufferLength; + input_buf = irp->AssociatedIrp.SystemBuffer; + + if (ext->pnp_state != STARTED) { + DEBUG_PRINT((0, "%s: not active\n", __FUNCTION__)); + } + + switch (stack->Parameters.DeviceIoControl.IoControlCode) { + case IOCTL_VDI_PORT_SET_EVENT: { + HANDLE user_event; + PKEVENT k_event; + if (input_len != sizeof(HANDLE)) { + status = STATUS_INVALID_PARAMETER; + goto error; + } + + DEBUG_PRINT((5, "%s: 0x%x\n", __FUNCTION__, stack->FileObject)); + + user_event = *(HANDLE*)input_buf; + if (user_event) { + status = ObReferenceObjectByHandle(user_event, GENERIC_ALL, *ExEventObjectType, + KernelMode, &k_event, NULL); + if (!NT_SUCCESS(status)) { + goto error; + } + } + SetUserEvent(ext, k_event); + break; + } + default: + DEBUG_PRINT((5, "%s: other\n", __FUNCTION__)); + IoDecrement(ext); + return IoCallDriver(ext->lower_dev, irp); + } + +error: + irp->IoStatus.Information = 0; + irp->IoStatus.Status = status; + IoCompleteRequest(irp, IO_NO_INCREMENT); + IoDecrement(ext); + return status; +} + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (PAGE, VDIPortPower) +#endif +NTSTATUS VDIPortPower(IN PDEVICE_OBJECT device, IN PIRP irp) +{ + PIO_STACK_LOCATION stack; + VDIPortExt *ext; + NTSTATUS status; + + PAGED_CODE(); + + DEBUG_PRINT((3, "%s: start\n", __FUNCTION__)); + + stack = IoGetCurrentIrpStackLocation(irp); + ext = device->DeviceExtension; + + status = IoIncrement(ext); + if (!NT_SUCCESS(status)) { + DEBUG_PRINT((0, "%s: io increment failed\n", __FUNCTION__)); + goto error_1; + } + +#ifndef USE_REMOVE_LOCK + if (ext->pnp_state == REMOVED) { + status = STATUS_NO_SUCH_DEVICE ; + DEBUG_PRINT((0, "%s: REMOVED\n", __FUNCTION__)); + goto error_2; + } +#endif + + switch (stack->MinorFunction) { + case IRP_MN_SET_POWER: { + POWER_STATE_TYPE type; + POWER_STATE state; + + type = stack->Parameters.Power.Type; + state = stack->Parameters.Power.State; + DEBUG_PRINT((1, "%s: set type %d state %d\n", + __FUNCTION__, + type, + state)); + break; + } + case IRP_MN_QUERY_POWER: + break; + }; + + PoStartNextPowerIrp(irp); + IoSkipCurrentIrpStackLocation(irp); + status = PoCallDriver(ext->lower_dev, irp); + DEBUG_PRINT((4, "%s: done\n", __FUNCTION__)); + IoDecrement(ext); + return status; + +#ifndef USE_REMOVE_LOCK +error_2: + IoDecrement(ext); +#endif + +error_1: + PoStartNextPowerIrp(irp); + irp->IoStatus.Status = status; + IoCompleteRequest (irp, IO_NO_INCREMENT); + return status; +} + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (PAGE, VDIPortSystemControl) +#endif +NTSTATUS VDIPortSystemControl(IN PDEVICE_OBJECT device, IN PIRP irp) +{ + PIO_STACK_LOCATION stack; + VDIPortExt *ext; + NTSTATUS status; + + PAGED_CODE(); + + DEBUG_PRINT((3, "%s: start\n", __FUNCTION__)); + + stack = IoGetCurrentIrpStackLocation(irp); + ext = device->DeviceExtension; + + status = IoIncrement(ext); + if (!NT_SUCCESS(status)) { + DEBUG_PRINT((0, "%s: io increment failed\n", __FUNCTION__)); + goto error_1; + } + +#ifndef USE_REMOVE_LOCK + if (ext->pnp_state == REMOVED) { + status = STATUS_NO_SUCH_DEVICE ; + DEBUG_PRINT((0, "%s: REMOVED\n", __FUNCTION__)); + goto error_2; + } +#endif + IoSkipCurrentIrpStackLocation(irp); + status = IoCallDriver(ext->lower_dev, irp); + DEBUG_PRINT((4, "%s: done\n", __FUNCTION__)); + IoDecrement(ext); + return status; + +#ifndef USE_REMOVE_LOCK +error_2: + IoDecrement(ext); +#endif + +error_1: + irp->IoStatus.Status = status; + IoCompleteRequest(irp, IO_NO_INCREMENT); + return status; +} + diff --git a/win/vdi_port.rc b/win/vdi_port.rc new file mode 100644 index 0000000..dcdb079 --- /dev/null +++ b/win/vdi_port.rc @@ -0,0 +1,29 @@ +#include <windows.h>
+
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_DRV
+#define VER_FILESUBTYPE VFT2_DRV_SYSTEM
+
+#undef VER_COMPANYNAME_STR
+#undef VER_FILEVERSION_STR
+#undef VER_LEGALCOPYRIGHT_STR
+#undef VER_LEGALCOPYRIGHT_YEARS
+#undef VER_PRODUCTNAME_STR
+#undef VER_PRODUCTVERSION_STR
+
+
+#define VER_FILEDESCRIPTION_STR "Red Hat VDI Port Driver"
+#define VER_INTERNALNAME_STR "vdi_port.sys"
+#define VER_ORIGINALFILENAME_STR VER_INTERNALNAME_STR
+#define VER_FILEVERSION_STR "1.1.0.0"
+#define VER_PRODUCTNAME_STR "Spice"
+#define VER_PRODUCTVERSION_STR VER_FILEVERSION_STR
+
+#undef VER_PRODUCTVERSION
+#define VER_PRODUCTVERSION 1,1,0,0
+
+#define VER_COMPANYNAME_STR "Red Hat Inc."
+#define VER_LEGALCOPYRIGHT_STR "© Red Hat Inc. All rights reserved."
+
+#include "common.ver"
diff --git a/win/vdiport.inf b/win/vdiport.inf new file mode 100644 index 0000000..56dd910 --- /dev/null +++ b/win/vdiport.inf @@ -0,0 +1,53 @@ +[Version]
+Signature = "$WINDOWS NT$"
+Class = System
+ClassGuid = {4d36e97d-e325-11ce-bfc1-08002be10318}
+Provider = %RHAT%
+DriverVer = 10/19/2009,1.1.0.0
+
+[DestinationDirs]
+DefaultDestDir = 12
+
+[ControlFlags]
+ExcludeFromSelect=*
+
+[Manufacturer]
+%RHAT% = redhat.Mfg
+
+[redhat.Mfg]
+%RHAT% %PORT% = VDIPort, PCI\VEN_1b36&DEV_0105
+
+[VDIPort]
+CopyFiles = VDIPort.CopyFiles
+
+[VDIPort.HW]
+AddReg = VDIPort.AddReg
+
+[VDIPort.AddReg]
+HKR,,Security,,"D:P(A;;GA;;;SY)(A;;GA;;;BA)"
+
+[VDIPort.CopyFiles]
+vdiport.sys
+
+[SourceDisksNames]
+1 = %DISK_NAME%
+
+[SourceDisksFiles]
+vdiport.sys = 1
+
+[VDIPort.Services]
+AddService = VDIPort, 0x00000002, VDIPort_Service
+
+[VDIPort_Service]
+DisplayName = %VDIPort.SVCDESC%
+ServiceType = 1 ; SERVICE_KERNEL_DRIVER
+StartType = 3 ; SERVICE_DEMAND_START
+ErrorControl = 1 ; SERVICE_ERROR_NORMAL
+ServiceBinary = %12%\vdiport.sys
+
+[Strings]
+RHAT = "Red Hat"
+PORT = "Virtual Desktop Interface Port"
+VDIPort.SVCDESC = "Virtual Desktop Interface Port Driver"
+VDIPort.DRVDESC = "Virtual Desktop Interface Port Device"
+DISK_NAME = "Virtual Desktop Interface Port Install Disk"
|