Don't call IoRaiseHardError at DISPATCH_LEVEL
[reactos.git] / reactos / drivers / input / mouclass / mouclass.c
index b7b972a..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)
 {
@@ -30,6 +35,9 @@ ClassCreate(
                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;
 }
 
@@ -44,6 +52,9 @@ ClassClose(
                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;
 }
 
@@ -58,6 +69,9 @@ ClassCleanup(
                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;
 }
 
@@ -91,7 +105,7 @@ ClassDeviceControl(
        IN PIRP Irp)
 {
        PCLASS_DEVICE_EXTENSION DeviceExtension;
-       NTSTATUS Status;
+       NTSTATUS Status = Irp->IoStatus.Status;
 
        DPRINT("IRP_MJ_DEVICE_CONTROL\n");
 
@@ -103,13 +117,25 @@ ClassDeviceControl(
        switch (IoGetCurrentIrpStackLocation(Irp)->Parameters.DeviceIoControl.IoControlCode)
        {
                case IOCTL_MOUSE_QUERY_ATTRIBUTES:
-                       /* FIXME */
-                       Status = STATUS_NOT_SUPPORTED;
+               {
+                       /* 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);
-                       Status = STATUS_NOT_SUPPORTED;
+                       ASSERT(FALSE);
+                       break;
        }
 
        Irp->IoStatus.Status = Status;
@@ -131,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:
@@ -201,7 +228,7 @@ ReadRegistryEntries(
        Parameters[2].EntryContext = &DriverExtension->DeviceBaseName;
        Parameters[2].DefaultType = REG_SZ;
        Parameters[2].DefaultData = &DefaultDeviceBaseName;
-       Parameters[2].DefaultLength = sizeof(ULONG);
+       Parameters[2].DefaultLength = 0;
 
        Status = RtlQueryRegistryValues(
                RTL_REGISTRY_ABSOLUTE,
@@ -301,8 +328,8 @@ CreateClassDeviceObject(
                }
                DeviceId++;
        }
-       DPRINT("Too much devices starting with '\\Device\\%wZ'\n", &DriverExtension->DeviceBaseName);
-       Status = STATUS_UNSUCCESSFUL;
+       DPRINT("Too many devices starting with '\\Device\\%wZ'\n", &DriverExtension->DeviceBaseName);
+       Status = STATUS_TOO_MANY_NAMES;
 cleanup:
        if (!NT_SUCCESS(Status))
        {
@@ -314,14 +341,23 @@ cleanup:
        RtlZeroMemory(DeviceExtension, sizeof(CLASS_DEVICE_EXTENSION));
        DeviceExtension->Common.IsClassDO = TRUE;
        DeviceExtension->DriverExtension = DriverExtension;
-       KeInitializeSpinLock(&(DeviceExtension->SpinLock));
+       InitializeListHead(&DeviceExtension->ListHead);
+       KeInitializeSpinLock(&DeviceExtension->ListSpinLock);
+       KeInitializeSpinLock(&DeviceExtension->SpinLock);
        DeviceExtension->ReadIsPending = FALSE;
        DeviceExtension->InputCount = 0;
        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);
 
@@ -331,6 +367,53 @@ 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
 ClassCallback(
        IN PDEVICE_OBJECT ClassDeviceObject,
@@ -347,6 +430,8 @@ ClassCallback(
 
        ASSERT(ClassDeviceExtension->Common.IsClassDO);
 
+       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
@@ -354,45 +439,48 @@ ClassCallback(
         */
        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,
-                       DataStart,
-                       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 */
-               DataStart++;
-               (*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->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, data gets thrown away.. better
-                * solution?
-               */
-
                /*
                 * Move the input data from the port data queue to our class data
                 * queue.
@@ -406,7 +494,6 @@ ClassCallback(
                ClassDeviceExtension->PortData += ReadSize;
                ClassDeviceExtension->InputCount += ReadSize;
 
-               KeReleaseSpinLock(&ClassDeviceExtension->SpinLock, OldIrql);
                (*ConsumedCount) += ReadSize;
        }
        else
@@ -414,6 +501,8 @@ ClassCallback(
                DPRINT("ClassCallBack() entered, InputCount = %lu - DOING NOTHING\n", InputCount);
        }
 
+       KeReleaseSpinLock(&ClassDeviceExtension->SpinLock, OldIrql);
+
        if (Irp != NULL)
        {
                IoStartNextPacket(ClassDeviceObject, FALSE);
@@ -455,7 +544,20 @@ ConnectPortDriver(
                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;
 }
@@ -466,54 +568,71 @@ ClassAddDevice(
        IN PDEVICE_OBJECT Pdo)
 {
        PCLASS_DRIVER_EXTENSION DriverExtension;
-       PDEVICE_OBJECT Fdo;
-       PPORT_DEVICE_EXTENSION DeviceExtension;
+       PDEVICE_OBJECT Fdo = NULL;
+       PPORT_DEVICE_EXTENSION DeviceExtension = NULL;
        NTSTATUS Status;
 
        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(PORT_DEVICE_EXTENSION),
                NULL,
                Pdo->DeviceType,
-               FILE_DEVICE_SECURE_OPEN,
+               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 = (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;
+       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 = ConnectPortDriver(Fdo, DriverExtension->MainClassDeviceObject);
+               DeviceExtension->ClassDO = DriverExtension->MainClassDeviceObject;
        else
-               Status = ConnectPortDriver(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("ConnectPortDriver() failed with status 0x%08lx\n", Status);
-               IoDetachDevice(DeviceExtension->LowerDevice);
-               /* FIXME: why can't I cleanup without error? */
-               //IoDeleteDevice(Fdo);
-               return Status;
+               goto cleanup;
        }
        Fdo->Flags &= ~DO_DEVICE_INITIALIZING;
 
@@ -523,13 +642,35 @@ ClassAddDevice(
                &GUID_DEVINTERFACE_MOUSE,
                NULL,
                &DeviceExtension->InterfaceName);
-       if (!NT_SUCCESS(Status))
+       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
@@ -545,30 +686,35 @@ ClassStartIo(
        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);
@@ -663,35 +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 = ConnectPortDriver(PortDeviceObject, DriverExtension->MainClassDeviceObject);
-                       if (!NT_SUCCESS(Status))
-                       {
-                               /* FIXME: Log the error */
-                               DPRINT("ConnectPortDriver() failed with status 0x%08lx\n", Status);
-                       }
-               }
-               else
+               Status = ClassAddDevice(DriverObject, PortDeviceObject);
+               if (!NT_SUCCESS(Status))
                {
-                       PDEVICE_OBJECT ClassDO;
-                       Status = CreateClassDeviceObject(DriverObject, &ClassDO);
-                       if (!NT_SUCCESS(Status))
-                       {
-                               /* FIXME: Log the error */
-                               DPRINT("CreatePointerClassDeviceObject() failed with status 0x%08lx\n", Status);
-                               continue;
-                       }
-                       Status = ConnectPortDriver(PortDeviceObject, ClassDO);
-                       if (!NT_SUCCESS(Status))
-                       {
-                               /* FIXME: Log the error */
-                               DPRINT("ConnectPortDriver() failed with status 0x%08lx\n", Status);
-                               IoDeleteDevice(ClassDO);
-                       }
+                       /* FIXME: Log the error */
+                       DPRINT("ClassAddDevice() failed with status 0x%08lx\n", Status);
                }
        }
        if (Status == STATUS_NO_MORE_ENTRIES)
@@ -731,6 +857,16 @@ DriverEntry(
        }
        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))
        {
@@ -753,7 +889,7 @@ DriverEntry(
        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]         = ClassCreate;
@@ -761,9 +897,8 @@ DriverEntry(
        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;
 
-       Status = SearchForLegacyDrivers(DriverObject, DriverExtension);
-
-       return Status;
+       return STATUS_SUCCESS;
 }