513 lines
14 KiB
C
513 lines
14 KiB
C
/*++
|
|
|
|
Copyright (c) 2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
ioctl.c
|
|
|
|
Abstract: Contains routines to support HIDCLASS internal
|
|
ioctl queries for the pen tablet devices.
|
|
|
|
Environment:
|
|
|
|
Kernel mode
|
|
|
|
Author:
|
|
Michael Tsang (MikeTs) 13-Apr-2000
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "pch.h"
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, GetDeviceDescriptor)
|
|
#pragma alloc_text(PAGE, GetReportDescriptor)
|
|
#pragma alloc_text(PAGE, GetString)
|
|
#pragma alloc_text(PAGE, GetAttributes)
|
|
#endif
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc EXTERNAL
|
|
*
|
|
* @func NTSTATUS | HbutInternalIoctl |
|
|
* Process the Control IRPs sent to this device.
|
|
* <nl>This function cannot be pageable because reads/writes
|
|
* can be made at dispatch-level
|
|
*
|
|
* @parm IN PDRIVER_OBJECT | DevObj | Points to the driver object.
|
|
* @parm IN PIRP | Irp | Points to an I/O Request Packet.
|
|
*
|
|
* @rvalue SUCCESS | returns STATUS_SUCCESS
|
|
* @rvalue FAILURE | returns NT status code
|
|
*
|
|
*****************************************************************************/
|
|
|
|
NTSTATUS EXTERNAL
|
|
HbutInternalIoctl(
|
|
IN PDEVICE_OBJECT DevObj,
|
|
IN PIRP Irp
|
|
)
|
|
{
|
|
PROCNAME("HbutInternalIoctl")
|
|
NTSTATUS status;
|
|
PIO_STACK_LOCATION irpsp;
|
|
PDEVICE_EXTENSION devext;
|
|
|
|
irpsp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
ENTER(1, ("(DevObj=%p,Irp=%p,IrpSp=%p,Ioctl=%s)\n",
|
|
DevObj, Irp, irpsp,
|
|
LookupName(irpsp->Parameters.DeviceIoControl.IoControlCode,
|
|
HidIoctlNames)));
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
devext = GET_MINIDRIVER_DEVICE_EXTENSION(DevObj);
|
|
status = IoAcquireRemoveLock(&devext->RemoveLock, Irp);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
ERRPRINT(("received PnP IRP after device was removed\n"));
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
}
|
|
else
|
|
{
|
|
ASSERT(devext->dwfHBut & HBUTF_DEVICE_STARTED);
|
|
switch(irpsp->Parameters.DeviceIoControl.IoControlCode)
|
|
{
|
|
case IOCTL_HID_GET_DEVICE_DESCRIPTOR:
|
|
status = GetDeviceDescriptor(DevObj, Irp);
|
|
break;
|
|
|
|
case IOCTL_HID_GET_REPORT_DESCRIPTOR:
|
|
status = GetReportDescriptor(DevObj, Irp);
|
|
break;
|
|
|
|
case IOCTL_HID_READ_REPORT:
|
|
status = ReadReport(DevObj, Irp);
|
|
break;
|
|
|
|
case IOCTL_HID_GET_STRING:
|
|
status = GetString(DevObj, Irp);
|
|
break;
|
|
|
|
case IOCTL_HID_GET_DEVICE_ATTRIBUTES:
|
|
status = GetAttributes(DevObj, Irp);
|
|
break;
|
|
|
|
case IOCTL_HID_ACTIVATE_DEVICE:
|
|
case IOCTL_HID_DEACTIVATE_DEVICE:
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
default:
|
|
WARNPRINT(("unsupported ioctl code (ioctl=%s)\n",
|
|
LookupName(irpsp->Parameters.DeviceIoControl.IoControlCode,
|
|
HidIoctlNames)));
|
|
status = Irp->IoStatus.Status;
|
|
break;
|
|
}
|
|
|
|
if (status != STATUS_PENDING)
|
|
{
|
|
IoReleaseRemoveLock(&devext->RemoveLock, Irp);
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
}
|
|
else
|
|
{
|
|
IoMarkIrpPending(Irp);
|
|
}
|
|
}
|
|
|
|
EXIT(1, ("=%x\n", status));
|
|
return status;
|
|
} //HbutInternalIoctl
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func NTSTATUS | GetDeviceDescriptor |
|
|
* Respond to HIDCLASS IOCTL_HID_GET_DEVICE_DESCRIPTOR
|
|
* by returning a device descriptor.
|
|
*
|
|
* @parm IN PDRIVER_OBJECT | DevObj | Points to the driver object.
|
|
* @parm IN PIRP | Irp | Points to an I/O Request Packet.
|
|
*
|
|
* @rvalue SUCCESS | returns STATUS_SUCCESS
|
|
* @rvalue FAILURE | returns STATUS_BUFFER_TOO_SMALL - need more memory
|
|
*
|
|
*****************************************************************************/
|
|
|
|
NTSTATUS INTERNAL
|
|
GetDeviceDescriptor(
|
|
IN PDEVICE_OBJECT DevObj,
|
|
IN PIRP Irp
|
|
)
|
|
{
|
|
PROCNAME("GetDeviceDescriptor")
|
|
NTSTATUS status;
|
|
PIO_STACK_LOCATION irpsp;
|
|
|
|
PAGED_CODE ();
|
|
|
|
irpsp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
ENTER(2, ("(DevObj=%p,Irp=%p,IrpSp=%p)\n", DevObj, Irp, irpsp));
|
|
|
|
if (irpsp->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(gHidDescriptor))
|
|
{
|
|
ERRPRINT(("output buffer too small (bufflen=%d)\n",
|
|
irpsp->Parameters.DeviceIoControl.OutputBufferLength));
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
else
|
|
{
|
|
RtlCopyMemory(Irp->UserBuffer,
|
|
&gHidDescriptor,
|
|
sizeof(gHidDescriptor));
|
|
|
|
Irp->IoStatus.Information = sizeof(gHidDescriptor);
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
EXIT(2, ("=%x\n", status));
|
|
return status;
|
|
} //GetDeviceDescriptor
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func NTSTATUS | GetReportDescriptor |
|
|
* Respond to HIDCLASS IOCTL_HID_GET_REPORT_DESCRIPTOR
|
|
* by returning appropriate the report descriptor.
|
|
*
|
|
* @parm IN PDRIVER_OBJECT | DevObj | Points to the driver object.
|
|
* @parm IN PIRP | Irp | Points to an I/O Request Packet.
|
|
*
|
|
* @rvalue SUCCESS | returns STATUS_SUCCESS
|
|
* @rvalue FAILURE | returns NT status code
|
|
*
|
|
*****************************************************************************/
|
|
|
|
NTSTATUS INTERNAL
|
|
GetReportDescriptor(
|
|
IN PDEVICE_OBJECT DevObj,
|
|
IN PIRP Irp
|
|
)
|
|
{
|
|
PROCNAME("GetReportDescriptor")
|
|
NTSTATUS status;
|
|
PIO_STACK_LOCATION irpsp;
|
|
|
|
PAGED_CODE ();
|
|
|
|
irpsp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
ENTER(2, ("(DevObj=%p,Irp=%p,IrpSp=%p\n", DevObj, Irp, irpsp));
|
|
|
|
if (irpsp->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(gReportDescriptor))
|
|
{
|
|
ERRPRINT(("output buffer too small (bufflen=%d)\n",
|
|
irpsp->Parameters.DeviceIoControl.OutputBufferLength));
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
else
|
|
{
|
|
RtlCopyMemory(Irp->UserBuffer,
|
|
gReportDescriptor,
|
|
sizeof(gReportDescriptor));
|
|
|
|
Irp->IoStatus.Information = sizeof(gReportDescriptor);
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
EXIT(2, ("=%x\n", status));
|
|
return status;
|
|
} //GetReportDescriptor
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func NTSTATUS | ReadReport |
|
|
* Read input report.
|
|
*
|
|
* @parm IN PDRIVER_OBJECT | DevObj | Points to the driver object.
|
|
* @parm IN PIRP | Irp | Points to an I/O Request Packet.
|
|
*
|
|
* @rvalue SUCCESS | returns STATUS_SUCCESS
|
|
* @rvalue FAILURE | returns NT status code
|
|
*
|
|
*****************************************************************************/
|
|
|
|
NTSTATUS INTERNAL
|
|
ReadReport(
|
|
IN PDEVICE_OBJECT DevObj,
|
|
IN PIRP Irp
|
|
)
|
|
{
|
|
PROCNAME("ReadReport")
|
|
NTSTATUS status;
|
|
PIO_STACK_LOCATION irpsp;
|
|
PDEVICE_EXTENSION devext;
|
|
|
|
irpsp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
ENTER(2, ("(DevObj=%p,Irp=%p,IrpSp=%p)\n", DevObj, Irp, irpsp));
|
|
|
|
ASSERT(Irp->UserBuffer != NULL);
|
|
devext = GET_MINIDRIVER_DEVICE_EXTENSION(DevObj);
|
|
|
|
if (irpsp->Parameters.DeviceIoControl.OutputBufferLength !=
|
|
sizeof(OEM_INPUT_REPORT))
|
|
{
|
|
ERRPRINT(("invalid input report size (bufflen=%d)\n",
|
|
irpsp->Parameters.DeviceIoControl.OutputBufferLength));
|
|
status = STATUS_INVALID_BUFFER_SIZE;
|
|
}
|
|
else if (!(devext->dwfHBut & HBUTF_DEVICE_STARTED))
|
|
{
|
|
ERRPRINT(("device not started yet\n"));
|
|
status = STATUS_DEVICE_NOT_READY ;
|
|
}
|
|
else
|
|
{
|
|
KIRQL OldIrql;
|
|
PDRIVER_CANCEL OldCancelRoutine;
|
|
|
|
KeAcquireSpinLock(&devext->SpinLock, &OldIrql);
|
|
OldCancelRoutine = IoSetCancelRoutine(Irp, ReadReportCanceled);
|
|
ASSERT(OldCancelRoutine == NULL);
|
|
|
|
if (Irp->Cancel)
|
|
{
|
|
//
|
|
// This IRP was canceled. Do not queue it.
|
|
//
|
|
OldCancelRoutine = IoSetCancelRoutine(Irp, NULL);
|
|
if (OldCancelRoutine != NULL)
|
|
{
|
|
//
|
|
// Cancel routine was not called.
|
|
//
|
|
ASSERT(OldCancelRoutine == ReadReportCanceled);
|
|
status = STATUS_CANCELLED;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Cancel routine was called and it will complete this IRP
|
|
// as soon as we drop the spinlock. Return PENDING so the
|
|
// caller doesn't touch this IRP.
|
|
//
|
|
InitializeListHead(&Irp->Tail.Overlay.ListEntry);
|
|
IoMarkIrpPending(Irp);
|
|
status = STATUS_PENDING;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
InsertTailList(&devext->PendingIrpList,
|
|
&Irp->Tail.Overlay.ListEntry);
|
|
IoMarkIrpPending(Irp);
|
|
status = STATUS_PENDING;
|
|
}
|
|
|
|
KeReleaseSpinLock(&devext->SpinLock, OldIrql);
|
|
}
|
|
|
|
EXIT(2, ("=%x\n", status));
|
|
return status;
|
|
} //ReadReport
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc EXTERNAL
|
|
*
|
|
* @func VOID | ReadReportCanceled |
|
|
* ReadReport IRP has been canceled, so do the clean up.
|
|
*
|
|
* @parm IN PDEVICE_OBJECT | DevObj | Points to the device object.
|
|
* @parm IN PIRP | Irp | Points to an I/O Request Packet.
|
|
*
|
|
* @rvalue None.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
VOID EXTERNAL
|
|
ReadReportCanceled(
|
|
IN PDEVICE_OBJECT DevObj,
|
|
IN PIRP Irp
|
|
)
|
|
{
|
|
PROCNAME("ReadReportCanceled")
|
|
PDEVICE_EXTENSION devext;
|
|
KIRQL OldIrql;
|
|
|
|
ENTER(2, ("(DevObj=%p,Irp=%p)\n", DevObj, Irp));
|
|
|
|
ASSERT(Irp->Cancel);
|
|
ASSERT(Irp->CancelRoutine == NULL);
|
|
devext = GET_MINIDRIVER_DEVICE_EXTENSION(DevObj);
|
|
KeAcquireSpinLock(&devext->SpinLock, &OldIrql);
|
|
RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
|
|
KeReleaseSpinLock(&devext->SpinLock, OldIrql);
|
|
IoReleaseCancelSpinLock(Irp->CancelIrql);
|
|
|
|
Irp->IoStatus.Status = STATUS_CANCELLED;
|
|
IoReleaseRemoveLock(&devext->RemoveLock, Irp);
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
WARNPRINT(("ReadReport IRP was canceled\n"));
|
|
|
|
EXIT(2, ("!\n"));
|
|
return;
|
|
} //ReadReportCanceled
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func NTSTATUS | GetString |
|
|
* Respond to IOCTL_HID_GET_STRING.
|
|
*
|
|
* @parm IN PDRIVER_OBJECT | DevObj | Points to the driver object.
|
|
* @parm IN PIRP | Irp | Points to an I/O Request Packet.
|
|
*
|
|
* @rvalue SUCCESS | returns STATUS_SUCCESS
|
|
* @rvalue FAILURE | returns NT status code
|
|
*
|
|
*****************************************************************************/
|
|
|
|
NTSTATUS INTERNAL
|
|
GetString(
|
|
PDEVICE_OBJECT DevObj,
|
|
PIRP Irp
|
|
)
|
|
{
|
|
PROCNAME("GetString")
|
|
NTSTATUS status;
|
|
PIO_STACK_LOCATION irpsp;
|
|
PWSTR pwstrID;
|
|
ULONG lenID;
|
|
|
|
PAGED_CODE();
|
|
|
|
irpsp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
ENTER(2, ("(DevObj=%p,Irp=%p,IrpSp=%p,StringID=%x)\n",
|
|
DevObj, Irp, irpsp,
|
|
(ULONG_PTR)irpsp->Parameters.DeviceIoControl.Type3InputBuffer));
|
|
|
|
switch ((ULONG_PTR)irpsp->Parameters.DeviceIoControl.Type3InputBuffer &
|
|
0xffff)
|
|
{
|
|
case HID_STRING_ID_IMANUFACTURER:
|
|
pwstrID = gpwstrManufacturerID;
|
|
break;
|
|
|
|
case HID_STRING_ID_IPRODUCT:
|
|
pwstrID = gpwstrProductID;
|
|
break;
|
|
|
|
case HID_STRING_ID_ISERIALNUMBER:
|
|
pwstrID = gpwstrSerialNumber;
|
|
break;
|
|
|
|
default:
|
|
pwstrID = NULL;
|
|
break;
|
|
}
|
|
|
|
lenID = pwstrID? wcslen(pwstrID)*sizeof(WCHAR) + sizeof(UNICODE_NULL): 0;
|
|
if (pwstrID == NULL)
|
|
{
|
|
ERRPRINT(("invalid string ID (ID=%x)\n",
|
|
(ULONG_PTR)irpsp->Parameters.DeviceIoControl.Type3InputBuffer));
|
|
status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
else if (irpsp->Parameters.DeviceIoControl.OutputBufferLength < lenID)
|
|
{
|
|
ERRPRINT(("output buffer too small (bufflen=%d,need=%d)\n",
|
|
irpsp->Parameters.DeviceIoControl.OutputBufferLength, lenID));
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
else
|
|
{
|
|
RtlCopyMemory(Irp->UserBuffer, pwstrID, lenID);
|
|
|
|
Irp->IoStatus.Information = lenID;
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
EXIT(2, ("=%x (string=%S)\n", status, pwstrID? pwstrID: L"Null"));
|
|
return status;
|
|
} //GetString
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func NTSTATUS | GetAttributes |
|
|
* Respond to IOCTL_HID_GET_ATTRIBUTES, by filling
|
|
* the HID_DEVICE_ATTRIBUTES struct.
|
|
*
|
|
* @parm IN PDRIVER_OBJECT | DevObj | Points to the driver object.
|
|
* @parm IN PIRP | Irp | Points to an I/O Request Packet.
|
|
*
|
|
* @rvalue SUCCESS | returns STATUS_SUCCESS
|
|
* @rvalue FAILURE | returns NT status code
|
|
*
|
|
*****************************************************************************/
|
|
|
|
NTSTATUS INTERNAL
|
|
GetAttributes(
|
|
PDEVICE_OBJECT DevObj,
|
|
PIRP Irp
|
|
)
|
|
{
|
|
PROCNAME("GetAttributes")
|
|
NTSTATUS status;
|
|
PIO_STACK_LOCATION irpsp;
|
|
|
|
PAGED_CODE();
|
|
|
|
irpsp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
ENTER(2, ("(DevObj=%p,Irp=%p,IrpSp=%p)\n", DevObj, Irp, irpsp));
|
|
|
|
if (irpsp->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(HID_DEVICE_ATTRIBUTES))
|
|
{
|
|
ERRPRINT(("output buffer too small (bufflen=%d)\n",
|
|
irpsp->Parameters.DeviceIoControl.OutputBufferLength));
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
else
|
|
{
|
|
PDEVICE_EXTENSION devext;
|
|
PHID_DEVICE_ATTRIBUTES DevAttrib;
|
|
|
|
devext = GET_MINIDRIVER_DEVICE_EXTENSION(DevObj);
|
|
DevAttrib = (PHID_DEVICE_ATTRIBUTES)Irp->UserBuffer;
|
|
|
|
DevAttrib->Size = sizeof(HID_DEVICE_ATTRIBUTES);
|
|
DevAttrib->VendorID = OEM_VENDOR_ID;
|
|
DevAttrib->ProductID = OEM_PRODUCT_ID;
|
|
DevAttrib->VersionNumber = OEM_VERSION_NUM;
|
|
|
|
Irp->IoStatus.Information = sizeof(HID_DEVICE_ATTRIBUTES);
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
EXIT(2, ("=%x\n", status));
|
|
return status;
|
|
} //GetAttributes
|