/*++

Copyright (c) 2000 Microsoft Corporation

Module Name:

    hidpen.c

Abstract: Serial Pen Tablet HID Driver.

Environment:

    Kernel mode

Author:

    Michael Tsang (MikeTs) 13-Mar-2000

Revision History:

--*/

#include "pch.h"

#ifdef ALLOC_PRAGMA
  #pragma alloc_text(INIT, DriverEntry)
  #pragma alloc_text(PAGE, HpenCreateClose)
  #pragma alloc_text(PAGE, HpenAddDevice)
  #pragma alloc_text(PAGE, HpenUnload)
#endif  //ifdef ALLOC_PRAGMA

/*****************************************************************************
 *
 *  @doc    EXTERNAL
 *
 *  @func   NTSTATUS | DriverEntry |
 *          Installable driver initialization entry point.
 *          <nl>This entry point is called directly by the I/O system.
 *
 *  @parm   IN PDRIVER_OBJECT | DrvObj | Points to the driver object.
 *  @parm   IN PUNICODE_STRINT | RegPath | Points to the registry path.
 *
 *  @rvalue SUCCESS | returns STATUS_SUCCESS
 *  @rvalue FAILURE | returns NT status code
 *
 *****************************************************************************/

NTSTATUS EXTERNAL
DriverEntry(
    IN PDRIVER_OBJECT  DrvObj,
    IN PUNICODE_STRING RegPath
    )
{
    PROCNAME("DriverEntry")
    NTSTATUS status = STATUS_SUCCESS;
    HID_MINIDRIVER_REGISTRATION hidMinidriverRegistration;

    ENTER(1, ("(DrvObj=%p,RegPath=%p)\n", DrvObj, RegPath));

    DrvObj->MajorFunction[IRP_MJ_CREATE] =
    DrvObj->MajorFunction[IRP_MJ_CLOSE] = HpenCreateClose;

    DrvObj->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = HpenInternalIoctl;

    DrvObj->MajorFunction[IRP_MJ_PNP]   = HpenPnp;
    DrvObj->MajorFunction[IRP_MJ_POWER] = HpenPower;
    DrvObj->DriverUnload                = HpenUnload;
    DrvObj->DriverExtension->AddDevice  = HpenAddDevice;

    //
    // Register with HIDCLASS.SYS module
    //
    RtlZeroMemory(&hidMinidriverRegistration,
                  sizeof(hidMinidriverRegistration));

    hidMinidriverRegistration.Revision            = HID_REVISION;
    hidMinidriverRegistration.DriverObject        = DrvObj;
    hidMinidriverRegistration.RegistryPath        = RegPath;
    hidMinidriverRegistration.DeviceExtensionSize = sizeof(DEVICE_EXTENSION);
    hidMinidriverRegistration.DevicesArePolled    = FALSE;

    status = HidRegisterMinidriver(&hidMinidriverRegistration);

    if (NT_SUCCESS(status))
    {
      #ifdef DEBUG
        ExInitializeFastMutex(&gmutexDevExtList);
        InitializeListHead(&glistDevExtHead);
      #endif
    }
    else
    {
        ERRPRINT(("failed to register mini driver (status=%x)\n", status));
    }

    EXIT(1, ("=%x\n", status));
    return status;
}       //DriverEntry

/*****************************************************************************
 *
 *  @doc    EXTERNAL
 *
 *  @func   NTSTATUS | HpenCreateClose |
 *          Process the create and close IRPs sent to this device.
 *
 *  @parm   IN PDEVICE_OBJECT | DevObj | Points to the device object.
 *  @parm   IN PIRP | Irp | Points to an I/O Request Packet.
 *
 *  @rvalue STATUS_SUCCESS | success
 *  @rvalue STATUS_INVALID_PARAMETER  | Irp not handled
 *
 *****************************************************************************/

