📄 client.cpp
字号:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft end-user
// license agreement (EULA) under which you licensed this SOFTWARE PRODUCT.
// If you did not accept the terms of the EULA, you are not authorized to use
// this source code. For a copy of the EULA, please see the LICENSE.RTF on your
// install media.
//
//
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Module Name:
client.cpp
Abstract:
USB Client interface for Human Interface Device (HID) Class.
Functions:
Notes:
--*/
#include "usbhid.h"
// Clear the stall feature on endpoint 0.
DWORD
ClearEndpointZeroStall(
PUSBHID_CONTEXT pUsbHid
)
{
static const DWORD dwClearStallTimeout = 1000;
DWORD dwErr;
DEBUGCHK(pUsbHid != NULL);
dwErr = ClearOrSetFeature(
pUsbHid->pUsbFuncs,
pUsbHid->hUsbDevice,
DefaultTransferComplete,
pUsbHid->hEP0Event,
USB_SEND_TO_ENDPOINT,
USB_FEATURE_ENDPOINT_STALL,
0, // Endpoint 0
dwClearStallTimeout,
FALSE);
return dwErr;
}
// Regarding fSendToInterface. The original HID spec said that the Hid
// descriptor would come after the interface and endpoint descriptors.
// It also said that class specific commands should be sent to the endpoint.
// The next spec said that the HID descriptor would come after the interface
// descriptor (not at the end) and that commands should be sent to the
// interface, not to the endpoint. So, I'm assuming that if I find the
// Hid descriptor after the interface, the device is following the new spec
// and I should send commands to the interface. Otherwise, I'll send them
// to the endpoint, as stated in the old spec.
void
DetermineDestination(
PUSBHID_CONTEXT pUsbHid,
BYTE *pbmRequestType,
USHORT *pwIndex
)
{
DEBUGCHK(pUsbHid != NULL);
DEBUGCHK(pbmRequestType != NULL);
DEBUGCHK(pwIndex != NULL);
// Do we send this to the endpoint or the interface?
if (pUsbHid->fSendToInterface == TRUE) {
*pbmRequestType = USB_REQUEST_FOR_INTERFACE;
*pwIndex = pUsbHid->pUsbInterface->Descriptor.bInterfaceNumber;
}
else {
*pbmRequestType = USB_REQUEST_FOR_ENDPOINT;
*pwIndex = pUsbHid->InterruptIn.bIndex;
}
}
// Get a report from the device. Note that this is not to be done for
// polling the device on a regular basis. Use GetInterruptReport for that.
// Returns the error values from IssueVendorTransfer.
DWORD
WINAPI
GetReport(
HID_HANDLE hDevice,
HIDP_REPORT_TYPE type,
PCHAR pbBuffer,
DWORD cbBuffer,
PDWORD pcbTransferred,
DWORD dwTimeout
)
{
SETFNAME(_T("HidGetReport"));
PUSBHID_CONTEXT pUsbHid = (PUSBHID_CONTEXT) hDevice;
USB_DEVICE_REQUEST udr;
BYTE bReportID;
DWORD dwErr;
BYTE bType = type + 1;
USB_ERROR usbErr;
if (VALID_CONTEXT(pUsbHid) == FALSE) {
DEBUGMSG(ZONE_ERROR, (_T("%s: Invalid device handle\r\n"), pszFname));
dwErr = ERROR_INVALID_HANDLE;
goto EXIT;
}
if (pbBuffer == NULL || pcbTransferred == NULL) {
DEBUGMSG(ZONE_ERROR, (_T("%s: Invalid parameter\r\n"), pszFname));
dwErr = ERROR_INVALID_PARAMETER;
goto EXIT;
}
if (cbBuffer > HID_MAX_REPORT_LENGTH) {
cbBuffer = HID_MAX_REPORT_LENGTH;
}
DEBUGCHK(IsBadReadPtr(pbBuffer, cbBuffer) == FALSE);
bReportID = *pbBuffer;
DetermineDestination(pUsbHid, &udr.bmRequestType, &udr.wIndex);
udr.bmRequestType |= USB_REQUEST_HOST_TO_DEVICE | USB_REQUEST_CLASS;
udr.bRequest = USB_REQUEST_HID_GET_REPORT;
udr.wValue = ( (bType << 8) | bReportID);
udr.wLength = (USHORT) (cbBuffer - 1); // Do not receive the report ID
dwErr = IssueVendorTransfer(
pUsbHid->pUsbFuncs,
pUsbHid->hUsbDevice,
NULL,
NULL,
USB_OUT_TRANSFER,
&udr,
pbBuffer + 1, // Do not receive the report ID
0,
pcbTransferred,
dwTimeout,
&usbErr);
if (dwErr == ERROR_SUCCESS && usbErr == USB_NO_ERROR) {
// Add in the report ID
*pbBuffer = bReportID;
}
else if (usbErr == USB_STALL_ERROR) {
// GET_REPORT is not required according to the HID spec. If it is not
// present, we might stall endpoint 0. If so, clear the stall feature.
ClearEndpointZeroStall(pUsbHid);
dwErr = ERROR_NOT_SUPPORTED;
}
else if (usbErr != ERROR_SUCCESS) {
dwErr = ERROR_GEN_FAILURE;
}
EXIT:
if (dwErr != ERROR_SUCCESS) {
SetLastError(dwErr);
// NKDbgPrintfW("GetReport Error:%d",GetLastError());
}
return dwErr;
}
// Set a report on the device.
// Returns the error values from IssueVendorTransfer.
DWORD
WINAPI
SetReport(
HID_HANDLE hDevice,
HIDP_REPORT_TYPE type,
PCHAR pbBuffer,
DWORD cbBuffer,
DWORD dwTimeout
)
{
SETFNAME(_T("HidSetReport"));
PHID_CLIENT_HANDLE pHidClient = (PHID_CLIENT_HANDLE) hDevice;
PUSBHID_CONTEXT pUsbHid;
USB_DEVICE_REQUEST udr;
BYTE bReportID;
DWORD dwBytesTransferred;
DWORD dwErr;
BYTE bType = type + 1;
USB_ERROR usbErr;
if (VALID_CLIENT_HANDLE(pHidClient) == FALSE) {
DEBUGMSG(ZONE_ERROR, (_T("%s: Invalid device handle\r\n"), pszFname));
dwErr = ERROR_INVALID_HANDLE;
goto EXIT;
}
if (pbBuffer == NULL) {
DEBUGMSG(ZONE_ERROR, (_T("%s: Invalid parameter\r\n"), pszFname));
dwErr = ERROR_INVALID_PARAMETER;
goto EXIT;
}
if (cbBuffer > HID_MAX_REPORT_LENGTH) {
DEBUGMSG(ZONE_ERROR, (_T("%s: Buffer is too large\r\n"), pszFname));
dwErr = ERROR_MESSAGE_EXCEEDS_MAX_SIZE;
goto EXIT;
}
DEBUGCHK(IsBadReadPtr(pbBuffer, cbBuffer) == FALSE);
ValidateClientHandle(pHidClient);
pUsbHid = pHidClient->pUsbHid;
ValidateHidContext(pUsbHid);
bReportID = *pbBuffer;
DetermineDestination(pUsbHid, &udr.bmRequestType, &udr.wIndex);
udr.bmRequestType |= USB_REQUEST_HOST_TO_DEVICE | USB_REQUEST_CLASS;
udr.bRequest = USB_REQUEST_HID_SET_REPORT;
udr.wValue = ( (bType << 8) | bReportID);
udr.wLength = (USHORT) (cbBuffer - 1); // Do not send the report ID
dwErr = IssueVendorTransfer(
pUsbHid->pUsbFuncs,
pUsbHid->hUsbDevice,
NULL,
NULL,
USB_OUT_TRANSFER,
&udr,
pbBuffer + 1, // Do not send the report ID
0,
&dwBytesTransferred,
dwTimeout,
&usbErr);
if (usbErr == USB_STALL_ERROR) {
// SET_REPORT is not required according to the HID spec. If it is not
// present, we might stall endpoint 0. If so, clear the stall feature.
ClearEndpointZeroStall(pUsbHid);
dwErr = ERROR_NOT_SUPPORTED;
}
else if (usbErr != ERROR_SUCCESS) {
dwErr = ERROR_GEN_FAILURE;
}
EXIT:
if (dwErr != ERROR_SUCCESS) {
SetLastError(dwErr);
}
// NKDbgPrintfW("SetReport Error:%d",GetLastError());
return dwErr;
}
// Get an interrupt report from the device.
// Returns ERROR_SUCCESS, ERROR_DEVICE_REMOVED, or ERROR_TIMEOUT.
DWORD
WINAPI
GetInterruptReport(
HID_HANDLE hDevice,
PCHAR pbBuffer,
DWORD cbBuffer,
PDWORD pcbTransferred,
HANDLE hCancel,
DWORD dwTimeout
)
{
SETFNAME(_T("GetInterruptReport"));
PHID_CLIENT_HANDLE pHidClient = (PHID_CLIENT_HANDLE) hDevice;
DWORD dwErr;
if (VALID_CLIENT_HANDLE(pHidClient) == FALSE) {
DEBUGMSG(ZONE_ERROR, (_T("%s: Invalid device handle\r\n"), pszFname));
dwErr = ERROR_INVALID_HANDLE;
goto EXIT;
}
if (pbBuffer == NULL || pcbTransferred == NULL) {
dwErr = ERROR_INVALID_PARAMETER;
goto EXIT;
}
DEBUGCHK(IsBadWritePtr(pbBuffer, cbBuffer) == FALSE);
DEBUGCHK(IsBadWritePtr(pcbTransferred, sizeof(*pcbTransferred)) == FALSE);
ValidateClientHandle(pHidClient);
dwErr = pHidClient->pQueue->Dequeue(pbBuffer, cbBuffer, pcbTransferred,
hCancel, dwTimeout);
EXIT:
if (dwErr != ERROR_SUCCESS) {
SetLastError(dwErr);
}
// NKDbgPrintfW("GetRInterruptReport Error:%d",GetLastError());
return dwErr;
}
// Return a report descriptor given the ID.
static
PHIDP_REPORT_IDS
GetReportDesc(
PHIDP_DEVICE_DESC phidpDeviceDesc,
DWORD dwReportID
)
{
PHIDP_REPORT_IDS pReport = NULL;
DWORD dwIdx;
DEBUGCHK(phidpDeviceDesc != NULL);
DEBUGCHK(phidpDeviceDesc->ReportIDs != NULL);
for (dwIdx = 0; dwIdx < phidpDeviceDesc->ReportIDsLength; ++dwIdx)
{
PHIDP_REPORT_IDS pCurrReport = &phidpDeviceDesc->ReportIDs[dwIdx];
DEBUGCHK(pCurrReport != NULL);
if (pCurrReport->ReportID == dwReportID) {
pReport = pCurrReport;
break;
}
}
DEBUGCHK(pReport != NULL);
return pReport;
}
// Return a collection decriptor given the collection number. Also returns the
// index of the collection in the device context's array.
static
PHIDP_COLLECTION_DESC
GetCollectionDesc(
PHIDP_DEVICE_DESC phidpDeviceDesc,
DWORD dwCollectionNumber,
PDWORD pdwCollectionIndex
)
{
PHIDP_COLLECTION_DESC pColl = NULL;
DWORD dwIdx;
DEBUGCHK(phidpDeviceDesc != NULL);
DEBUGCHK(phidpDeviceDesc->CollectionDesc != NULL);
for (dwIdx = 0; dwIdx < phidpDeviceDesc->CollectionDescLength; ++dwIdx)
{
PHIDP_COLLECTION_DESC pCurrColl = &phidpDeviceDesc->CollectionDesc[dwIdx];
DEBUGCHK(pCurrColl != NULL);
if (pCurrColl->CollectionNumber == dwCollectionNumber) {
pColl = pCurrColl;
break;
}
}
DEBUGCHK(pColl != NULL);
if (pdwCollectionIndex != NULL) {
*pdwCollectionIndex = dwIdx;
}
return pColl;
}
// If this report includes power events, perform them.
static
void
PerformPowerEvents(
PHIDP_COLLECTION_DESC phidpCollection,
BYTE *pbHidPacket,
DWORD cbHidPacket
)
{
SETFNAME(_T("PerformPowerEvents"));
NTSTATUS status;
ULONG uPowerEvents = 0;
DEBUGCHK(phidpCollection != NULL);
DEBUGCHK(pbHidPacket != NULL);
status = HidP_SysPowerEvent((PCHAR) pbHidPacket, (USHORT) cbHidPacket,
phidpCollection->PreparsedData, &uPowerEvents);
if (NT_SUCCESS(status))
{
DEBUGMSG(ZONE_HID_DATA, (_T("%s: Received power event 0x%08x\r\n"),
pszFname, uPowerEvents));
if ((uPowerEvents & (SYS_BUTTON_SLEEP | SYS_BUTTON_POWER)) != 0) {
keybd_event(VK_OFF, 0, 0, 0);
keybd_event(VK_OFF, 0, KEYEVENTF_KEYUP, 0);
}
}
}
#ifdef DEBUG
// Print out the report received.
static
void
OutputReport(
PBYTE pData,
DWORD cbData,
DWORD dwInterface
)
{
TCHAR szBuf[512];
TCHAR *szCurr = szBuf;
DEBUGCHK(dim(szBuf) > cbData * 3); // Overflow if failure
if (dim(szBuf) > cbData * 3)
{
for (DWORD dwByte = 0; dwByte < cbData; ++dwByte) {
_stprintf(szCurr, _T("%02X "), pData[dwByte]);
szCurr += 3;
}
}
else {
DEBUGCHK(FALSE); // Why does the buffer need to be bigger? 512 is plenty!
szBuf[0] = 0;
}
DEBUGMSG(ZONE_HID_DATA, (_T("HID Interface %i report: %s\r\n"),
dwInterface, szBuf));
}
#else
#define OutputReport(ptr, dw1, dw2)
#endif // DEBUG
// Process an incoming report from a device.
// The first byte of pData must contain a report ID even if
// the device would not normally include it (for ID 0).
void
ProcessReport(
PUSBHID_CONTEXT pUsbHid,
BYTE *pData,
DWORD cbData
)
{
SETFNAME(_T("ProcessReport"));
PHIDP_REPORT_IDS phidpReport;
PHIDP_COLLECTION_DESC phidpCollection;
DWORD dwReportID;
DWORD dwCollectionIndex;
PHID_CLIENT_HANDLE pClientHandle;
HidTLCQueue *pQueue;
DEBUGCHK(pUsbHid != NULL);
DEBUGCHK(pData != NULL);
// We always get a report ID.
dwReportID = *pData;
// Get the report and collection data structures
phidpReport = GetReportDesc(pUsbHid->phidpDeviceDesc, dwReportID);
DEBUGCHK(phidpReport != NULL);
phidpCollection = GetCollectionDesc(pUsbHid->phidpDeviceDesc,
phidpReport->CollectionNumber, &dwCollectionIndex);
DEBUGCHK(phidpCollection != NULL);
DEBUGCHK(dwCollectionIndex < pUsbHid->phidpDeviceDesc->CollectionDescLength);
OutputReport(pData, cbData, pUsbHid->pUsbInterface->Descriptor.bInterfaceNumber);
// Perform any power events listed in this report
PerformPowerEvents(phidpCollection, pData, cbData);
// Send this HID packet to the proper client
pClientHandle = &pUsbHid->pClientHandles[dwCollectionIndex];
// Only queue this report if we have a client that will receive it.
if (pClientHandle->hInst != NULL)
{
pQueue = pClientHandle->pQueue;
pQueue->Lock();
if (pQueue->IsAccepting() == TRUE) {
if (pQueue->IsFull() == FALSE) {
BOOL fRet;
fRet = pQueue->Enqueue((PCHAR) pData, cbData);
DEBUGCHK(fRet);
}
else {
DEBUGMSG(ZONE_ERROR, (_T("%s: Error: Queue is full. Dropping packet.\r\n"), pszFname));
}
}
else {
DEBUGMSG(ZONE_HID_DATA, (_T("%s: Queue not accepting input. Dropping packet.\r\n"), pszFname));
}
pQueue->Unlock();
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -