windows-nt/Source/XPSP1/NT/drivers/wdm/input/tabletpc/tabsrv/tabdev.cpp
2020-09-26 16:20:57 +08:00

627 lines
21 KiB
C++

/*++
Copyright (c) 2000 Microsoft Corporation. All rights reserved.
Module Name:
tabdev.cpp
Abstract:
This module contains tablet device functions.
Author:
Michael Tsang (MikeTs) 01-Jun-2000
Environment:
User mode
Revision History:
--*/
#include "pch.h"
/*++
@doc INTERNAL
@func unsigned | DeviceThread | Device thread.
@parm IN OUT PDEVICE_DATA | pdevdata | Points to device data.
@rvalue Always returns 0.
--*/
unsigned __stdcall
DeviceThread(
IN PVOID param
)
{
TRACEPROC("DeviceThread", 2)
PDEVICE_DATA pdevdata = (PDEVICE_DATA)param;
BOOL fDigitizer = (pdevdata == &gdevDigitizer);
BOOL fSuccess = TRUE;
TRACEENTER(("(pdevdata=%p,Thread=%s)\n",
pdevdata, fDigitizer? "Digitizer": "Buttons"));
// Bump our priority so we can service device data ASAP.
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
if (OpenTabletDevice(pdevdata))
{
OVERLAPPED overLapped;
HANDLE ahWait[2];
PCHAR apBuf[2];
// Initialize the OVERLAPPED structure for ReadFile
overLapped.Offset = overLapped.OffsetHigh = 0;
overLapped.hEvent = CreateEvent(NULL, // default security
FALSE, // auto-reset event
FALSE, // initially unset
NULL); // no name
TRACEASSERT(overLapped.hEvent != NULL);
// Create the array of event handles to wait upon (below)
ahWait[0] = pdevdata->hStopDeviceEvent;
ahWait[1] = overLapped.hEvent;
// Allocate and zero-init a couple of buffers for device reports
apBuf[0] = (PCHAR)calloc(pdevdata->hidCaps.InputReportByteLength,
sizeof(CHAR));
apBuf[1] = (PCHAR)calloc(pdevdata->hidCaps.InputReportByteLength,
sizeof(CHAR));
TRACEASSERT(apBuf[0] && apBuf[1]);
// Allocate a buffer for HID report button states
pdevdata->dwcButtons = HidP_MaxUsageListLength(HidP_Input,
0,
pdevdata->pPreParsedData);
if (pdevdata->dwcButtons > 0)
{
pdevdata->pDownButtonUsages = (PUSAGE)malloc(pdevdata->dwcButtons*
sizeof(USAGE));
TRACEASSERT(pdevdata->pDownButtonUsages);
}
else
{
pdevdata->pDownButtonUsages = NULL;
}
if (fDigitizer)
{
// Determine the digitizer's (default) max X & Y values for scaling
// the reports later
GetMinMax(HID_USAGE_PAGE_GENERIC,
HID_USAGE_GENERIC_X,
&gdwMinX,
&gdwMaxX);
gdwRngX = gdwMaxX - gdwMinX;
GetMinMax(HID_USAGE_PAGE_GENERIC,
HID_USAGE_GENERIC_Y,
&gdwMinY, &gdwMaxY);
gdwRngY = gdwMaxY - gdwMinY;
#ifdef DRAW_INK
ghwndDrawInk = GetDesktopWindow();
#endif
}
PTSTHREAD pThread = FindThread(fDigitizer?
TSF_DIGITHREAD: TSF_BUTTONTHREAD);
while (fSuccess && !(gdwfTabSrv & TSF_TERMINATE))
{
if (SwitchThreadToInputDesktop(pThread))
{
BOOL fImpersonate = FALSE;
int iBufIdx = 0;
DWORD dwBytesRead;
fImpersonate = ImpersonateCurrentUser();
// Issue an overlapped read to get a device (input) report
if (ReadReportOverlapped(pdevdata,
apBuf[iBufIdx],
&dwBytesRead,
&overLapped))
{
DWORD rcWait;
MSG Msg;
for (;;)
{
// Wait for the read to complete, or for the termination
// event to be set
if (fDigitizer)
{
rcWait = WaitForMultipleObjectsEx(2,
ahWait,
FALSE,
INFINITE,
FALSE);
}
else
{
rcWait = MsgWaitForMultipleObjects(2,
ahWait,
FALSE,
INFINITE,
QS_TIMER);
}
if (rcWait == WAIT_OBJECT_0)
{
// The terminate device event was set.
break;
}
else if (rcWait == WAIT_OBJECT_0 + 1)
{
DWORD dwcb;
// Previous read has completed, swap the buffer
// pointer and issue another overlapped read
// before processing this report.
PCHAR pBufLast = apBuf[iBufIdx];
iBufIdx ^= 1;
if (!GetOverlappedResult(pdevdata->hDevice,
&overLapped,
&dwcb,
FALSE))
{
TRACEWARN(("GetOverlappedResult failed (err=%d).\n",
GetLastError()));
fSuccess = FALSE;
}
else
{
if (!ReadReportOverlapped(pdevdata,
apBuf[iBufIdx],
&dwBytesRead,
&overLapped))
{
TABSRVERR(("Read error getting device report (err=%d)",
GetLastError()));
fSuccess = FALSE;
break;
}
if (fDigitizer)
{
ProcessDigitizerReport(pBufLast);
}
else
{
ProcessButtonsReport(pBufLast);
}
}
}
else if (!fDigitizer &&
PeekMessage(&Msg,
NULL,
WM_TIMER,
WM_TIMER,
PM_REMOVE | PM_NOYIELD))
{
((TIMERPROC)Msg.lParam)(Msg.hwnd,
Msg.message,
Msg.wParam,
Msg.time);
}
else
{
TRACEWARN(("WaitForMultipleObject failed (rcWait=%d,err=%d)\n",
rcWait, GetLastError()));
}
}
CancelIo(pdevdata->hDevice);
}
else
{
TABSRVERR(("Read error getting device report (err=%d)",
GetLastError()));
fSuccess = FALSE;
}
if (fImpersonate)
{
RevertToSelf();
}
}
else
{
TABSRVERR(("Failed to set current desktop.\n"));
fSuccess = FALSE;
}
}
if (pdevdata->pDownButtonUsages)
{
free(pdevdata->pDownButtonUsages);
pdevdata->pDownButtonUsages = NULL;
}
free(apBuf[0]);
free(apBuf[1]);
CloseHandle(overLapped.hEvent);
CloseTabletDevice(pdevdata);
}
else
{
TABSRVERR(("Failed to open %s device.\n",
fDigitizer? "Digitizer": "Buttons"));
}
TRACEEXIT(("=0\n"));
return 0;
} //DeviceThread
/*++
@doc INTERNAL
@func BOOL | OpenTabletDevice | Open and initialize the tablet device.
@parm IN OUT PDEVICE_DATA | pDevData | Points to the tablet device data.
@rvalue SUCCESS | Returns TRUE.
@rvalue FAILURE | Returns FALSE.
--*/
BOOL
OpenTabletDevice(
IN OUT PDEVICE_DATA pDevData
)
{
TRACEPROC("OpenTabletDevice", 2)
BOOL rc = FALSE;
GUID hidGuid;
HDEVINFO hDevInfo;
TRACEENTER(("(pDevData=%p,UsagePage=%x,Usage=%x)\n",
pDevData, pDevData->UsagePage, pDevData->Usage));
pDevData->hDevice = INVALID_HANDLE_VALUE;
// Get the GUID for HID devices
HidD_GetHidGuid(&hidGuid);
// Get handle to present HID devices
hDevInfo = SetupDiGetClassDevs(&hidGuid,
NULL,
NULL,
(DIGCF_PRESENT | DIGCF_DEVICEINTERFACE));
if (hDevInfo != INVALID_HANDLE_VALUE)
{
ULONG i;
// Enumerate the HID devices, looking for the digitizer and button devices
for (i = 0; ; ++i)
{
SP_DEVICE_INTERFACE_DATA devInterface;
devInterface.cbSize = sizeof(devInterface);
// First we have to enumerate the device interfaces
if (SetupDiEnumDeviceInterfaces(hDevInfo,
0,
&hidGuid,
i,
&devInterface))
{
// Then we get the interface detail information (including device pathname)
PSP_DEVICE_INTERFACE_DETAIL_DATA pIfDetail;
pIfDetail = GetDeviceInterfaceDetail(hDevInfo, &devInterface);
TRACEASSERT(pIfDetail != 0);
if (pIfDetail != 0)
{
DEVICE_DATA thisDev;
TRACEINFO(2, ("HID DEVICE: %s\n", pIfDetail->DevicePath));
// Finally we get specific device info to determine
// if it's one of ours
thisDev = *pDevData;
if (GetDeviceData(pIfDetail->DevicePath, &thisDev))
{
TRACEINFO(2, ("Usage Page: %d, Usage: %d\n",
thisDev.hidCaps.UsagePage,
thisDev.hidCaps.Usage));
// Is this is a device we want?
if ((pDevData->UsagePage == thisDev.hidCaps.UsagePage) &&
(pDevData->Usage == thisDev.hidCaps.Usage))
{
*pDevData = thisDev;
break;
}
else
{
// Not one of our devices, clean-up and try again
CloseTabletDevice(&thisDev);
}
}
free(pIfDetail);
}
}
else
{
DWORD dwLastError = GetLastError();
// SetupDiEnumDeviceInterfaces() failed, hopefull with
// ERROR_NO_MORE_ITEMS
if (dwLastError != ERROR_NO_MORE_ITEMS)
{
TABSRVERR(("SetupDiEnumDeviceInterfaces failed (err=%d)\n",
dwLastError));
}
break;
}
}
// Clean-up from SetupDiGetClassDevs()
SetupDiDestroyDeviceInfoList(hDevInfo);
}
else
{
TABSRVERR(("SetupDiGetClassDevs failed (err=%d)\n", GetLastError()));
}
rc = pDevData->hDevice != INVALID_HANDLE_VALUE;
TRACEEXIT(("=%x\n", rc));
return rc;
} //OpenTabletDevice
/*++
@doc INTERNAL
@func VOID | CloseTabletDevice | Close and clean up the tablet device.
@parm IN PDEVICE_DATA | pDevData | Points to the tablet device data.
@rvalue None.
--*/
VOID
CloseTabletDevice(
IN PDEVICE_DATA pDevData
)
{
TRACEPROC("CloseTabletDevice", 2)
TRACEENTER(("(pDevData=%p)\n", pDevData));
if (pDevData->hDevice != INVALID_HANDLE_VALUE)
{
CloseHandle(pDevData->hDevice);
HidD_FreePreparsedData(pDevData->pPreParsedData);
pDevData->hDevice = INVALID_HANDLE_VALUE;
}
TRACEEXIT(("!\n"));
} //CloseTabletDevice
/*++
@doc INTERNAL
@func PSP_DEVICE_INTERFACE_DETAIL_DATA | GetDeviceInterfaceDetail |
Gets interface details for a specific device.
@parm IN HDEVINFO | hDevInfo | Handle to HID device info.
@parm IN PSP_DEVICE_INTERFACE_DATA | pDevInterface |
Points to device interface data.
@rvalue SUCCESS | Returns pointer to SP_DEVICE_INTERFACE_DETAIL_DATA.
@rvalue FAILURE | Returns NULL.
@note Caller is responsible for freeing the returned memory.
--*/
PSP_DEVICE_INTERFACE_DETAIL_DATA
GetDeviceInterfaceDetail(
IN HDEVINFO hDevInfo,
IN PSP_DEVICE_INTERFACE_DATA pDevInterface
)
{
TRACEPROC("GetDeviceInterfaceDetail", 3)
PSP_DEVICE_INTERFACE_DETAIL_DATA pDetails;
ULONG ulLen = 0;
TRACEENTER(("(hDevInfo=%x,pDevInterface=%p)\n", hDevInfo, pDevInterface));
// Make a call to get the required buffer length
SetupDiGetDeviceInterfaceDetail(hDevInfo,
pDevInterface,
NULL,
0,
&ulLen,
NULL);
TRACEASSERT(ulLen > 0);
// Allocate a sufficiently large buffer
pDetails = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(ulLen);
TRACEASSERT(pDetails != 0);
if (pDetails != NULL)
{
// Now that we have a buffer, get the details
pDetails->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
if (!SetupDiGetDeviceInterfaceDetail(hDevInfo,
pDevInterface,
pDetails,
ulLen,
&ulLen,
NULL))
{
TABSRVERR(("SetupDiGetDeviceInterfaceDetail failed (err=%d)\n",
GetLastError()));
free(pDetails);
pDetails = NULL;
}
}
TRACEEXIT(("=%p\n", pDetails));
return pDetails;
} //GetDeviceInterfaceDetail
/*++
@doc INTERNAL
@func BOOL | GetDeviceData | Opens a HID device and retrieves the
'preparsed' data and HID capabilities.
@parm IN LPCTSTR | pszDevPath | Points to the device path name.
@parm OUT PDEVICE_DATA | pDevData | Points to the DEVICE_DATA structure.
@rvalue SUCCESS | Returns TRUE.
@rvalue FAILURE | Returns FALSE.
--*/
BOOL
GetDeviceData(
IN LPCTSTR pszDevPath,
OUT PDEVICE_DATA pDevData
)
{
TRACEPROC("GetDeviceData", 3)
BOOL rc = FALSE;
TRACEENTER(("(pszDevPath=%s,pDevData=%p)\n", pszDevPath, pDevData));
// First, open the device for read/write, exclusive access
pDevData->hDevice = CreateFile(pszDevPath,
GENERIC_READ | GENERIC_WRITE,// access mode
FILE_SHARE_READ | FILE_SHARE_WRITE,// sharing flags
NULL, // security attributes
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED, // flags & attributes
NULL); // template file
if (pDevData->hDevice != INVALID_HANDLE_VALUE)
{
// Then get the preparsed data and capabilities
if (HidD_GetPreparsedData(pDevData->hDevice,
&pDevData->pPreParsedData) &&
(HidP_GetCaps(pDevData->pPreParsedData, &pDevData->hidCaps) ==
HIDP_STATUS_SUCCESS))
{
#ifdef DEBUG
#if !defined(_UNICODE)
char szAnsi[512];
#endif
TCHAR szBuff[256];
if (HidD_GetManufacturerString(pDevData->hDevice,
szBuff,
sizeof(szBuff)))
{
#if !defined(_UNICODE)
WideCharToMultiByte(CP_ACP,
0,
(LPWSTR)szBuff,
-1,
szAnsi,
sizeof(szAnsi),
NULL,
NULL);
strcpy(szBuff, szAnsi);
#endif
TRACEINFO(2, ("ManufacturerStr: %s\n", szBuff));
}
if (HidD_GetProductString(pDevData->hDevice,
szBuff,
sizeof(szBuff)))
{
#if !defined(_UNICODE)
WideCharToMultiByte(CP_ACP,
0,
(LPWSTR)szBuff,
-1,
szAnsi,
sizeof(szAnsi),
NULL,
NULL);
strcpy(szBuff, szAnsi);
#endif
TRACEINFO(2, ("ProductStr: %s\n", szBuff));
}
#endif
rc = TRUE;
}
else
{
TABSRVERR(("HidD_GetPreparsedData/HidP_GetCaps failed (err=%d)\n",
GetLastError()));
CloseHandle(pDevData->hDevice);
pDevData->hDevice = INVALID_HANDLE_VALUE;
}
}
else
{
TRACEWARN(("failed to open %s (err=%d)\n", pszDevPath, GetLastError()));
}
TRACEEXIT(("=%x\n", rc));
return rc;
} //GetDeviceData
/*++
@doc INTERNAL
@func BOOL | ReadReportOverlapped | Get a device input report using
overlapped ReadFile.
@parm IN PDEVICE_DATA | pDevData | Device to read.
@parm OUT LPVOID | lpvBuffer | Data buffer.
@parm OUT LPDWORD | lpdwcBytesRead | To hold number of bytes read.
@parm IN LPOVERLAPPED | lpOverlapped | Overlapped buffer
@rvalue SUCCESS | Returns TRUE.
@rvalue FAILURE | Returns FALSE.
--*/
BOOL
ReadReportOverlapped(
IN PDEVICE_DATA pDevData,
OUT LPVOID lpvBuffer,
OUT LPDWORD lpdwcBytesRead,
IN LPOVERLAPPED lpOverlapped
)
{
TRACEPROC("ReadReportOverlapped", 5)
BOOL rc = TRUE;
TRACEENTER(("(pDevData=%p,lpvBuffer=%p,lpdwcBytesRead=%p,lpOverlapped=%p)\n",
pDevData, lpvBuffer, lpdwcBytesRead, lpOverlapped));
rc = ReadFile(pDevData->hDevice,
lpvBuffer,
pDevData->hidCaps.InputReportByteLength,
lpdwcBytesRead,
lpOverlapped);
if (rc == TRUE)
{
// ReadFile completed synchronously. Set our completion event
// so this case can be handled by without special checking by
// our caller.
SetEvent(lpOverlapped->hEvent);
}
else
{
DWORD dwLastError = GetLastError();
if (dwLastError == ERROR_IO_PENDING)
{
rc = TRUE;
}
else
{
TABSRVERR(("Error reading device report (err=%d)\n",
dwLastError));
}
}
TRACEEXIT(("=%x\n", rc));
return rc;
} //ReadReportOverlapped