NTSTATUS EXTERNAL
HpenCreateClose(
    IN PDEVICE_OBJECT DevObj,
    IN PIRP Irp
    )
{
    PROCNAME("HpenCreateClose")
    NTSTATUS status = STATUS_SUCCESS;
    PIO_STACK_LOCATION irpsp;

    PAGED_CODE ();

    UNREFERENCED_PARAMETER(DevObj);
    irpsp = IoGetCurrentIrpStackLocation(Irp);

    ENTER(1, ("(DevObj=%p,Irp=%p,IrpStack=%p,Major=%s)\n",
              DevObj, Irp, irpsp,
              LookupName(irpsp->MajorFunction, MajorFnNames)));

    switch(irpsp->MajorFunction)
    {
        case IRP_MJ_CREATE:
        case IRP_MJ_CLOSE:
            Irp->IoStatus.Information = 0;
            break;

        default:
            ERRPRINT(("invalid major function %s\n",
                      LookupName(irpsp->MajorFunction, MajorFnNames)));
            status = STATUS_INVALID_PARAMETER;
            break;
    }

    Irp->IoStatus.Status = status;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);

    EXIT(1, ("=%x\n", status));
    return status;
}       //HpenCreateClose

/*****************************************************************************
 *
 *  @doc    EXTERNAL
 *
 *  @func   NTSTATUS | HpenAddDevice |
 *          Called by hidclass, allows us to initialize our device extensions.
 *
 *  @parm   IN PDRIVER_OBJECT | DrvObj | Points to the driver object.
 *  @parm   IN PDEVICE_OBJECT | DevObj |
 *          Points to a functional device object created by hidclass.
 *
 *  @rvalue SUCCESS | Returns STATUS_SUCCESS.
 *  @rvalue FAILURE | Returns NT status code.
 *
 *****************************************************************************/

NTSTATUS EXTERNAL
HpenAddDevice(
    IN PDRIVER_OBJECT DrvObj,
    IN PDEVICE_OBJECT DevObj
    )
{
    PROCNAME("HpenAddDevice")
    NTSTATUS status;
    PDEVICE_EXTENSION devext;

    PAGED_CODE ();

    ENTER(1, ("(DrvObj=%p,DevObj=%p)\n", DrvObj, DevObj));

    ASSERT(DevObj != NULL);
    UNREFERENCED_PARAMETER(DrvObj);

    devext = GET_MINIDRIVER_DEVICE_EXTENSION(DevObj);

    RtlZeroMemory(devext, sizeof(*devext));
    devext->pdo = GET_PDO(DevObj);
    devext->SerialDevObj = GET_NEXT_DEVICE_OBJECT(DevObj);
    devext->ReadWorkItem[0].WorkItem = IoAllocateWorkItem(DevObj);
    if (devext->ReadWorkItem[0].WorkItem != NULL)
    {
        devext->ReadWorkItem[1].WorkItem = IoAllocateWorkItem(DevObj);
        if (devext->ReadWorkItem[1].WorkItem != NULL)
        {
            IoInitializeRemoveLock(&devext->RemoveLock, HPEN_POOL_TAG, 0, 10);
            KeInitializeSpinLock(&devext->SpinLock);
            devext->PowerState = PowerDeviceD0;
          #ifdef DEBUG
            ExAcquireFastMutex(&gmutexDevExtList);
            InsertTailList(&glistDevExtHead, &devext->List);
            ExReleaseFastMutex(&gmutexDevExtList);
          #endif
            OemAddDevice(devext);
            DevObj->Flags &= ~DO_DEVICE_INITIALIZING;
            DevObj->Flags |= DO_POWER_PAGABLE;
            status = STATUS_SUCCESS;
        }
        else
        {
            ERRPRINT(("failed to allocate second read work item\n"));
            IoFreeWorkItem(devext->ReadWorkItem[0].WorkItem);
            status = STATUS_INSUFFICIENT_RESOURCES;
        }
    }
    else
    {
        ERRPRINT(("failed to allocate first read work item\n"));
        status = STATUS_INSUFFICIENT_RESOURCES;
    }

    EXIT(1, ("=%x\n", status));
    return status;
}       //HpenAddDevice

/*****************************************************************************
 *
 *  @doc    EXTERNAL
 *
 *  @func   void | HpenUnload | Free all the allocated resources, etc.
 *
 *  @parm   IN PDRIVER_OBJECT | DrvObj | Points to the driver object.
 *
 *****************************************************************************/

VOID EXTERNAL
HpenUnload(
    IN PDRIVER_OBJECT DrvObj
    )
{
    PROCNAME("HpenUnload")

    PAGED_CODE();

    ENTER(1, ("(DrvObj=%p)\n", DrvObj));

    ASSERT(DrvObj->DeviceObject == NULL);
    UNREFERENCED_PARAMETER(DrvObj);

    EXIT(1, ("!\n"));
    return;
}       //HpenUnload