Don't call IoRaiseHardError at DISPATCH_LEVEL
[reactos.git] / reactos / drivers / input / mouclass / mouclass.c
index bbeaecd..d566f43 100644 (file)
 #define INITGUID
 #include "mouclass.h"
 
+static NTSTATUS
+SearchForLegacyDrivers(
+       IN PDRIVER_OBJECT DriverObject,
+       IN PCLASS_DRIVER_EXTENSION DriverExtension);
+
 static VOID NTAPI
 DriverUnload(IN PDRIVER_OBJECT DriverObject)
 {
@@ -20,7 +25,7 @@ DriverUnload(IN PDRIVER_OBJECT DriverObject)
 }
 
 static NTSTATUS NTAPI
-MouclassCreate(
+ClassCreate(
        IN PDEVICE_OBJECT DeviceObject,
        IN PIRP Irp)
 {
@@ -30,11 +35,14 @@ MouclassCreate(
                return ForwardIrpAndForget(DeviceObject, Irp);
 
        /* FIXME: open all associated Port devices */
+       Irp->IoStatus.Status = STATUS_SUCCESS;
+       Irp->IoStatus.Information = 0;
+       IoCompleteRequest(Irp, IO_NO_INCREMENT);
        return STATUS_SUCCESS;
 }
 
 static NTSTATUS NTAPI
-MouclassClose(
+ClassClose(
        IN PDEVICE_OBJECT DeviceObject,
        IN PIRP Irp)
 {
@@ -44,11 +52,31 @@ MouclassClose(
                return ForwardIrpAndForget(DeviceObject, Irp);
 
        /* FIXME: close all associated Port devices */
+       Irp->IoStatus.Status = STATUS_SUCCESS;
+       Irp->IoStatus.Information = 0;
+       IoCompleteRequest(Irp, IO_NO_INCREMENT);
+       return STATUS_SUCCESS;
+}
+
+static NTSTATUS NTAPI
+ClassCleanup(
+       IN PDEVICE_OBJECT DeviceObject,
+       IN PIRP Irp)
+{
+       DPRINT("IRP_MJ_CLEANUP\n");
+
+       if (!((PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->IsClassDO)
+               return ForwardIrpAndForget(DeviceObject, Irp);
+
+       /* FIXME: cleanup all associated Port devices */
+       Irp->IoStatus.Status = STATUS_SUCCESS;
+       Irp->IoStatus.Information = 0;
+       IoCompleteRequest(Irp, IO_NO_INCREMENT);
        return STATUS_SUCCESS;
 }
 
 static NTSTATUS NTAPI
-MouclassRead(
+ClassRead(
        IN PDEVICE_OBJECT DeviceObject,
        IN PIRP Irp)
 {
@@ -71,6 +99,52 @@ MouclassRead(
        return STATUS_PENDING;
 }
 
+static NTSTATUS NTAPI
+ClassDeviceControl(
+       IN PDEVICE_OBJECT DeviceObject,
+       IN PIRP Irp)
+{
+       PCLASS_DEVICE_EXTENSION DeviceExtension;
+       NTSTATUS Status = Irp->IoStatus.Status;
+
+       DPRINT("IRP_MJ_DEVICE_CONTROL\n");
+
+       if (!((PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->IsClassDO)
+               return ForwardIrpAndForget(DeviceObject, Irp);
+
+       DeviceExtension = (PCLASS_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+
+       switch (IoGetCurrentIrpStackLocation(Irp)->Parameters.DeviceIoControl.IoControlCode)
+       {
+               case IOCTL_MOUSE_QUERY_ATTRIBUTES:
+               {
+                       /* FIXME: We hope that all devices will return the same result.
+                        * Ask only the first one */
+                       PLIST_ENTRY Head = &((PCLASS_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->ListHead;
+                       if (Head->Flink != Head)
+                       {
+                               /* We have at least one mouse */
+                               PPORT_DEVICE_EXTENSION DevExt = CONTAINING_RECORD(Head->Flink, PORT_DEVICE_EXTENSION, ListEntry);
+                               IoGetCurrentIrpStackLocation(Irp)->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
+                               IoSkipCurrentIrpStackLocation(Irp);
+                               return IoCallDriver(DevExt->DeviceObject, Irp);
+                       }
+                       break;
+               }
+               default:
+                       DPRINT1("IRP_MJ_DEVICE_CONTROL / unknown I/O control code 0x%lx\n",
+                               IoGetCurrentIrpStackLocation(Irp)->Parameters.DeviceIoControl.IoControlCode);
+                       ASSERT(FALSE);
+                       break;
+       }
+
+       Irp->IoStatus.Status = Status;
+       Irp->IoStatus.Information = 0;
+       IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+       return Status;
+}
+
 static NTSTATUS NTAPI
 IrpStub(
        IN PDEVICE_OBJECT DeviceObject,
@@ -83,6 +157,7 @@ IrpStub(
                /* Forward some IRPs to lower device */
                switch (IoGetCurrentIrpStackLocation(Irp)->MajorFunction)
                {
+                       case IRP_MJ_PNP:
                        case IRP_MJ_INTERNAL_DEVICE_CONTROL:
                                return ForwardIrpAndForget(DeviceObject, Irp);
                        default:
@@ -110,14 +185,27 @@ IrpStub(
 static NTSTATUS
 ReadRegistryEntries(
        IN PUNICODE_STRING RegistryPath,
-       IN PMOUCLASS_DRIVER_EXTENSION DriverExtension)
+       IN PCLASS_DRIVER_EXTENSION DriverExtension)
 {
+       UNICODE_STRING ParametersRegistryKey;
        RTL_QUERY_REGISTRY_TABLE Parameters[4];
        NTSTATUS Status;
 
        ULONG DefaultConnectMultiplePorts = 1;
-       ULONG DefaultMouseDataQueueSize = 0x64;
-       UNICODE_STRING DefaultPointerDeviceBaseName = RTL_CONSTANT_STRING(L"PointerClass");
+       ULONG DefaultDataQueueSize = 0x64;
+       UNICODE_STRING DefaultDeviceBaseName = RTL_CONSTANT_STRING(L"PointerClass");
+
+       ParametersRegistryKey.Length = 0;
+       ParametersRegistryKey.MaximumLength = RegistryPath->Length + sizeof(L"\\Parameters") + sizeof(UNICODE_NULL);
+       ParametersRegistryKey.Buffer = ExAllocatePool(PagedPool, ParametersRegistryKey.MaximumLength);
+       if (!ParametersRegistryKey.Buffer)
+       {
+               DPRINT("ExAllocatePool() failed\n");
+               return STATUS_INSUFFICIENT_RESOURCES;
+       }
+       RtlCopyUnicodeString(&ParametersRegistryKey, RegistryPath);
+       RtlAppendUnicodeToString(&ParametersRegistryKey, L"\\Parameters");
+       ParametersRegistryKey.Buffer[ParametersRegistryKey.Length / sizeof(WCHAR)] = UNICODE_NULL;
 
        RtlZeroMemory(Parameters, sizeof(Parameters));
 
@@ -127,24 +215,24 @@ ReadRegistryEntries(
        Parameters[0].DefaultType = REG_DWORD;
        Parameters[0].DefaultData = &DefaultConnectMultiplePorts;
        Parameters[0].DefaultLength = sizeof(ULONG);
-       
+
        Parameters[1].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_REGISTRY_OPTIONAL;
        Parameters[1].Name = L"MouseDataQueueSize";
-       Parameters[1].EntryContext = &DriverExtension->MouseDataQueueSize;
+       Parameters[1].EntryContext = &DriverExtension->DataQueueSize;
        Parameters[1].DefaultType = REG_DWORD;
-       Parameters[1].DefaultData = &DefaultMouseDataQueueSize;
+       Parameters[1].DefaultData = &DefaultDataQueueSize;
        Parameters[1].DefaultLength = sizeof(ULONG);
-       
+
        Parameters[2].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_REGISTRY_OPTIONAL;
        Parameters[2].Name = L"PointerDeviceBaseName";
-       Parameters[2].EntryContext = &DriverExtension->PointerDeviceBaseName;
+       Parameters[2].EntryContext = &DriverExtension->DeviceBaseName;
        Parameters[2].DefaultType = REG_SZ;
-       Parameters[2].DefaultData = &DefaultPointerDeviceBaseName;
-       Parameters[2].DefaultLength = sizeof(ULONG);
+       Parameters[2].DefaultData = &DefaultDeviceBaseName;
+       Parameters[2].DefaultLength = 0;
 
        Status = RtlQueryRegistryValues(
                RTL_REGISTRY_ABSOLUTE,
-               RegistryPath->Buffer,
+               ParametersRegistryKey.Buffer,
                Parameters,
                NULL,
                NULL);
@@ -157,39 +245,49 @@ ReadRegistryEntries(
                {
                        DriverExtension->ConnectMultiplePorts = DefaultConnectMultiplePorts;
                }
-               if (DriverExtension->MouseDataQueueSize == 0)
+               if (DriverExtension->DataQueueSize == 0)
                {
-                       DriverExtension->MouseDataQueueSize = DefaultMouseDataQueueSize;
+                       DriverExtension->DataQueueSize = DefaultDataQueueSize;
                }
        }
+       else if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
+       {
+               /* Registry path doesn't exist. Set defaults */
+               DriverExtension->ConnectMultiplePorts = DefaultConnectMultiplePorts;
+               DriverExtension->DataQueueSize = DefaultDataQueueSize;
+               Status = RtlDuplicateUnicodeString(
+                       RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE,
+                       &DefaultDeviceBaseName,
+                       &DriverExtension->DeviceBaseName);
+       }
 
        return Status;
 }
 
 static NTSTATUS
-CreatePointerClassDeviceObject(
+CreateClassDeviceObject(
        IN PDRIVER_OBJECT DriverObject,
        OUT PDEVICE_OBJECT *ClassDO OPTIONAL)
 {
-       PMOUCLASS_DRIVER_EXTENSION DriverExtension;
+       PCLASS_DRIVER_EXTENSION DriverExtension;
        ULONG DeviceId = 0;
        ULONG PrefixLength;
        UNICODE_STRING DeviceNameU;
        PWSTR DeviceIdW = NULL; /* Pointer into DeviceNameU.Buffer */
        PDEVICE_OBJECT Fdo;
-       PMOUCLASS_DEVICE_EXTENSION DeviceExtension;
+       PCLASS_DEVICE_EXTENSION DeviceExtension;
        NTSTATUS Status;
 
-       DPRINT("CreatePointerClassDeviceObject(0x%p)\n", DriverObject);
+       DPRINT("CreateClassDeviceObject(0x%p)\n", DriverObject);
 
        /* Create new device object */
        DriverExtension = IoGetDriverObjectExtension(DriverObject, DriverObject);
        DeviceNameU.Length = 0;
        DeviceNameU.MaximumLength =
-               wcslen(L"\\Device\\") * sizeof(WCHAR)           /* "\Device\" */
-               + DriverExtension->PointerDeviceBaseName.Length /* "PointerClass" */
-               + 4 * sizeof(WCHAR)                             /* Id between 0 and 9999 */
-               + sizeof(UNICODE_NULL);                         /* Final NULL char */
+               wcslen(L"\\Device\\") * sizeof(WCHAR)    /* "\Device\" */
+               + DriverExtension->DeviceBaseName.Length /* "PointerClass" */
+               + 4 * sizeof(WCHAR)                      /* Id between 0 and 9999 */
+               + sizeof(UNICODE_NULL);                  /* Final NULL char */
        DeviceNameU.Buffer = ExAllocatePool(PagedPool, DeviceNameU.MaximumLength);
        if (!DeviceNameU.Buffer)
        {
@@ -202,7 +300,7 @@ CreatePointerClassDeviceObject(
                DPRINT("RtlAppendUnicodeToString() failed with status 0x%08lx\n", Status);
                goto cleanup;
        }
-       Status = RtlAppendUnicodeStringToString(&DeviceNameU, &DriverExtension->PointerDeviceBaseName);
+       Status = RtlAppendUnicodeStringToString(&DeviceNameU, &DriverExtension->DeviceBaseName);
        if (!NT_SUCCESS(Status))
        {
                DPRINT("RtlAppendUnicodeStringToString() failed with status 0x%08lx\n", Status);
@@ -215,11 +313,11 @@ CreatePointerClassDeviceObject(
                DeviceNameU.Length = PrefixLength + swprintf(DeviceIdW, L"%lu", DeviceId) * sizeof(WCHAR);
                Status = IoCreateDevice(
                        DriverObject,
-                       sizeof(MOUCLASS_DEVICE_EXTENSION),
+                       sizeof(CLASS_DEVICE_EXTENSION),
                        &DeviceNameU,
                        FILE_DEVICE_MOUSE,
                        FILE_DEVICE_SECURE_OPEN,
-                       FALSE,
+                       TRUE,
                        &Fdo);
                if (NT_SUCCESS(Status))
                        goto cleanup;
@@ -230,26 +328,38 @@ CreatePointerClassDeviceObject(
                }
                DeviceId++;
        }
-       DPRINT("Too much devices starting with '\\Device\\%wZ'\n", &DriverExtension->PointerDeviceBaseName);
-       Status = STATUS_UNSUCCESSFUL;
+       DPRINT("Too many devices starting with '\\Device\\%wZ'\n", &DriverExtension->DeviceBaseName);
+       Status = STATUS_TOO_MANY_NAMES;
 cleanup:
-       ExFreePool(DeviceNameU.Buffer);
        if (!NT_SUCCESS(Status))
+       {
+               ExFreePool(DeviceNameU.Buffer);
                return Status;
+       }
 
-       DeviceExtension = (PMOUCLASS_DEVICE_EXTENSION)Fdo->DeviceExtension;
-       RtlZeroMemory(DeviceExtension, sizeof(MOUCLASS_DEVICE_EXTENSION));
+       DeviceExtension = (PCLASS_DEVICE_EXTENSION)Fdo->DeviceExtension;
+       RtlZeroMemory(DeviceExtension, sizeof(CLASS_DEVICE_EXTENSION));
        DeviceExtension->Common.IsClassDO = TRUE;
        DeviceExtension->DriverExtension = DriverExtension;
-       DeviceExtension->PnpState = dsStopped;
-       KeInitializeSpinLock(&(DeviceExtension->SpinLock));
+       InitializeListHead(&DeviceExtension->ListHead);
+       KeInitializeSpinLock(&DeviceExtension->ListSpinLock);
+       KeInitializeSpinLock(&DeviceExtension->SpinLock);
        DeviceExtension->ReadIsPending = FALSE;
        DeviceExtension->InputCount = 0;
-       DeviceExtension->PortData = ExAllocatePool(NonPagedPool, DeviceExtension->DriverExtension->MouseDataQueueSize * sizeof(MOUSE_INPUT_DATA));
+       DeviceExtension->PortData = ExAllocatePool(NonPagedPool, DeviceExtension->DriverExtension->DataQueueSize * sizeof(MOUSE_INPUT_DATA));
        Fdo->Flags |= DO_POWER_PAGABLE;
        Fdo->Flags &= ~DO_DEVICE_INITIALIZING;
 
-       /* FIXME: create registry entry in HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP */
+       /* Add entry entry to HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\[DeviceBaseName] */
+       RtlWriteRegistryValue(
+               RTL_REGISTRY_DEVICEMAP,
+               DriverExtension->DeviceBaseName.Buffer,
+               DeviceNameU.Buffer,
+               REG_SZ,
+               DriverExtension->RegistryPath.Buffer,
+               DriverExtension->RegistryPath.MaximumLength);
+
+       ExFreePool(DeviceNameU.Buffer);
 
        if (ClassDO)
                *ClassDO = Fdo;
@@ -257,104 +367,157 @@ cleanup:
        return STATUS_SUCCESS;
 }
 
+static NTSTATUS
+FillOneEntry(
+       IN PDEVICE_OBJECT ClassDeviceObject,
+       IN PIRP Irp,
+       IN PMOUSE_INPUT_DATA DataStart)
+{
+       NTSTATUS Status = STATUS_SUCCESS;
+
+       if (ClassDeviceObject->Flags & DO_BUFFERED_IO)
+       {
+               RtlCopyMemory(
+                       Irp->AssociatedIrp.SystemBuffer,
+                       DataStart,
+                       sizeof(MOUSE_INPUT_DATA));
+       }
+       else if (ClassDeviceObject->Flags & DO_DIRECT_IO)
+       {
+               PVOID DestAddress = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
+               if (DestAddress)
+               {
+                       RtlCopyMemory(
+                               DestAddress,
+                               DataStart,
+                               sizeof(MOUSE_INPUT_DATA));
+               }
+               else
+                       Status = STATUS_UNSUCCESSFUL;
+       }
+       else
+       {
+               _SEH_TRY
+               {
+                       RtlCopyMemory(
+                               Irp->UserBuffer,
+                               DataStart,
+                               sizeof(MOUSE_INPUT_DATA));
+               }
+               _SEH_HANDLE
+               {
+                       Status = _SEH_GetExceptionCode();
+               }
+               _SEH_END;
+       }
+
+       return Status;
+}
+
 static BOOLEAN
-MouclassCallback(
+ClassCallback(
        IN PDEVICE_OBJECT ClassDeviceObject,
-       IN OUT PMOUSE_INPUT_DATA MouseDataStart,
-       IN PMOUSE_INPUT_DATA MouseDataEnd,
+       IN OUT PMOUSE_INPUT_DATA DataStart,
+       IN PMOUSE_INPUT_DATA DataEnd,
        IN OUT PULONG ConsumedCount)
 {
-       PMOUCLASS_DEVICE_EXTENSION ClassDeviceExtension = ClassDeviceObject->DeviceExtension;
+       PCLASS_DEVICE_EXTENSION ClassDeviceExtension = ClassDeviceObject->DeviceExtension;
        PIRP Irp = NULL;
        KIRQL OldIrql;
        PIO_STACK_LOCATION Stack;
-       ULONG InputCount = MouseDataEnd - MouseDataStart;
+       ULONG InputCount = DataEnd - DataStart;
        ULONG ReadSize;
 
        ASSERT(ClassDeviceExtension->Common.IsClassDO);
 
-       DPRINT("MouclassCallback()\n");
+       KeAcquireSpinLock(&ClassDeviceExtension->SpinLock, &OldIrql);
+
+       DPRINT("ClassCallback()\n");
        /* A filter driver might have consumed all the data already; I'm
         * not sure if they are supposed to move the packets when they
         * consume them though.
         */
        if (ClassDeviceExtension->ReadIsPending == TRUE && InputCount)
        {
+               NTSTATUS Status;
+
                Irp = ClassDeviceObject->CurrentIrp;
                ClassDeviceObject->CurrentIrp = NULL;
                Stack = IoGetCurrentIrpStackLocation(Irp);
 
                /* A read request is waiting for input, so go straight to it */
-               /* FIXME: use SEH */
-               RtlCopyMemory(
-                       Irp->MdlAddress ? MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority) : Irp->UserBuffer,
-                       MouseDataStart,
-                       sizeof(MOUSE_INPUT_DATA));
+               Status = FillOneEntry(
+                       ClassDeviceObject,
+                       Irp,
+                       DataStart);
 
-               /* Go to next packet and complete this request with STATUS_SUCCESS */
-               Irp->IoStatus.Status = STATUS_SUCCESS;
-               Irp->IoStatus.Information = sizeof(MOUSE_INPUT_DATA);
-               Stack->Parameters.Read.Length = sizeof(MOUSE_INPUT_DATA);
+               if (NT_SUCCESS(Status))
+               {
+                       /* Go to next packet and complete this request with STATUS_SUCCESS */
+                       Irp->IoStatus.Status = STATUS_SUCCESS;
+                       Irp->IoStatus.Information = sizeof(MOUSE_INPUT_DATA);
+                       Stack->Parameters.Read.Length = sizeof(MOUSE_INPUT_DATA);
 
-               ClassDeviceExtension->ReadIsPending = FALSE;
+                       ClassDeviceExtension->ReadIsPending = FALSE;
 
-               /* Skip the packet we just sent away */
-               MouseDataStart++;
-               (*ConsumedCount)++;
-               InputCount--;
+                       /* Skip the packet we just sent away */
+                       DataStart++;
+                       (*ConsumedCount)++;
+                       InputCount--;
+               }
        }
 
        /* If we have data from the port driver and a higher service to send the data to */
        if (InputCount != 0)
        {
-               KeAcquireSpinLock(&ClassDeviceExtension->SpinLock, &OldIrql);
-
-               if (ClassDeviceExtension->InputCount + InputCount > ClassDeviceExtension->DriverExtension->MouseDataQueueSize)
-                       ReadSize = ClassDeviceExtension->DriverExtension->MouseDataQueueSize - ClassDeviceExtension->InputCount;
+               if (ClassDeviceExtension->InputCount + InputCount > ClassDeviceExtension->DriverExtension->DataQueueSize)
+               {
+                       /*
+                        * We're exceeding the buffer, and data will be thrown away...
+                        * FIXME: What could we do, as we are at DISPATCH_LEVEL?
+                        */
+                       ReadSize = ClassDeviceExtension->DriverExtension->DataQueueSize - ClassDeviceExtension->InputCount;
+               }
                else
                        ReadSize = InputCount;
 
                /*
-                * FIXME: If we exceed the buffer, mouse data gets thrown away.. better
-                * solution?
-               */
-
-               /*
-                * Move the mouse input data from the port data queue to our class data
+                * Move the input data from the port data queue to our class data
                 * queue.
                 */
                RtlMoveMemory(
                        ClassDeviceExtension->PortData,
-                       (PCHAR)MouseDataStart,
+                       (PCHAR)DataStart,
                        sizeof(MOUSE_INPUT_DATA) * ReadSize);
 
                /* Move the pointer and counter up */
                ClassDeviceExtension->PortData += ReadSize;
                ClassDeviceExtension->InputCount += ReadSize;
 
-               KeReleaseSpinLock(&ClassDeviceExtension->SpinLock, OldIrql);
                (*ConsumedCount) += ReadSize;
        }
        else
        {
-               DPRINT("MouclassCallBack() entered, InputCount = %lu - DOING NOTHING\n", InputCount);
+               DPRINT("ClassCallBack() entered, InputCount = %lu - DOING NOTHING\n", InputCount);
        }
 
+       KeReleaseSpinLock(&ClassDeviceExtension->SpinLock, OldIrql);
+
        if (Irp != NULL)
        {
                IoStartNextPacket(ClassDeviceObject, FALSE);
                IoCompleteRequest(Irp, IO_MOUSE_INCREMENT);
        }
 
-       DPRINT("Leaving MouclassCallback()\n");
+       DPRINT("Leaving ClassCallback()\n");
        return TRUE;
 }
 
-/* Send IOCTL_INTERNAL_MOUSE_CONNECT to pointer port */
+/* Send IOCTL_INTERNAL_*_CONNECT to port */
 static NTSTATUS
-ConnectMousePortDriver(
-       IN PDEVICE_OBJECT PointerPortDO,
-       IN PDEVICE_OBJECT PointerClassDO)
+ConnectPortDriver(
+       IN PDEVICE_OBJECT PortDO,
+       IN PDEVICE_OBJECT ClassDO)
 {
        KEVENT Event;
        PIRP Irp;
@@ -364,103 +527,158 @@ ConnectMousePortDriver(
 
        KeInitializeEvent(&Event, NotificationEvent, FALSE);
 
-       ConnectData.ClassDeviceObject = PointerClassDO;
-       ConnectData.ClassService      = MouclassCallback;
+       ConnectData.ClassDeviceObject = ClassDO;
+       ConnectData.ClassService      = ClassCallback;
 
        Irp = IoBuildDeviceIoControlRequest(IOCTL_INTERNAL_MOUSE_CONNECT,
-               PointerPortDO,
+               PortDO,
                &ConnectData, sizeof(CONNECT_DATA),
                NULL, 0,
                TRUE, &Event, &IoStatus);
 
-       Status = IoCallDriver(PointerPortDO, Irp);
+       Status = IoCallDriver(PortDO, Irp);
 
        if (Status == STATUS_PENDING)
                KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, NULL);
        else
                IoStatus.Status = Status;
 
+       if (NT_SUCCESS(IoStatus.Status))
+       {
+               ObReferenceObject(PortDO);
+               ExInterlockedInsertTailList(
+                       &((PCLASS_DEVICE_EXTENSION)ClassDO->DeviceExtension)->ListHead,
+                       &((PPORT_DEVICE_EXTENSION)PortDO->DeviceExtension)->ListEntry,
+                       &((PCLASS_DEVICE_EXTENSION)ClassDO->DeviceExtension)->ListSpinLock);
+               if (ClassDO->StackSize <= PortDO->StackSize)
+               {
+                       /* Increase the stack size, in case we have to
+                        * forward some IRPs to the port device object
+                        */
+                       ClassDO->StackSize = PortDO->StackSize + 1;
+               }
+       }
+
        return IoStatus.Status;
 }
 
 static NTSTATUS NTAPI
-MouclassAddDevice(
+ClassAddDevice(
        IN PDRIVER_OBJECT DriverObject,
        IN PDEVICE_OBJECT Pdo)
 {
-       PMOUCLASS_DRIVER_EXTENSION DriverExtension;
-       PDEVICE_OBJECT Fdo;
-       PMOUCLASS_DEVICE_EXTENSION DeviceExtension;
+       PCLASS_DRIVER_EXTENSION DriverExtension;
+       PDEVICE_OBJECT Fdo = NULL;
+       PPORT_DEVICE_EXTENSION DeviceExtension = NULL;
        NTSTATUS Status;
 
-       DPRINT("MouclassAddDevice called. Pdo = 0x%p\n", Pdo);
+       DPRINT("ClassAddDevice called. Pdo = 0x%p\n", Pdo);
 
        DriverExtension = IoGetDriverObjectExtension(DriverObject, DriverObject);
 
+       if (Pdo == NULL)
+               /* We're getting a NULL Pdo at the first call as we're a legacy driver.
+                * Use it to search for legacy port drivers. */
+               return SearchForLegacyDrivers(DriverObject, DriverExtension);
+
        /* Create new device object */
        Status = IoCreateDevice(
                DriverObject,
-               sizeof(MOUCLASS_DEVICE_EXTENSION),
+               sizeof(PORT_DEVICE_EXTENSION),
                NULL,
                Pdo->DeviceType,
-               FILE_DEVICE_SECURE_OPEN,
-               FALSE,
+               Pdo->Characteristics & FILE_DEVICE_SECURE_OPEN ? FILE_DEVICE_SECURE_OPEN : 0,
+               TRUE,
                &Fdo);
        if (!NT_SUCCESS(Status))
        {
                DPRINT("IoCreateDevice() failed with status 0x%08lx\n", Status);
-               return Status;
+               goto cleanup;
        }
 
-       DeviceExtension = (PMOUCLASS_DEVICE_EXTENSION)Fdo->DeviceExtension;
-       RtlZeroMemory(DeviceExtension, sizeof(MOUCLASS_DEVICE_EXTENSION));
+       DeviceExtension = (PPORT_DEVICE_EXTENSION)Fdo->DeviceExtension;
+       RtlZeroMemory(DeviceExtension, sizeof(CLASS_DEVICE_EXTENSION));
        DeviceExtension->Common.IsClassDO = FALSE;
+       DeviceExtension->DeviceObject = Fdo;
        DeviceExtension->PnpState = dsStopped;
-       Fdo->Flags |= DO_POWER_PAGABLE;
        Status = IoAttachDeviceToDeviceStackSafe(Fdo, Pdo, &DeviceExtension->LowerDevice);
        if (!NT_SUCCESS(Status))
        {
                DPRINT("IoAttachDeviceToDeviceStackSafe() failed with status 0x%08lx\n", Status);
-               IoDeleteDevice(Fdo);
-               return Status;
+               goto cleanup;
        }
-       Fdo->Flags |= DO_BUFFERED_IO;
-       Fdo->Flags &= ~DO_DEVICE_INITIALIZING;
+       if (DeviceExtension->LowerDevice->Flags & DO_POWER_PAGABLE)
+               Fdo->Flags |= DO_POWER_PAGABLE;
+       if (DeviceExtension->LowerDevice->Flags & DO_BUFFERED_IO)
+               Fdo->Flags |= DO_BUFFERED_IO;
+       if (DeviceExtension->LowerDevice->Flags & DO_DIRECT_IO)
+               Fdo->Flags |= DO_DIRECT_IO;
 
        if (DriverExtension->ConnectMultiplePorts)
-               Status = ConnectMousePortDriver(Fdo, DriverExtension->MainMouclassDeviceObject);
+               DeviceExtension->ClassDO = DriverExtension->MainClassDeviceObject;
        else
-               Status = ConnectMousePortDriver(Fdo, Fdo);
+       {
+               /* We need a new class device object for this Fdo */
+               Status = CreateClassDeviceObject(
+                       DriverObject,
+                       &DeviceExtension->ClassDO);
+               if (!NT_SUCCESS(Status))
+               {
+                       DPRINT("CreateClassDeviceObject() failed with status 0x%08lx\n", Status);
+                       goto cleanup;
+               }
+       }
+       Status = ConnectPortDriver(Fdo, DeviceExtension->ClassDO);
        if (!NT_SUCCESS(Status))
        {
-               DPRINT("ConnectMousePortDriver() failed with status 0x%08lx\n", Status);
-               /* FIXME: why can't I cleanup without error? */
-               //IoDetachDevice(Fdo);
-               //IoDeleteDevice(Fdo);
-               return Status;
+               DPRINT("ConnectPortDriver() failed with status 0x%08lx\n", Status);
+               goto cleanup;
        }
+       Fdo->Flags &= ~DO_DEVICE_INITIALIZING;
 
-       /* Register GUID_DEVINTERFACE_MOUSE interface */
+       /* Register interface */
        Status = IoRegisterDeviceInterface(
                Pdo,
                &GUID_DEVINTERFACE_MOUSE,
                NULL,
-               &DeviceExtension->MouseInterfaceName);
-       if (!NT_SUCCESS(Status))
+               &DeviceExtension->InterfaceName);
+       if (Status == STATUS_INVALID_PARAMETER_1)
+       {
+               /* The Pdo was a strange one ; maybe it is a legacy device.
+                * Ignore the error. */
+               return STATUS_SUCCESS;
+       }
+       else if (!NT_SUCCESS(Status))
        {
                DPRINT("IoRegisterDeviceInterface() failed with status 0x%08lx\n", Status);
-               return Status;
+               goto cleanup;
        }
 
        return STATUS_SUCCESS;
+
+cleanup:
+       if (DeviceExtension)
+       {
+               if (DeviceExtension->LowerDevice)
+                       IoDetachDevice(DeviceExtension->LowerDevice);
+               if (DriverExtension->ConnectMultiplePorts && DeviceExtension->ClassDO)
+               {
+                       PCLASS_DEVICE_EXTENSION ClassDeviceExtension;
+                       ClassDeviceExtension = (PCLASS_DEVICE_EXTENSION)DeviceExtension->ClassDO->DeviceExtension;
+                       ExFreePool(ClassDeviceExtension->PortData);
+               }
+       }
+       if (Fdo)
+               IoDeleteDevice(Fdo);
+       return Status;
 }
 
 static VOID NTAPI
-MouclassStartIo(
+ClassStartIo(
        IN PDEVICE_OBJECT DeviceObject,
        IN PIRP Irp)
 {
-       PMOUCLASS_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
+       PCLASS_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
        PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
 
        ASSERT(DeviceExtension->Common.IsClassDO);
@@ -468,30 +686,35 @@ MouclassStartIo(
        if (DeviceExtension->InputCount > 0)
        {
                KIRQL oldIrql;
+               NTSTATUS Status;
 
                KeAcquireSpinLock(&DeviceExtension->SpinLock, &oldIrql);
 
-               /* FIXME: use SEH */
-               RtlCopyMemory(
-                       Irp->MdlAddress ? MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority) : Irp->UserBuffer,
-                       DeviceExtension->PortData - DeviceExtension->InputCount,
-                       sizeof(MOUSE_INPUT_DATA));
+               Status = FillOneEntry(
+                       DeviceObject,
+                       Irp,
+                       DeviceExtension->PortData - DeviceExtension->InputCount);
 
-               if (DeviceExtension->InputCount > 1)
+               if (NT_SUCCESS(Status))
                {
-                       RtlMoveMemory(
-                               DeviceExtension->PortData - DeviceExtension->InputCount,
-                               DeviceExtension->PortData - DeviceExtension->InputCount + 1,
-                               (DeviceExtension->InputCount - 1) * sizeof(MOUSE_INPUT_DATA));
+                       if (DeviceExtension->InputCount > 1)
+                       {
+                               RtlMoveMemory(
+                                       DeviceExtension->PortData - DeviceExtension->InputCount,
+                                       DeviceExtension->PortData - DeviceExtension->InputCount + 1,
+                                       (DeviceExtension->InputCount - 1) * sizeof(MOUSE_INPUT_DATA));
+                       }
+
+                       DeviceExtension->PortData--;
+                       DeviceExtension->InputCount--;
+                       DeviceExtension->ReadIsPending = FALSE;
+
+                       Irp->IoStatus.Information = sizeof(MOUSE_INPUT_DATA);
+                       Stack->Parameters.Read.Length = sizeof(MOUSE_INPUT_DATA);
                }
-               DeviceExtension->PortData--;
-               DeviceExtension->InputCount--;
-               DeviceExtension->ReadIsPending = FALSE;
-
-               /* Go to next packet and complete this request with STATUS_SUCCESS */
-               Irp->IoStatus.Status = STATUS_SUCCESS;
-               Irp->IoStatus.Information = sizeof(MOUSE_INPUT_DATA);
-               Stack->Parameters.Read.Length = sizeof(MOUSE_INPUT_DATA);
+
+               /* Go to next packet and complete this request */
+               Irp->IoStatus.Status = Status;
                IoCompleteRequest(Irp, IO_MOUSE_INCREMENT);
 
                IoStartNextPacket(DeviceObject, FALSE);
@@ -506,7 +729,7 @@ MouclassStartIo(
 static NTSTATUS
 SearchForLegacyDrivers(
        IN PDRIVER_OBJECT DriverObject,
-       IN PMOUCLASS_DRIVER_EXTENSION DriverExtension)
+       IN PCLASS_DRIVER_EXTENSION DriverExtension)
 {
        UNICODE_STRING DeviceMapKeyU = RTL_CONSTANT_STRING(L"\\REGISTRY\\MACHINE\\HARDWARE\\DEVICEMAP");
        UNICODE_STRING PortBaseName = {0, };
@@ -521,7 +744,7 @@ SearchForLegacyDrivers(
        /* Create port base name, by replacing Class by Port at the end of the class base name */
        Status = RtlDuplicateUnicodeString(
                RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE,
-               &DriverExtension->PointerDeviceBaseName,
+               &DriverExtension->DeviceBaseName,
                &PortBaseName);
        if (!NT_SUCCESS(Status))
        {
@@ -544,7 +767,13 @@ SearchForLegacyDrivers(
        /* Open HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP */
        InitializeObjectAttributes(&ObjectAttributes, &DeviceMapKeyU, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL);
        Status = ZwOpenKey(&hDeviceMapKey, 0, &ObjectAttributes);
-       if (!NT_SUCCESS(Status))
+       if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
+       {
+               DPRINT("HKLM\\HARDWARE\\DEVICEMAP is non-existent\n");
+               Status = STATUS_SUCCESS;
+               goto cleanup;
+       }
+       else if (!NT_SUCCESS(Status))
        {
                DPRINT("ZwOpenKey() failed with status 0x%08lx\n", Status);
                goto cleanup;
@@ -553,10 +782,15 @@ SearchForLegacyDrivers(
        /* Open sub key */
        InitializeObjectAttributes(&ObjectAttributes, &PortBaseName, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, hDeviceMapKey, NULL);
        Status = ZwOpenKey(&hPortKey, KEY_QUERY_VALUE, &ObjectAttributes);
-       if (!NT_SUCCESS(Status))
+       if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
+       {
+               DPRINT("HKLM\\HARDWARE\\DEVICEMAP\\%wZ is non-existent\n", &PortBaseName);
+               Status = STATUS_SUCCESS;
+               goto cleanup;
+       }
+       else if (!NT_SUCCESS(Status))
        {
                DPRINT("ZwOpenKey() failed with status 0x%08lx\n", Status);
-               DPRINT1("ZwOpenKey() failed with status 0x%08lx\n", Status);
                goto cleanup;
        }
 
@@ -575,37 +809,15 @@ SearchForLegacyDrivers(
                if (!NT_SUCCESS(Status))
                {
                        DPRINT("IoGetDeviceObjectPointer(%wZ) failed with status 0x%08lx\n", Status);
+                       continue;
                }
+               DPRINT("Legacy driver found: %wZ\n", &PortDeviceObject->DriverObject->DriverName);
 
-               /* Connect the port device object */
-               if (DriverExtension->ConnectMultiplePorts)
+               Status = ClassAddDevice(DriverObject, PortDeviceObject);
+               if (!NT_SUCCESS(Status))
                {
-                       Status = ConnectMousePortDriver(PortDeviceObject, DriverExtension->MainMouclassDeviceObject);
-                       if (!NT_SUCCESS(Status))
-                       {
-                               /* FIXME: Log the error */
-                               DPRINT("ConnectMousePortDriver() failed with status 0x%08lx\n", Status);
-                               /* FIXME: cleanup */
-                       }
-               }
-               else
-               {
-                       PDEVICE_OBJECT ClassDO;
-                       Status = CreatePointerClassDeviceObject(DriverObject, &ClassDO);
-                       if (!NT_SUCCESS(Status))
-                       {
-                               /* FIXME: Log the error */
-                               DPRINT("CreatePointerClassDeviceObject() failed with status 0x%08lx\n", Status);
-                               /* FIXME: cleanup */
-                               continue;
-                       }
-                       Status = ConnectMousePortDriver(PortDeviceObject, ClassDO);
-                       if (!NT_SUCCESS(Status))
-                       {
-                               /* FIXME: Log the error */
-                               DPRINT("ConnectMousePortDriver() failed with status 0x%08lx\n", Status);
-                               /* FIXME: cleanup */
-                       }
+                       /* FIXME: Log the error */
+                       DPRINT("ClassAddDevice() failed with status 0x%08lx\n", Status);
                }
        }
        if (Status == STATUS_NO_MORE_ENTRIES)
@@ -629,21 +841,31 @@ DriverEntry(
        IN PDRIVER_OBJECT DriverObject,
        IN PUNICODE_STRING RegistryPath)
 {
-       PMOUCLASS_DRIVER_EXTENSION DriverExtension;
+       PCLASS_DRIVER_EXTENSION DriverExtension;
        ULONG i;
        NTSTATUS Status;
 
        Status = IoAllocateDriverObjectExtension(
                DriverObject,
                DriverObject,
-               sizeof(MOUCLASS_DRIVER_EXTENSION),
+               sizeof(CLASS_DRIVER_EXTENSION),
                (PVOID*)&DriverExtension);
        if (!NT_SUCCESS(Status))
        {
                DPRINT("IoAllocateDriverObjectExtension() failed with status 0x%08lx\n", Status);
                return Status;
        }
-       RtlZeroMemory(DriverExtension, sizeof(MOUCLASS_DRIVER_EXTENSION));
+       RtlZeroMemory(DriverExtension, sizeof(CLASS_DRIVER_EXTENSION));
+
+       Status = RtlDuplicateUnicodeString(
+               RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE,
+               RegistryPath,
+               &DriverExtension->RegistryPath);
+       if (!NT_SUCCESS(Status))
+       {
+               DPRINT("RtlDuplicateUnicodeString() failed with status 0x%08lx\n", Status);
+               return Status;
+       }
 
        Status = ReadRegistryEntries(RegistryPath, DriverExtension);
        if (!NT_SUCCESS(Status))
@@ -654,28 +876,29 @@ DriverEntry(
 
        if (DriverExtension->ConnectMultiplePorts == 1)
        {
-               Status = CreatePointerClassDeviceObject(
+               Status = CreateClassDeviceObject(
                        DriverObject,
-                       &DriverExtension->MainMouclassDeviceObject);
+                       &DriverExtension->MainClassDeviceObject);
                if (!NT_SUCCESS(Status))
                {
-                       DPRINT("CreatePointerClassDeviceObject() failed with status 0x%08lx\n", Status);
+                       DPRINT("CreateClassDeviceObject() failed with status 0x%08lx\n", Status);
                        return Status;
                }
        }
 
-       DriverObject->DriverExtension->AddDevice = MouclassAddDevice;
+       DriverObject->DriverExtension->AddDevice = ClassAddDevice;
        DriverObject->DriverUnload = DriverUnload;
 
-       for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
+       for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
                DriverObject->MajorFunction[i] = IrpStub;
 
-       DriverObject->MajorFunction[IRP_MJ_CREATE] = MouclassCreate;
-       DriverObject->MajorFunction[IRP_MJ_CLOSE]  = MouclassClose;
-       DriverObject->MajorFunction[IRP_MJ_READ]   = MouclassRead;
-       DriverObject->DriverStartIo                = MouclassStartIo;
-
-       Status = SearchForLegacyDrivers(DriverObject, DriverExtension);
+       DriverObject->MajorFunction[IRP_MJ_CREATE]         = ClassCreate;
+       DriverObject->MajorFunction[IRP_MJ_CLOSE]          = ClassClose;
+       DriverObject->MajorFunction[IRP_MJ_CLEANUP]        = ClassCleanup;
+       DriverObject->MajorFunction[IRP_MJ_READ]           = ClassRead;
+       DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ClassDeviceControl;
+       DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = ForwardIrpAndForget;
+       DriverObject->DriverStartIo                        = ClassStartIo;
 
-       return Status;
+       return STATUS_SUCCESS;
 }