windows-nt/Source/XPSP1/NT/drivers/wdm/input/tabletpc/mutohpen/oempen.c
2020-09-26 16:20:57 +08:00

1512 lines
50 KiB
C

/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
oempen.c
Abstract: Contains OEM specific functions.
Environment:
Kernel mode
Author:
Michael Tsang (MikeTs) 13-Mar-2000
Revision History:
--*/
#include "pch.h"
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, OemAddDevice)
#pragma alloc_text(PAGE, OemInitSerialPort)
#pragma alloc_text(PAGE, OemInitDevice)
#pragma alloc_text(PAGE, OemQueryDeviceInfo)
#pragma alloc_text(PAGE, OemRemoveDevice)
#pragma alloc_text(PAGE, OemWakeupDevice)
#pragma alloc_text(PAGE, OemStandbyDevice)
#pragma alloc_text(PAGE, OemReadMoreBytes)
#pragma alloc_text(PAGE, OemGetFeatures)
#pragma alloc_text(PAGE, OemSetFeatures)
#pragma alloc_text(PAGE, OemSetTabletFeatures)
#pragma alloc_text(PAGE, RegQueryDeviceParam)
#pragma alloc_text(PAGE, RegSetDeviceParam)
#endif //ifdef ALLOC_PRAGMA
UCHAR gReportDescriptor[130] = {
0x05, 0x0d, // USAGE_PAGE (Digitizers)
0x09, 0x02, // USAGE (Pen)
0xa1, 0x01, // COLLECTION (Application)
0x85, 0x01, // REPORT_ID (1)
0x09, 0x20, // USAGE (Stylus)
0xa1, 0x00, // COLLECTION (Physical)
0x09, 0x42, // USAGE (Tip Switch)
0x09, 0x44, // USAGE (Barrel Switch)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x75, 0x01, // REPORT_SIZE (1)
0x95, 0x02, // REPORT_COUNT (2)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x03, // REPORT_COUNT (3)
0x81, 0x03, // INPUT (Cnst,Var,Abs)
0x09, 0x32, // USAGE (In Range)
0x95, 0x01, // REPORT_COUNT (1)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x02, // REPORT_COUNT (2)
0x81, 0x03, // INPUT (Cnst,Var,Abs)
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x30, // USAGE (X)
0x26, 0x98, 0x21, // LOGICAL_MAXIMUM (8600)
0x75, 0x10, // REPORT_SIZE (16)
0x95, 0x01, // REPORT_COUNT (1)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x09, 0x31, // USAGE (Y)
0x26, 0x50, 0x19, // LOGICAL_MAXIMUM (6480)
0x81, 0x02, // INPUT (Data,Var,Abs)
0xc0, // END_COLLECTION
//
// Feature report
//
0x85, 0x02, // REPORT_ID (2)
0x06, 0x00, 0xff, // USAGE_PAGE (Vendor Defined)
0x09, 0x01, // USAGE (Vendor Usage 1)
0x27, 0xff, 0xff, 0xff, 0xff, // LOGICAL_MAXIMUM (0xffffffff)
0x75, 0x20, // REPORT_SIZE (32)
0x95, 0x01, // REPORT_COUNT (1)
0xb1, 0x02, // FEATURE (Data,Var,Abs)
0xc0, // END_COLLECTION
//
// Dummy mouse collection starts here
//
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x02, // USAGE (Mouse)
0xa1, 0x01, // COLLECTION (Application)
0x85, 0x03, // REPORT_ID (3)
0x09, 0x01, // USAGE (Pointer)
0xa1, 0x00, // COLLECTION (Physical)
0x05, 0x09, // USAGE_PAGE (Button)
0x19, 0x01, // USAGE_MINIMUM (Button 1)
0x29, 0x02, // USAGE_MAXIMUM (Button 2)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x75, 0x01, // REPORT_SIZE (1)
0x95, 0x02, // REPORT_COUNT (2)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x06, // REPORT_COUNT (6)
0x81, 0x03, // INPUT (Cnst,Var,Abs)
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x30, // USAGE (X)
0x09, 0x31, // USAGE (Y)
0x15, 0x81, // LOGICAL_MINIMUM (-127)
0x25, 0x7f, // LOGICAL_MAXIMUM (127)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x02, // REPORT_COUNT (2)
0x81, 0x06, // INPUT (Data,Var,Rel)
0xc0, // END_COLLECTION
0xc0 // END_COLLECTION
};
HID_DESCRIPTOR gHidDescriptor =
{
sizeof(HID_DESCRIPTOR), //bLength
HID_HID_DESCRIPTOR_TYPE, //bDescriptorType
HID_REVISION, //bcdHID
0, //bCountry - not localized
1, //bNumDescriptors
{ //DescriptorList[0]
HID_REPORT_DESCRIPTOR_TYPE, //bReportType
sizeof(gReportDescriptor) //wReportLength
}
};
PWSTR gpwstrManufacturerID = L"Mutoh";
PWSTR gpwstrProductID = L"Serial Pen Tablet (3310)";
PWSTR gpwstrSerialNumber = L"0";
OEM_INPUT_REPORT gLastReport = {0};
LARGE_INTEGER gLastReportTime = {0};
USHORT gwDXThreshold = OEM_THRESHOLD_DX;
USHORT gwDYThreshold = OEM_THRESHOLD_DY;
/*****************************************************************************
*
* @doc INTERNAL
*
* @func NTSTATUS | OemAddDevice |
* OEM specific AddDevice code.
*
* @parm IN PDEVICE_EXTENSION | DevExt | Points to the device extension.
*
* @rvalue STATUS_SUCCESS | success
*
*****************************************************************************/
NTSTATUS INTERNAL
OemAddDevice(
IN PDEVICE_EXTENSION DevExt
)
{
PROCNAME("OemAddDevice")
NTSTATUS status;
UCHAR bConversionRate;
PAGED_CODE ();
ENTER(2, ("(DevExt=%p)\n", DevExt));
status = RegQueryDeviceParam(DevExt->pdo,
STR_TABLET_FEATURES,
&DevExt->OemData.dwTabletFeatures,
sizeof(DevExt->OemData.dwTabletFeatures));
if (!NT_SUCCESS(status))
{
//
// Registry doesn't have this parameter, default to maximum conversion
// rate, digital filter on.
//
DevExt->OemData.dwTabletFeatures = 1 |
OEM_FEATURE_DIGITAL_FILTER_ON;
status = STATUS_SUCCESS;
}
bConversionRate = (UCHAR)(DevExt->OemData.dwTabletFeatures &
OEM_FEATURE_RATE_MASK);
if (bConversionRate != 0)
{
//
// At sampling rate of 133.3, the sampling period is 7.5msec.
// We set the threshold period 10 times of the sampling period.
// ThresholdPeriod = 75000*10*ConversionRate (in units of 100nsec)
//
DevExt->OemData.dwThresholdPeriod = 75000*10*bConversionRate;
}
else
{
//
// Conversion rate of 0 means 100 samples per second which
// means sampling interval is 10msec.
// ThresholdPeriod = 100000*10 (in units of 100nsec)
//
DevExt->OemData.dwThresholdPeriod = 1000000;
}
EXIT(2, ("=%x\n", status));
return status;
} //OemAddDevice
/*****************************************************************************
*
* @doc INTERNAL
*
* @func NTSTATUS | OemInitSerialPort |
* Initialize com port for communication.
*
* @parm IN PDEVICE_EXTENSION | DevExt | Points to the device extension.
*
* @rvalue SUCCESS | returns STATUS_SUCCESS
* @rvalue FAILURE | returns NT status code
*
*****************************************************************************/
NTSTATUS INTERNAL
OemInitSerialPort(
IN PDEVICE_EXTENSION DevExt
)
{
PROCNAME("OemInitSerialPort")
NTSTATUS status;
IO_STATUS_BLOCK iosb;
PAGED_CODE();
ENTER(2, ("(DevExt=%p)\n", DevExt));
//
// Set the com port to basic operating mode: reads/writes one byte at
// time, no handshake flow control or timeouts.
//
status = SerialSyncSendIoctl(IOCTL_SERIAL_INTERNAL_BASIC_SETTINGS,
DevExt->SerialDevObj,
NULL,
0,
&DevExt->PrevSerialSettings,
sizeof(DevExt->PrevSerialSettings),
TRUE,
&iosb);
if (!NT_SUCCESS(status))
{
ERRPRINT(("failed to set com to basic settings (status=%x)\n", status));
}
else
{
SERIAL_BAUD_RATE sbr;
sbr.BaudRate = OEM_SERIAL_BAUDRATE;
status = SerialSyncSendIoctl(IOCTL_SERIAL_SET_BAUD_RATE,
DevExt->SerialDevObj,
&sbr,
sizeof(sbr),
NULL,
0,
FALSE,
&iosb);
if (!NT_SUCCESS(status))
{
ERRPRINT(("failed to set com port to 19200 baud (status=%x)\n",
status));
}
else
{
SERIAL_LINE_CONTROL slc;
slc.WordLength = OEM_SERIAL_WORDLEN;
slc.Parity = OEM_SERIAL_PARITY;
slc.StopBits = OEM_SERIAL_STOPBITS;
status = SerialSyncSendIoctl(IOCTL_SERIAL_SET_LINE_CONTROL,
DevExt->SerialDevObj,
&slc,
sizeof(slc),
NULL,
0,
FALSE,
&iosb);
if (!NT_SUCCESS(status))
{
ERRPRINT(("failed to set com line control (status=%x)\n",
status));
}
else
{
//
// Enable FIFO receive trigger at 4 bytes
//
ULONG Data = SERIAL_IOC_FCR_FIFO_ENABLE |
SERIAL_IOC_FCR_RCVR_RESET |
SERIAL_IOC_FCR_XMIT_RESET |
SERIAL_IOC_FCR_RCVR_TRIGGER_04_BYTES;
status = SerialSyncSendIoctl(IOCTL_SERIAL_SET_FIFO_CONTROL,
DevExt->SerialDevObj,
&Data,
sizeof(Data),
NULL,
0,
FALSE,
&iosb);
if (!NT_SUCCESS(status))
{
ERRPRINT(("failed to set FIFO control (status=%x)\n",
status));
}
else
{
Data = SERIAL_PURGE_RXCLEAR;
status = SerialSyncSendIoctl(IOCTL_SERIAL_PURGE,
DevExt->SerialDevObj,
&Data,
sizeof(Data),
NULL,
0,
FALSE,
&iosb);
if (!NT_SUCCESS(status))
{
ERRPRINT(("failed to flush receive buffer (status=%x)\n",
status));
}
}
}
}
}
EXIT(2, ("=%x\n", status));
return status;
} //OemInitSerialPort
/*****************************************************************************
*
* @doc INTERNAL
*
* @func NTSTATUS | OemInitDevice |
* Initialize pen tablet device.
*
* @parm IN PDEVICE_EXTENSION | DevExt | Points to the device extension.
*
* @rvalue SUCCESS | returns STATUS_SUCCESS
* @rvalue FAILURE | returns NT status code
*
*****************************************************************************/
NTSTATUS INTERNAL
OemInitDevice(
IN PDEVICE_EXTENSION DevExt
)
{
PROCNAME("OemInitDevice")
NTSTATUS status;
PAGED_CODE();
ENTER(2, ("(DevExt=%p)\n", DevExt));
status = SerialSyncReadWritePort(FALSE, DevExt, "@", 1, NULL, NULL);
if (!NT_SUCCESS(status))
{
ERRPRINT(("failed to send reset command to tablet (status=%x)\n",
status));
}
else
{
LONGLONG WaitTime = Int32x32To64(100, -10000);
//
// We need to delay 20msec after a software reset is sent.
//
KeDelayExecutionThread(KernelMode,
FALSE,
(LARGE_INTEGER *)&WaitTime);
status = OemSetTabletFeatures(DevExt, DevExt->OemData.dwTabletFeatures);
if (!NT_SUCCESS(status))
{
ERRPRINT(("failed to set default tablet features (status=%x,features=%x)\n",
status, DevExt->OemData.dwTabletFeatures));
}
else if (!NT_SUCCESS(status = SerialSyncReadWritePort(
FALSE, DevExt, "LO", 2, NULL, NULL)))
{
ERRPRINT(("failed to set default tablet configuration (status=%x)\n",
status));
}
else
{
status = OemQueryDeviceInfo(DevExt);
if (!NT_SUCCESS(status))
{
//
// It's not a big deal if we don't get the device info.
// It's more important to keep the driver running.
//
status = STATUS_SUCCESS;
}
}
}
EXIT(2, ("=%x\n", status));
return status;
} //OemInitDevice
/*****************************************************************************
*
* @doc INTERNAL
*
* @func NTSTATUS | OemQueryDeviceInfo |
* Query pen tablet device information.
*
* @parm IN PDEVICE_EXTENSION | DevExt | Points to the device extension.
*
* @rvalue SUCCESS | returns STATUS_SUCCESS
* @rvalue FAILURE | returns NT status code
*
*****************************************************************************/
NTSTATUS INTERNAL
OemQueryDeviceInfo(
IN PDEVICE_EXTENSION DevExt
)
{
PROCNAME("OemQueryDeviceInfo")
NTSTATUS status, status2;
PAGED_CODE();
ENTER(2, ("(DevExt=%p)\n", DevExt));
status = SerialSyncReadWritePort(FALSE, DevExt, "K", 1, NULL, NULL);
if (NT_SUCCESS(status))
{
LARGE_INTEGER Timeout;
OEM_INPUT_REPORT InData[3];
ULONG BytesRead;
// Set timeout to 100 msec.
Timeout.QuadPart = Int32x32To64(100, -10000);
while ((status = SerialSyncReadWritePort(TRUE,
DevExt,
(PUCHAR)InData,
1,
&Timeout,
&BytesRead)) ==
STATUS_SUCCESS)
{
if (InData[0].InputReport.bStatus == 0x88)
{
break;
}
}
if (NT_SUCCESS(status))
{
status = SerialSyncReadWritePort(TRUE,
DevExt,
((PUCHAR)InData) + 1,
sizeof(InData) - 1,
&Timeout,
&BytesRead);
if (NT_SUCCESS(status))
{
if ((BytesRead == sizeof(InData) - 1) &&
(InData[0].InputReport.bStatus == 0x88) &&
(InData[1].InputReport.bStatus == 0x88) &&
(InData[2].InputReport.bStatus == 0x8f))
{
DevExt->OemData.wFirmwareDate =
NORMALIZE_DATA(InData[0].InputReport.wXData);
DevExt->OemData.wFirmwareYear =
NORMALIZE_DATA(InData[0].InputReport.wYData);
DevExt->OemData.wProductID =
NORMALIZE_DATA(InData[1].InputReport.wXData);
DevExt->OemData.wFirmwareRev =
NORMALIZE_DATA(InData[1].InputReport.wYData);
DevExt->OemData.wCorrectionRev =
NORMALIZE_DATA(InData[2].InputReport.wXData);
DBGPRINT(1, ("FirmwareDate=%d,FirmwareYear=%d,ProductID=%d,FirmwareRev=%d,CorrectionRev=%d\n",
DevExt->OemData.wFirmwareDate,
DevExt->OemData.wFirmwareYear,
DevExt->OemData.wProductID,
DevExt->OemData.wFirmwareRev,
DevExt->OemData.wCorrectionRev));
}
else
{
ERRPRINT(("invalid response of status command (size=%d,InData=%p)\n",
BytesRead, InData));
status = STATUS_DEVICE_DATA_ERROR;
}
}
else
{
ERRPRINT(("failed to read response packet (status=%x)\n",
status));
}
}
else
{
ERRPRINT(("failed to read first byte of the response (status=%x)\n",
status));
}
}
else
{
ERRPRINT(("failed to send status command (status=%x)\n", status));
}
status2 = SerialSyncReadWritePort(FALSE,
DevExt,
"A",
1,
NULL,
NULL);
if (!NT_SUCCESS(status2))
{
ERRPRINT(("failed to send acknowledge command to tablet (status=%x)\n",
status2));
}
EXIT(2, ("=%x\n", status));
return status;
} //OemQueryDeviceInfo
/*****************************************************************************
*
* @doc INTERNAL
*
* @func NTSTATUS | OemRemoveDevice |
* OEM specific cleanups.
*
* @parm IN PDEVICE_EXTENSION | DevExt | Points to the device extension.
*
* @rvalue SUCCESS | returns STATUS_SUCCESS
* @rvalue FAILURE | returns NT status code
*
*****************************************************************************/
NTSTATUS INTERNAL
OemRemoveDevice(
IN PDEVICE_EXTENSION DevExt
)
{
PROCNAME("OemRemoveDevice")
NTSTATUS status;
IO_STATUS_BLOCK iosb;
PAGED_CODE();
ENTER(2, ("(DevExt=%p)\n", DevExt));
status = SerialSyncSendIoctl(IOCTL_SERIAL_INTERNAL_RESTORE_SETTINGS,
DevExt->SerialDevObj,
&DevExt->PrevSerialSettings,
sizeof(DevExt->PrevSerialSettings),
NULL,
0,
TRUE,
&iosb);
if (!NT_SUCCESS(status))
{
ERRPRINT(("failed to restore serial port settings (status=%x)\n",
status));
}
EXIT(2, ("=%x\n", status));
return status;
} //OemRemoveDevice
/*****************************************************************************
*
* @doc INTERNAL
*
* @func NTSTATUS | OemWakeupDevice |
* OEM specific wake up code.
*
* @parm IN PDEVICE_EXTENSION | DevExt | Points to the device extension.
*
* @rvalue SUCCESS | returns STATUS_SUCCESS
* @rvalue FAILURE | returns NT status code
*
*****************************************************************************/
NTSTATUS INTERNAL
OemWakeupDevice(
IN PDEVICE_EXTENSION DevExt
)
{
PROCNAME("OemWakeupDevice")
NTSTATUS status = STATUS_SUCCESS;
PAGED_CODE();
ENTER(2, ("(DevExt=%p)\n", DevExt));
if (DevExt->dwfHPen & HPENF_DEVICE_STARTED)
{
status = SerialSyncReadWritePort(FALSE, DevExt, "A", 1, NULL, NULL);
if (!NT_SUCCESS(status))
{
ERRPRINT(("failed to send acknowledge command to the tablet (status=%x)\n",
status));
}
}
EXIT(2, ("=%x\n", status));
return status;
} //OemWakeupDevice
/*****************************************************************************
*
* @doc INTERNAL
*
* @func NTSTATUS | OemStandbyDevice |
* OEM specific wake up code.
*
* @parm IN PDEVICE_EXTENSION | DevExt | Points to the device extension.
*
* @rvalue SUCCESS | returns STATUS_SUCCESS
* @rvalue FAILURE | returns NT status code
*
*****************************************************************************/
NTSTATUS INTERNAL
OemStandbyDevice(
IN PDEVICE_EXTENSION DevExt
)
{
PROCNAME("OemStandbyDevice")
NTSTATUS status = STATUS_SUCCESS;
PAGED_CODE();
ENTER(2, ("(DevExt=%p)\n", DevExt));
if (DevExt->dwfHPen & HPENF_DEVICE_STARTED)
{
status = SerialSyncReadWritePort(FALSE, DevExt, "W", 1, NULL, NULL);
if (!NT_SUCCESS(status))
{
ERRPRINT(("failed to send standby command to the tablet (status=%x)\n",
status));
}
}
EXIT(2, ("=%x\n", status));
return status;
} //OemStandbyDevice
/*****************************************************************************
*
* @doc INTERNAL
*
* @func NTSTATUS | OemProcessResyncBuffer |
* Process input data from the resync buffer.
* Note that this function must be called at IRQL==DISPATCH_LEVEL
*
* @parm IN PDEVICE_EXTENSION | DevExt | Points to the device extension.
* @parm IN PIRP | Irp | Points to an I/O request packet.
*
* @rvalue SUCCESS | Returns STATUS_SUCCESS.
* @rvalue FAILURE | Returns STATUS_MORE_PROCESSING_REQUIRED
* (We want the IRP back).
*
*****************************************************************************/
NTSTATUS INTERNAL
OemProcessResyncBuffer(
IN PDEVICE_EXTENSION DevExt,
IN PIRP Irp
)
{
PROCNAME("OemProcessResyncBuffer")
NTSTATUS status = STATUS_DATA_ERROR;
PHID_INPUT_REPORT HidReport = (PHID_INPUT_REPORT)Irp->UserBuffer;
ENTER(2, ("(DevExt=%p,Irp=%p,Len=%d,status=%x,xData=%x,yData=%x)\n",
DevExt, Irp, DevExt->BytesInBuff,
DevExt->ResyncData[0].InputReport.bStatus,
DevExt->ResyncData[0].InputReport.wXData,
DevExt->ResyncData[0].InputReport.wYData));
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
while (DevExt->BytesInBuff >= sizeof(OEM_INPUT_REPORT))
{
if (OemIsResyncDataValid(DevExt))
{
status = OemNormalizeInputData(DevExt, &DevExt->ResyncData[0]);
if (NT_SUCCESS(status))
{
HidReport->ReportID = OEM_PEN_REPORT_ID;
RtlCopyMemory(HidReport->Report.RawInput,
&DevExt->ResyncData[0],
sizeof(OEM_INPUT_REPORT));
Irp->IoStatus.Information = sizeof(HID_INPUT_REPORT);
}
DevExt->BytesInBuff -= sizeof(OEM_INPUT_REPORT);
if (DevExt->BytesInBuff > 0)
{
RtlMoveMemory(&DevExt->ResyncData[0],
&DevExt->ResyncData[1],
DevExt->BytesInBuff);
}
if (NT_SUCCESS(status))
{
break;
}
}
}
EXIT(2, ("=%x (status=%x,xData=%x,yData=%x)\n",
status,
HidReport->Report.InputReport.bStatus,
HidReport->Report.InputReport.wXData,
HidReport->Report.InputReport.wYData));
return status;
} //OemProcessResyncBuffer
/*****************************************************************************
*
* @doc INTERNAL
*
* @func NTSTATUS | OemProcessInputData |
* OEM specific code to process input data.
*
* @parm IN PDEVICE_EXTENSION | DevExt | Points to the device extension.
* @parm IN PIRP | Irp | Points to an I/O request packet.
* @parm IN PHID_INPUT_REPORT | HidReport | Points to hid report packet.
*
* @rvalue SUCCESS | Returns STATUS_SUCCESS.
* @rvalue FAILURE | Returns STATUS_MORE_PROCESSING_REQUIRED
* (We want the IRP back).
*
*****************************************************************************/
NTSTATUS INTERNAL
OemProcessInputData(
IN PDEVICE_EXTENSION DevExt,
IN PIRP Irp,
IN PHID_INPUT_REPORT HidReport
)
{
PROCNAME("OemProcessInputData")
NTSTATUS status;
KIRQL OldIrql;
ENTER(2, ("(DevExt=%p,Irp=%p,HidReport=%p,Len=%d,status=%x,xData=%x,yData=%x)\n",
DevExt, Irp, HidReport, Irp->IoStatus.Information,
HidReport->Report.InputReport.bStatus,
HidReport->Report.InputReport.wXData,
HidReport->Report.InputReport.wYData));
KeAcquireSpinLock(&DevExt->SpinLock, &OldIrql);
if ((DevExt->BytesInBuff == 0) &&
(Irp->IoStatus.Information == sizeof(OEM_INPUT_REPORT)) &&
OemIsValidPacket(&HidReport->Report))
{
status = OemNormalizeInputData(DevExt, &HidReport->Report);
if (NT_SUCCESS(status))
{
HidReport->ReportID = OEM_PEN_REPORT_ID;
Irp->IoStatus.Information = sizeof(HID_INPUT_REPORT);
}
}
else
{
//
// Either resync buffer already has something in it or packet is
// partial or invalid, so append data to resync buffer and process
// it again.
//
RtlMoveMemory((PUCHAR)&DevExt->ResyncData[0] + DevExt->BytesInBuff,
&HidReport->Report,
Irp->IoStatus.Information);
DevExt->BytesInBuff += (ULONG)Irp->IoStatus.Information;
ASSERT(DevExt->BytesInBuff <= sizeof(DevExt->ResyncData));
status = OemProcessResyncBuffer(DevExt, Irp);
}
if (!NT_SUCCESS(status))
{
PREAD_WORKITEM ReadWorkItem;
//
// No valid data packet, send another IRP down to read more.
//
if (!(DevExt->QueuedWorkItems & QUEUED_WORKITEM_0))
{
ReadWorkItem = &DevExt->ReadWorkItem[0];
ReadWorkItem->WorkItemBit = QUEUED_WORKITEM_0;
}
else
{
ASSERT(!(DevExt->QueuedWorkItems & QUEUED_WORKITEM_1));
DBGPRINT(3, ("Queue second work item!\n"));
ReadWorkItem = &DevExt->ReadWorkItem[1];
ReadWorkItem->WorkItemBit = QUEUED_WORKITEM_1;
}
status = IoAcquireRemoveLock(&DevExt->RemoveLock, Irp);
if (!NT_SUCCESS(status))
{
ERRPRINT(("trying to queue a work item after device was removed\n"));
}
else
{
DevExt->QueuedWorkItems |= ReadWorkItem->WorkItemBit;
ReadWorkItem->Irp = Irp;
ReadWorkItem->HidReport = HidReport;
IoQueueWorkItem(ReadWorkItem->WorkItem,
OemReadMoreBytes,
DelayedWorkQueue,
ReadWorkItem);
status = STATUS_MORE_PROCESSING_REQUIRED;
}
}
KeReleaseSpinLock(&DevExt->SpinLock, OldIrql);
EXIT(2, ("=%x (status=%x,xData=%x,yData=%x)\n",
status,
HidReport->Report.InputReport.bStatus,
HidReport->Report.InputReport.wXData,
HidReport->Report.InputReport.wYData));
return status;
} //OemProcessInputData
/*****************************************************************************
*
* @doc INTERNAL
*
* @func NTSTATUS | OemReadMoreBytes |
* Read more bytes to resync packet.
*
* @parm IN PDEVICE_OBJECT | DevObj | Points to the device object.
* @parm IN PREAD_WORKITEM | ReadWorkItem | Points to the read work item.
* @parm IN PIRP | Irp | Points to an I/O request packet.
*
* @rvalue None.
*
*****************************************************************************/
VOID INTERNAL
OemReadMoreBytes(
IN PDEVICE_OBJECT DevObj,
IN PREAD_WORKITEM ReadWorkItem
)
{
PROCNAME("OemReadMoreBytes")
PDEVICE_EXTENSION devext;
ULONG BytesToRead;
KIRQL OldIrql;
PAGED_CODE();
ENTER(2, ("(DevObj=%p,ReadWorkItem=%p)\n", DevObj, ReadWorkItem));
devext = GET_MINIDRIVER_DEVICE_EXTENSION(DevObj);
KeAcquireSpinLock(&devext->SpinLock, &OldIrql);
ASSERT(devext->QueuedWorkItems & ReadWorkItem->WorkItemBit);
devext->QueuedWorkItems &= ~ReadWorkItem->WorkItemBit;
IoReleaseRemoveLock(&devext->RemoveLock, ReadWorkItem->Irp);
BytesToRead = sizeof(OEM_INPUT_REPORT) -
devext->BytesInBuff%sizeof(OEM_INPUT_REPORT);
KeReleaseSpinLock(&devext->SpinLock, OldIrql);
DBGPRINT(3, ("Read %d more bytes (WorkItemBit=%x)\n",
BytesToRead, ReadWorkItem->WorkItemBit));
SerialAsyncReadWritePort(TRUE,
devext,
ReadWorkItem->Irp,
ReadWorkItem->HidReport->Report.RawInput,
BytesToRead,
ReadReportCompletion,
ReadWorkItem->HidReport);
ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
EXIT(2, ("!\n"));
return;
} //OemReadMoreBytes
/*****************************************************************************
*
* @doc INTERNAL
*
* @func BOOLEAN | OemIsResyncDataValid |
* Check if the data in the resync buffer is valid.
*
* @parm IN PDEVICE_EXTENSION | DevExt | Points to the device extension.
*
* @rvalue SUCCESS | Returns TRUE.
* @rvalue FAILURE | Returns FALSE.
*
*****************************************************************************/
BOOLEAN INTERNAL
OemIsResyncDataValid(
IN PDEVICE_EXTENSION DevExt
)
{
PROCNAME("OemIsResyncDataValid")
BOOLEAN rc;
ENTER(2, ("(DevExt=%p)\n", DevExt));
rc = OemIsValidPacket(&DevExt->ResyncData[0]);
if ((rc == FALSE) || (DevExt->BytesInBuff > sizeof(OEM_INPUT_REPORT)))
{
PUCHAR pb = (PUCHAR)&DevExt->ResyncData[0] + DevExt->BytesInBuff - 1;
PUCHAR pbEnd = rc? (PUCHAR)&DevExt->ResyncData[1]:
(PUCHAR)&DevExt->ResyncData[0];
//
// Even if we seem to have a valid packet in the resync buffer, we
// still need to scan the next packet if any. If the next packet
// has a sync bit out of place, the first packet could still be
// invalid and we better throw it away.
//
while (pb > pbEnd)
{
if (*pb & INSTATUS_SYNC)
{
DBGPRINT(3,
("invalid buffer (len=%d,status0=%x,xData0=%x,yData0=%x,status1=%x,xData1=%x,yData1=%x)\n",
DevExt->BytesInBuff,
DevExt->ResyncData[0].InputReport.bStatus,
DevExt->ResyncData[0].InputReport.wXData,
DevExt->ResyncData[0].InputReport.wYData,
DevExt->ResyncData[1].InputReport.bStatus,
DevExt->ResyncData[1].InputReport.wXData,
DevExt->ResyncData[1].InputReport.wYData));
DevExt->BytesInBuff = (ULONG)((PUCHAR)&DevExt->ResyncData[0] +
DevExt->BytesInBuff - pb);
RtlMoveMemory(&DevExt->ResyncData[0], pb, DevExt->BytesInBuff);
DBGPRINT(3,
("Resync'd buffer (len=%d,status=%x,xData=%x,yData=%x)\n",
DevExt->BytesInBuff,
DevExt->ResyncData[0].InputReport.bStatus,
DevExt->ResyncData[0].InputReport.wXData,
DevExt->ResyncData[0].InputReport.wYData));
#ifdef DEBUG
{
ULONG dwcDeletedBytes = (ULONG)(pb -
(PUCHAR)&DevExt->ResyncData[0]);
gdwcLostBytes +=
(dwcDeletedBytes > sizeof(OEM_INPUT_REPORT))?
sizeof(OEM_INPUT_REPORT)*2 - dwcDeletedBytes:
sizeof(OEM_INPUT_REPORT) - dwcDeletedBytes;
}
#endif
rc = FALSE;
break;
}
--pb;
}
if ((rc == FALSE) && (pb <= pbEnd))
{
//
// We didn't have a valid packet and we couldn't find the sync
// bit of the next packet, so the whole resync buffer is invalid.
//
DevExt->BytesInBuff = 0;
}
}
EXIT(2, ("=%x\n", rc));
return rc;
} //OemIsResyncDataValid
/*****************************************************************************
*
* @doc INTERNAL
*
* @func NTSTATUS | OemNormalizeInputData |
* Normalize the input data.
*
* @parm IN PDEVICE_EXTENSION | DevExt | Points to the device extension.
* @parm IN OUT POEM_INPUT_REPORT | InData | Points to the input data packet.
*
* @rvalue SUCCESS | Returns STATUS_SUCCESS.
* @rvalue FAILURE | Returns STATUS_DATA_ERROR.
*
*****************************************************************************/
NTSTATUS INTERNAL
OemNormalizeInputData(
IN PDEVICE_EXTENSION DevExt,
IN OUT POEM_INPUT_REPORT InData
)
{
PROCNAME("OemNormalizeInputData")
NTSTATUS status = STATUS_SUCCESS;
LARGE_INTEGER CurrentTime;
ENTER(2, ("(DevExt=%p,InData=%p,Status=%x,XData=%x,YData=%x)\n",
DevExt, InData, InData->InputReport.bStatus,
InData->InputReport.wXData, InData->InputReport.wYData));
InData->InputReport.wXData = NORMALIZE_DATA(InData->InputReport.wXData);
InData->InputReport.wYData = NORMALIZE_DATA(InData->InputReport.wYData);
if (InData->InputReport.wXData >= OEM_PEN_MAX_X)
{
#ifdef DEBUG
if (InData->InputReport.wXData > gwMaxX)
{
gwMaxX = InData->InputReport.wXData;
}
#endif
InData->InputReport.wXData = OEM_PEN_MAX_X - 1;
}
if (InData->InputReport.wYData >= OEM_PEN_MAX_Y)
{
#ifdef DEBUG
if (InData->InputReport.wYData > gwMaxY)
{
gwMaxY = InData->InputReport.wYData;
}
#endif
InData->InputReport.wYData = 0;
}
else
{
InData->InputReport.wYData = OEM_PEN_MAX_Y - 1 -
InData->InputReport.wYData;
}
KeQuerySystemTime(&CurrentTime);
#ifdef DEBUG
if ((gLastReport.InputReport.bStatus ^ InData->InputReport.bStatus) &
INSTATUS_PEN_TIP_DOWN)
{
//
// The tip switch changes state
//
if (InData->InputReport.bStatus & INSTATUS_PEN_TIP_DOWN)
{
gdwcSamples = 0;
gdwcLostBytes = 0;
gStartTime = CurrentTime;
}
else
{
CurrentTime.QuadPart -= gStartTime.QuadPart;
CurrentTime.QuadPart /= (LONGLONG)10000;
DBGPRINT(1, ("Samples=%d,Elapsed=%d,Rate=%d,BytesLost=%d\n",
gdwcSamples,
CurrentTime.LowPart,
CurrentTime.LowPart?
gdwcSamples*1000/CurrentTime.LowPart: 0,
gdwcLostBytes));
}
}
gdwcSamples++;
#endif
if (DevExt->OemData.dwTabletFeatures & OEM_FEATURE_GLITCH_FILTER_ON)
{
LARGE_INTEGER ElapsedTime;
ElapsedTime.QuadPart = CurrentTime.QuadPart - gLastReportTime.QuadPart;
if (ElapsedTime.QuadPart < (LONGLONG)DevExt->OemData.dwThresholdPeriod)
{
USHORT wDX, wDY;
wDX = (USHORT)(abs(InData->InputReport.wXData -
gLastReport.InputReport.wXData));
wDY = (USHORT)(abs(InData->InputReport.wYData -
gLastReport.InputReport.wYData));
if ((wDX > gwDXThreshold) || (wDY > gwDYThreshold))
{
//
// Spike detected, drop this packet.
//
WARNPRINT(("dX or dY exceeding threshold (dX=%d,dY=%d)\n",
wDX, wDY));
status = STATUS_DATA_ERROR;
}
#ifdef DEBUG
else
{
if (wDX > gwMaxDX)
{
gwMaxDX = wDX;
}
if (wDY > gwMaxDY)
{
gwMaxDY = wDY;
}
}
#endif
}
}
gLastReportTime = CurrentTime;
RtlCopyMemory(&gLastReport, InData, sizeof(gLastReport));
if (NT_SUCCESS(status))
{
//
// We have a valid report, tell the system that we are not idling.
//
PoSetSystemState(ES_USER_PRESENT);
}
DBGPRINT(3, ("status=%x,xData=%x(%d),yData=%x(%d)\n",
InData->InputReport.bStatus,
InData->InputReport.wXData,
InData->InputReport.wXData,
InData->InputReport.wYData,
InData->InputReport.wYData));
EXIT(2, ("=%x (Status=%x,XData=%x(%d),YData=%x(%d)\n",
status,
InData->InputReport.bStatus,
InData->InputReport.wXData,
InData->InputReport.wXData,
InData->InputReport.wYData,
InData->InputReport.wYData));
return status;
} //OemNormalizeInputData
/*****************************************************************************
*
* @doc INTERNAL
*
* @func NTSTATUS | OemGetFeatures |
* Get feature 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
OemGetFeatures(
IN PDEVICE_OBJECT DevObj,
IN PIRP Irp
)
{
PROCNAME("OemGetFeatures")
NTSTATUS status;
PIO_STACK_LOCATION irpsp;
PDEVICE_EXTENSION devext;
PHID_XFER_PACKET FeaturePacket = (PHID_XFER_PACKET)Irp->UserBuffer;
PAGED_CODE();
irpsp = IoGetCurrentIrpStackLocation(Irp);
ENTER(2, ("(DevObj=%p,Irp=%p,IrpSp=%p)\n", DevObj, Irp, irpsp));
ASSERT(FeaturePacket != NULL);
devext = GET_MINIDRIVER_DEVICE_EXTENSION(DevObj);
if (irpsp->Parameters.DeviceIoControl.OutputBufferLength !=
sizeof(HID_XFER_PACKET))
{
ERRPRINT(("invalid xfer packet size (bufflen=%d)\n",
irpsp->Parameters.DeviceIoControl.OutputBufferLength));
status = STATUS_INVALID_BUFFER_SIZE;
}
else if (FeaturePacket->reportBufferLen != sizeof(HID_FEATURE_REPORT))
{
ERRPRINT(("invalid feature report size (bufflen=%d)\n",
FeaturePacket->reportBufferLen));
status = STATUS_INVALID_BUFFER_SIZE;
}
else if (!(devext->dwfHPen & HPENF_DEVICE_STARTED))
{
ERRPRINT(("device not started yet\n"));
status = STATUS_DEVICE_NOT_READY ;
}
else
{
PHID_FEATURE_REPORT Feature =
(PHID_FEATURE_REPORT)FeaturePacket->reportBuffer;
ASSERT(FeaturePacket->reportId == OEM_FEATURE_REPORT_ID);
Feature->ReportID = OEM_FEATURE_REPORT_ID;
Feature->Report.dwTabletFeatures = devext->OemData.dwTabletFeatures;
Irp->IoStatus.Information = sizeof(HID_FEATURE_REPORT);
status = STATUS_SUCCESS;
}
EXIT(2, ("=%x\n", status));
return status;
} //OemGetFeatures
/*****************************************************************************
*
* @doc INTERNAL
*
* @func NTSTATUS | OemSetFeatures |
* Set feature 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
OemSetFeatures(
IN PDEVICE_OBJECT DevObj,
IN PIRP Irp
)
{
PROCNAME("OemSetFeatures")
NTSTATUS status;
PIO_STACK_LOCATION irpsp;
PDEVICE_EXTENSION devext;
PHID_XFER_PACKET FeaturePacket = (PHID_XFER_PACKET)Irp->UserBuffer;
PHID_FEATURE_REPORT Feature;
PAGED_CODE();
irpsp = IoGetCurrentIrpStackLocation(Irp);
ENTER(2, ("(DevObj=%p,Irp=%p,IrpSp=%p)\n", DevObj, Irp, irpsp));
ASSERT(FeaturePacket != NULL);
Feature = (PHID_FEATURE_REPORT)FeaturePacket->reportBuffer;
devext = GET_MINIDRIVER_DEVICE_EXTENSION(DevObj);
if (irpsp->Parameters.DeviceIoControl.InputBufferLength !=
sizeof(HID_XFER_PACKET))
{
ERRPRINT(("invalid xfer packet size (bufflen=%d)\n",
irpsp->Parameters.DeviceIoControl.InputBufferLength));
status = STATUS_INVALID_BUFFER_SIZE;
}
else if (FeaturePacket->reportBufferLen != sizeof(HID_FEATURE_REPORT))
{
ERRPRINT(("invalid feature report size (bufflen=%d)\n",
FeaturePacket->reportBufferLen));
status = STATUS_INVALID_BUFFER_SIZE;
}
else if (!(devext->dwfHPen & HPENF_DEVICE_STARTED))
{
ERRPRINT(("device not started yet\n"));
status = STATUS_DEVICE_NOT_READY ;
}
else if ((Feature->Report.dwTabletFeatures & OEM_FEATURE_UNUSED_BITS) ||
((Feature->Report.dwTabletFeatures & OEM_FEATURE_RATE_MASK) >
OEM_MAX_RATE_DIVISOR))
{
ERRPRINT(("invalid tablet features (features=%x)\n",
Feature->Report.dwTabletFeatures));
status = STATUS_INVALID_DEVICE_REQUEST;
}
else
{
ASSERT(FeaturePacket->reportId == OEM_FEATURE_REPORT_ID);
devext->OemData.dwTabletFeatures = Feature->Report.dwTabletFeatures;
status = OemSetTabletFeatures(devext, Feature->Report.dwTabletFeatures);
}
EXIT(2, ("=%x\n", status));
return status;
} //OemSetFeatures
/*****************************************************************************
*
* @doc INTERNAL
*
* @func NTSTATUS | OemSetTabletFeatures |
* Set tablet feature.
*
* @parm IN PDEVICE_EXTENSION | DevExt | Points to the device extension.
* @parm IN ULONG | dwTabletFeatures | Specifies the tablet features.
*
* @rvalue SUCCESS | returns STATUS_SUCCESS
* @rvalue FAILURE | returns NT status code
*
*****************************************************************************/
NTSTATUS INTERNAL
OemSetTabletFeatures(
IN PDEVICE_EXTENSION DevExt,
IN ULONG dwTabletFeatures
)
{
PROCNAME("OemSetTabletFeatures")
NTSTATUS status;
char szTabletCmd[3] = "??";
UCHAR bConversionRate = (UCHAR)(dwTabletFeatures & OEM_FEATURE_RATE_MASK);
PAGED_CODE();
ENTER(2, ("(DevExt=%p,Features=%x)\n", DevExt, dwTabletFeatures));
status = RegSetDeviceParam(DevExt->pdo,
STR_TABLET_FEATURES,
REG_DWORD,
&dwTabletFeatures,
sizeof(dwTabletFeatures));
if (NT_SUCCESS(status))
{
szTabletCmd[0] = (dwTabletFeatures & OEM_FEATURE_DIGITAL_FILTER_ON)?
'V': 'N';
szTabletCmd[1] = (char)('0' + bConversionRate);
if (bConversionRate != 0)
{
//
// At sampling rate of 133.3, the sampling period is 7.5msec.
// We set the threshold period 10 times of the sampling period.
// ThresholdPeriod = 75000*ConversionRate*10 (in units of 100nsec)
//
DevExt->OemData.dwThresholdPeriod = bConversionRate*750000;
}
else
{
//
// Conversion rate of 0 means 100 samples per second which
// means sampling interval is 10msec.
// ThresholdPeriod = 100000*10 (in units of 100nsec)
//
DevExt->OemData.dwThresholdPeriod = 1000000;
}
status = SerialSyncReadWritePort(FALSE,
DevExt,
szTabletCmd,
sizeof(szTabletCmd) - 1,
NULL,
NULL);
}
else
{
ERRPRINT(("failed to set tablet features in the registry (status=%x)\n",
status));
}
EXIT(2, ("=%x\n", status));
return status;
} //OemSetTabletFeatures
/*****************************************************************************
*
* @doc INTERNAL
*
* @func NTSTATUS | RegQueryDeviceParam | Query the registry for a device
* parameter.
*
* @parm IN PDEVICE_OBJECT | pdo | Points to the pdo of the device.
* @parm IN PWSTR | pwstrParamName | Points to the param name string.
* @parm OUT PVOID | pbBuff | Points to the buffer to hold the result.
* @parm IN ULONG | dwcbLen | Specifies the length of the buffer.
*
* @rvalue SUCCESS | returns STATUS_SUCCESS
* @rvalue FAILURE | returns NT status code
*
*****************************************************************************/
NTSTATUS INTERNAL
RegQueryDeviceParam(
IN PDEVICE_OBJECT pdo,
IN PWSTR pwstrParamName,
OUT PVOID pbBuff,
IN ULONG dwcbLen
)
{
PROCNAME("RegQueryDeviceParam")
NTSTATUS status;
ULONG dwSize;
PKEY_VALUE_PARTIAL_INFORMATION pValueInfo;
PAGED_CODE();
ENTER(2, ("(pdo=%p,ParamName=%S,pbBuff=%p,Len=%d)\n",
pdo, pwstrParamName, pbBuff, dwcbLen));
dwSize = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data) + dwcbLen;
pValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)ExAllocatePool(PagedPool,
dwSize);
if (pValueInfo != NULL)
{
HANDLE hkey;
status = IoOpenDeviceRegistryKey(pdo,
PLUGPLAY_REGKEY_DEVICE,
STANDARD_RIGHTS_READ,
&hkey);
if (NT_SUCCESS(status))
{
UNICODE_STRING ucKeyName;
RtlInitUnicodeString(&ucKeyName, pwstrParamName);
status = ZwQueryValueKey(hkey,
&ucKeyName,
KeyValuePartialInformation,
pValueInfo,
dwSize,
&dwSize);
if (NT_SUCCESS(status))
{
ASSERT(pValueInfo->DataLength == dwcbLen);
RtlCopyMemory(pbBuff, pValueInfo->Data, dwcbLen);
}
else
{
WARNPRINT(("failed to read parameter %S (status=%x)\n",
pwstrParamName, status));
}
ZwClose(hkey);
}
else
{
ERRPRINT(("failed to open device registry key (status=%x)\n",
status));
}
ExFreePool(pValueInfo);
}
else
{
status = STATUS_INSUFFICIENT_RESOURCES;
ERRPRINT(("failed to allocate registry value buffer (size=%d)\n",
dwSize));
}
EXIT(2, ("=%x\n", status));
return status;
} //RegQueryDeviceParam
/*****************************************************************************
*
* @doc INTERNAL
*
* @func NTSTATUS | RegSetDeviceParam | Set the device parameter in the
* registry.
*
* @parm IN PDEVICE_OBJECT | pdo | Points to the pdo of the device.
* @parm IN PWSTR | pwstrParamName | Points to the param name string.
* @parm IN ULONG | dwType | Value type.
* @parm IN PVOID | pbBuff | Points to the buffer to hold the result.
* @parm IN ULONG | dwcbLen | Specifies the length of the buffer.
*
* @rvalue SUCCESS | returns STATUS_SUCCESS
* @rvalue FAILURE | returns NT status code
*
*****************************************************************************/
NTSTATUS INTERNAL
RegSetDeviceParam(
IN PDEVICE_OBJECT pdo,
IN PWSTR pwstrParamName,
IN ULONG dwType,
IN PVOID pbBuff,
IN ULONG dwcbLen
)
{
PROCNAME("RegSetDeviceParam")
NTSTATUS status;
HANDLE hkey;
PAGED_CODE();
ENTER(2, ("(pdo=%p,ParamName=%S,Type=%x,pbBuff=%p,Len=%d)\n",
pdo, pwstrParamName, dwType, pbBuff, dwcbLen));
status = IoOpenDeviceRegistryKey(pdo,
PLUGPLAY_REGKEY_DEVICE,
STANDARD_RIGHTS_ALL,
&hkey);
if (NT_SUCCESS(status))
{
UNICODE_STRING ucKeyName;
RtlInitUnicodeString(&ucKeyName, pwstrParamName);
status = ZwSetValueKey(hkey,
&ucKeyName,
0,
dwType,
pbBuff,
dwcbLen);
if (!NT_SUCCESS(status))
{
ERRPRINT(("failed to set parameter %S (status=%x)\n",
pwstrParamName, status));
}
ZwClose(hkey);
}
else
{
ERRPRINT(("failed to open device registry key (status=%x)\n",
status));
}
EXIT(2, ("=%x\n", status));
return status;
} //RegSetDeviceParam