/*++ Copyright (c) 1990-1998 Microsoft Corporation, All Rights Reserved Copyright (c) 1993 Logitech Inc. Module Name: sermcmn.c Abstract: The common portions of the Microsoft serial (i8250) mouse port driver. This file should not require modification to support new mice that are similar to the serial mouse. Environment: Kernel mode only. Notes: NOTES: (Future/outstanding issues) - Powerfail not implemented. - IOCTL_INTERNAL_MOUSE_DISCONNECT has not been implemented. It's not needed until the class unload routine is implemented. Right now, we don't want to allow the mouse class driver to unload. - Consolidate duplicate code, where possible and appropriate. Revision History: --*/ #include "stdarg.h" #include "stdio.h" #include "string.h" #include "ntddk.h" #include "mouser.h" #include "sermlog.h" #include "debug.h" #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, SerialMouseCreate) #pragma alloc_text(PAGE, SerialMouseClose) #endif NTSTATUS SerialMouseFlush( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { PDEVICE_EXTENSION deviceExtension; NTSTATUS status; // // Get a pointer to the device extension. // deviceExtension = DeviceObject->DeviceExtension; Print(deviceExtension, DBG_UART_INFO, ("Flush \n")); status = IoAcquireRemoveLock(&deviceExtension->RemoveLock, Irp); if (!NT_SUCCESS(status)) { return status; } // // Fire and forget // IoSkipCurrentIrpStackLocation(Irp); status = IoCallDriver(deviceExtension->TopOfStack, Irp); IoReleaseRemoveLock(&deviceExtension->RemoveLock, Irp); return status; } NTSTATUS SerialMouseInternalDeviceControl( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine is the dispatch routine for internal device control requests. Arguments: DeviceObject - Pointer to the device object. Irp - Pointer to the request packet. Return Value: Status is returned. --*/ { PIO_STACK_LOCATION irpSp; PDEVICE_EXTENSION deviceExtension; NTSTATUS status; // // Get a pointer to the device extension. // deviceExtension = DeviceObject->DeviceExtension; Print(deviceExtension, DBG_IOCTL_TRACE, ("IOCTL, enter\n")); // // Initialize the returned Information field. // Irp->IoStatus.Information = 0; // // Get a pointer to the current parameters for this request. The // information is contained in the current stack location. // irpSp = IoGetCurrentIrpStackLocation(Irp); status = IoAcquireRemoveLock(&deviceExtension->RemoveLock, Irp); if (!NT_SUCCESS(status)) { Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; } ASSERT (deviceExtension->Started || (IOCTL_INTERNAL_MOUSE_CONNECT == irpSp->Parameters.DeviceIoControl.IoControlCode)); // // Case on the device control subfunction that is being performed by the // requestor. // switch (irpSp->Parameters.DeviceIoControl.IoControlCode) { // // Connect a mouse class device driver to the port driver. // case IOCTL_INTERNAL_MOUSE_CONNECT: Print(deviceExtension, DBG_IOCTL_INFO, ("connect\n")); // // Only allow one connection. // if (deviceExtension->ConnectData.ClassService != NULL) { Print(deviceExtension, DBG_IOCTL_ERROR, ("error - already connected\n")); status = STATUS_SHARING_VIOLATION; break; } else if (irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(CONNECT_DATA)) { Print(deviceExtension, DBG_IOCTL_ERROR, ("connect error - invalid buffer length\n")); status = STATUS_INVALID_PARAMETER; break; } // // Copy the connection parameters to the device extension. // deviceExtension->ConnectData = *((PCONNECT_DATA) (irpSp->Parameters.DeviceIoControl.Type3InputBuffer)); status = STATUS_SUCCESS; break; // // Disconnect a mouse class device driver from the port driver. // // NOTE: Not implemented. // case IOCTL_INTERNAL_MOUSE_DISCONNECT: Print(deviceExtension, DBG_IOCTL_INFO, ("disconnect\n")); TRAP(); // // Not implemented. // // To implement, code the following: // --------------------------------- // o ENSURE that we are NOT enabled (extension->EnableCount); // o If we are, then (a) return STATUS_UNSUCCESSFUL, or // (b) disable all devices immediately; see // DISABLE IOCTL call for necessary code. // o SYNCHRONIZE with the mouse read completion routine (must // protect the callback pointer from being dereferenced when // it becomes null). Note that no mechanism currently exists // for this. // o CLEAR the connection parameters in the device extension; // ie. extension->ConnectData = { 0, 0 } // o RELEASE the synchronizing lock. // o RETURN STATUS_SUCCESS. // // // Clear the connection parameters in the device extension. // NOTE: Must synchronize this with the mouse ISR. // // //deviceExtension->ConnectData.ClassDeviceObject = // Null; //deviceExtension->ConnectData.ClassService = // Null; // // Set the completion status. // status = STATUS_NOT_IMPLEMENTED; break; case IOCTL_INTERNAL_MOUSE_ENABLE: // // Enable interrupts // Print (deviceExtension, DBG_IOCTL_ERROR, ("ERROR: PnP => use create not enable! \n")); status = STATUS_NOT_SUPPORTED; break; case IOCTL_INTERNAL_MOUSE_DISABLE: // // Disable Mouse interrupts // Print(deviceExtension, DBG_IOCTL_ERROR, ("ERROR: PnP => use close not Disable! \n")); status = STATUS_NOT_SUPPORTED; break; // // Query the mouse attributes. First check for adequate buffer // length. Then, copy the mouse attributes from the device // extension to the output buffer. // case IOCTL_MOUSE_QUERY_ATTRIBUTES: Print(deviceExtension, DBG_IOCTL_INFO, ("query attributes\n")); if (irpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUSE_ATTRIBUTES)) { Print(deviceExtension, DBG_IOCTL_ERROR, ("QA buffer too small\n")); status = STATUS_BUFFER_TOO_SMALL; } else { // // Copy the attributes from the DeviceExtension to the // buffer. // *(PMOUSE_ATTRIBUTES) Irp->AssociatedIrp.SystemBuffer = deviceExtension->MouseAttributes; Irp->IoStatus.Information = sizeof(MOUSE_ATTRIBUTES); status = STATUS_SUCCESS; } break; default: Print (deviceExtension, DBG_IOCTL_ERROR, ("ERROR: unknown IOCTL: 0x%x \n", irpSp->Parameters.DeviceIoControl.IoControlCode)); TRAP(); status = STATUS_INVALID_DEVICE_REQUEST; break; } Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); IoReleaseRemoveLock(&deviceExtension->RemoveLock, Irp); Print(deviceExtension, DBG_IOCTL_TRACE, ("IOCTL, exit (%x)\n", status)); return status; } NTSTATUS SerialMouseClose( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { PDEVICE_EXTENSION deviceExtension; NTSTATUS status; PAGED_CODE(); deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension; Print(deviceExtension, DBG_CC_NOISE, ("Close: enable count is %d\n", deviceExtension->EnableCount)); ASSERT(0 < deviceExtension->EnableCount); status = IoAcquireRemoveLock(&deviceExtension->RemoveLock, Irp); if (!NT_SUCCESS(status)) { goto SerialMouseCloseReject; } // // Serial can only handle one create/close, all others fail. This is not // true for mice though. Only send the last close on to serial. // if (0 == InterlockedDecrement(&deviceExtension->EnableCount)) { Print(deviceExtension, DBG_PNP_INFO | DBG_CC_INFO, ("Cancelling and stopping detection for close\n")); // // Cleanup: cancel the read and stop detection // IoCancelIrp(deviceExtension->ReadIrp); SerialMouseStopDetection(deviceExtension); // // Restore the port to the state it was before we opened it // SerialMouseRestorePort(deviceExtension); IoReleaseRemoveLock(&deviceExtension->RemoveLock, Irp); IoSkipCurrentIrpStackLocation(Irp); return IoCallDriver(deviceExtension->TopOfStack, Irp); } else { Print(deviceExtension, DBG_CC_INFO, ("Close (%d)\n", deviceExtension->EnableCount)); status = STATUS_SUCCESS; } IoReleaseRemoveLock(&deviceExtension->RemoveLock, Irp); SerialMouseCloseReject: Irp->IoStatus.Status = status; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; } NTSTATUS SerialMouseCreate( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This is the dispatch routine for create/open requests. These requests complete successfully. Arguments: DeviceObject - Pointer to the device object. Irp - Pointer to the request packet. Return Value: Status is returned. --*/ { PIO_STACK_LOCATION irpSp = NULL; NTSTATUS status = STATUS_SUCCESS; PDEVICE_EXTENSION deviceExtension = NULL; deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension; Print(deviceExtension, DBG_CC_TRACE, ("Create: Enter.\n")); Print(deviceExtension, DBG_CC_NOISE, ("Create: enable count is %d\n")); // // Get a pointer to the current parameters for this request. The // information is contained in the current stack location. // irpSp = IoGetCurrentIrpStackLocation (Irp); // // Determine if request is trying to open a subdirectory of the // given device object. This is not allowed. // if (0 != irpSp->FileObject->FileName.Length) { Print(deviceExtension, DBG_CC_ERROR, ("ERROR: Create Access Denied.\n")); status = STATUS_ACCESS_DENIED; goto SerialMouseCreateReject; } status = IoAcquireRemoveLock(&deviceExtension->RemoveLock, Irp); if (!NT_SUCCESS(status)) { goto SerialMouseCreateReject; } if (NULL == deviceExtension->ConnectData.ClassService) { // // No Connection yet. How can we be enabled? // Print(deviceExtension, DBG_IOCTL_ERROR, ("ERROR: enable before connect!\n")); status = STATUS_UNSUCCESSFUL; } else if ( 1 == InterlockedIncrement(&deviceExtension->EnableCount)) { // // send it down the stack // status = SerialMouseSendIrpSynchronously(deviceExtension->TopOfStack, Irp, TRUE); if (NT_SUCCESS(status) && NT_SUCCESS(Irp->IoStatus.Status)) { // // Everything worked, start up the mouse. // status = SerialMouseStartDevice(deviceExtension, Irp, TRUE); } else { // // Create failed, decrement the enable count back to zero // InterlockedDecrement(&deviceExtension->EnableCount); } } else { // // Serial only handles one create/close. Don't send this one down the // stack, it will fail. The call to InterlockedIncrement above // correctly adjusts the count. // ASSERT (deviceExtension->EnableCount >= 1); status = STATUS_SUCCESS; } IoReleaseRemoveLock(&deviceExtension->RemoveLock, Irp); SerialMouseCreateReject: Irp->IoStatus.Status = status; Irp->IoStatus.Information = 0; IoCompleteRequest (Irp, IO_NO_INCREMENT); Print(deviceExtension, DBG_CC_TRACE, ("SerialMouseCreate, 0x%x\n", status)); return status; }