📄 hidmdd.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:
hidmdd.cpp
Abstract:
MDD for Human Interface Device (HID) Class Driver.
Functions:
Notes:
--*/
#include "hidmdd.h"
#include <pm.h>
#include <devload.h>
// Globals that keep track of the reference count for the unnamed stream
// driver.
static CRITICAL_SECTION g_csHidRefCount;
static DWORD g_dwHidRefCount;
static HANDLE g_hMainHidDriver;
#ifdef DEBUG
// Print the report.
static
void
OutputReport(
PBYTE pData,
DWORD cbData,
DWORD dwReportID,
USAGE UsagePage,
USAGE Usage
)
{
TCHAR szBuf[512];
TCHAR *szCurr = szBuf;
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 Report ID %u (TLC %X-%X): %s\r\n"), dwReportID,
UsagePage, Usage, szBuf));
}
// Prints the available device strings.
static
void
DumpDeviceStrings(
PCHID_CONTEXT pHidContext
)
{
SETFNAME(_T("DumpDeviceStrings"));
struct DESC_TYPE_PAIR {
LPCTSTR pszDesc;
HID_STRING_TYPE stringType;
};
static const DESC_TYPE_PAIR rgList[] = {
{ _T("Manufacturer"), HID_STRING_ID_IMANUFACTURER },
{ _T("Product"), HID_STRING_ID_IPRODUCT},
{ _T("Serial Number"), HID_STRING_ID_ISERIALNUMBER},
};
TCHAR szBuf[256];
DWORD cchBuf;
DWORD dwIdx;
DWORD dwErr;
for (dwIdx = 0; dwIdx < dim(rgList); ++dwIdx) {
dwErr = HidPdd_GetString(pHidContext->hPddDevice,
rgList[dwIdx].stringType, 0, szBuf, dim(szBuf), &cchBuf);
if (dwErr == ERROR_SUCCESS) {
DEBUGMSG(ZONE_INIT, (_T("%s: %s: %s\r\n"), pszFname,
rgList[dwIdx].pszDesc, szBuf));
}
}
}
#else
#define OutputReport(ptr, dw1, dw2, usage1, usage2)
#define DumpDeviceStrings(ptr)
#endif // DEBUG
// Generic routine to return the subkey of the form
// pdwVals[0]_pdwVals[1]_...pdwVals[cdwVals-1].
static
HKEY
OpenSpecificClientRegKey(
HKEY hKeyRoot,
const DWORD *pdwVals,
DWORD cdwVals
)
{
HKEY hKeyRet = NULL;
TCHAR szBuf[40];
TCHAR *pszCur = szBuf;
const TCHAR *pszKey;
DWORD dwIdx;
int ichWritten;
DEBUGCHK(cdwVals * 11 + 1 < dim(szBuf));
if (cdwVals == 0) {
// Try default
pszKey = DEFAULT_SZ;
}
else
{
// Build key string
for (dwIdx = 0; dwIdx < cdwVals; ++dwIdx) {
ichWritten = _stprintf(pszCur, _T("%u_"), pdwVals[dwIdx]);
pszCur += ichWritten;
}
*(pszCur - 1) = 0; // Remove the last _
pszKey = szBuf;
}
RegOpenKey(hKeyRoot, pszKey, &hKeyRet);
return hKeyRet;
}
// Contains most of the logic to find and open the proper HID client key.
// The algorithm searches for the most specific vendor key of the form
// idVendor_idProduct_bcdDevice. Then its subkeys are searched for the most
// specific interface key of the form bInterface_bCollection. Then its
// subkeys are searched for the most specific top level collection key
// of the form UsagePage_Usage.
//
// The order of vendor precedence is described below.
//
// idVendor_idProduct_bcdDevice
// idVendor_idProduct
// idVendor
// Default
//
// The order of interface precedence is described below.
//
// bInterface_bCollection
// bInterface
// Default
//
// The order of TLC precedence is
//
// UsagePage_Usage
// UsagePage
// Default
//
// So the order of checking would be
// 1. idVendor_idProduct_bcdDevice\bInterface_bCollection\UsagePage_Usage
// 2. idVendor_idProduct_bcdDevice\bInterface_bCollection\UsagePage
// 3. idVendor_idProduct_bcdDevice\bInterface_bCollection\Default
// 4. idVendor_idProduct_bcdDevice\bInterface\UsagePage_Usage
// ...
// 7. idVendor_idProduct_bcdDevice\Default\UsagePage_Usage
// ...
// 10. idVendor_idProduct\bInterface_bCollection\UsagePage_Usage
// ...
// 42. Default\bInterface_bCollection\Default
// 43. Default\bInterface\UsagePage_Usage
// 44. Default\bInterface\UsagePage
// 45. Default\bInterface\Default
// 46. Default\Default\UsagePage_Usage
// 47. Default\Default\UsagePage
// 48. Default\Default\Default
//
static
HKEY
OpenClientRegKey(
HKEY hKeyRoot,
const DWORD *pdwVendorVals,
DWORD cdwVendorVals,
const DWORD *pdwInterfaceVals,
DWORD cdwInterfaceVals,
const DWORD *pdwTLCVals,
DWORD cdwTLCVals,
PHID_DRIVER_SETTINGS pDriverSettingsUsed
)
{
SETFNAME(_T("OpenClientRegKey"));
HKEY hKeyRet = NULL;
HKEY hKeyVendor;
HKEY hKeyInterface;
INT cCurrVendor = -1;
INT cCurrInterface = -1;
INT cCurrTLC = -1;
INT iIdx;
DEBUGCHK(pdwVendorVals != NULL);
DEBUGCHK(pdwInterfaceVals != NULL);
DEBUGCHK(pdwTLCVals != NULL);
for (cCurrVendor = (INT) cdwVendorVals; cCurrVendor >= 0; --cCurrVendor)
{
hKeyVendor = OpenSpecificClientRegKey(hKeyRoot, pdwVendorVals, cCurrVendor);
if (hKeyVendor != NULL)
{
for (cCurrInterface = (INT) cdwInterfaceVals;
cCurrInterface >= 0;
--cCurrInterface)
{
hKeyInterface = OpenSpecificClientRegKey(hKeyVendor, pdwInterfaceVals, cCurrInterface);
if (hKeyInterface != NULL)
{
for (cCurrTLC = (INT) cdwTLCVals; cCurrTLC >= 0; --cCurrTLC)
{
hKeyRet = OpenSpecificClientRegKey(hKeyInterface, pdwTLCVals, cCurrTLC);
if (hKeyRet != NULL) {
// We found our key
break;
}
}
RegCloseKey(hKeyInterface);
if (hKeyRet != NULL) {
// We found our key
break;
}
}
}
RegCloseKey(hKeyVendor);
if (hKeyRet != NULL) {
// We found our key.
break;
}
}
}
if (hKeyRet != NULL) {
// We found a match. Fill in the driver settings used.
PDWORD rgpdwDriverSettings[] = {
&pDriverSettingsUsed->dwVendorId,
&pDriverSettingsUsed->dwProductId,
&pDriverSettingsUsed->dwReleaseNumber,
&pDriverSettingsUsed->dwInterfaceNumber,
&pDriverSettingsUsed->dwCollection,
&pDriverSettingsUsed->dwUsagePage,
&pDriverSettingsUsed->dwUsage,
};
// Fill in the matching vendor info
for (iIdx = 0; iIdx < cCurrVendor; ++iIdx) {
*rgpdwDriverSettings[iIdx] = pdwVendorVals[iIdx];
}
// Fill in the matching interface info
for (iIdx = 0; iIdx < cCurrInterface; ++iIdx) {
*rgpdwDriverSettings[iIdx + cdwVendorVals] = pdwInterfaceVals[iIdx];
}
// Fill in the matching TLC info
for (iIdx = 0; iIdx < cCurrTLC; ++iIdx) {
*rgpdwDriverSettings[iIdx + cdwVendorVals + cdwInterfaceVals]
= pdwTLCVals[iIdx];
}
}
return hKeyRet;
}
// Get the registry key for the HID client that will best service the device
// descripted in pDriverSettings.
// Returns the registry key and the actual driver settings that matched the key.
static
HKEY
FindClientRegKey(
const HID_DRIVER_SETTINGS *pDriverSettings,
PHID_DRIVER_SETTINGS pDriverSettingsUsed
)
{
SETFNAME(_T("FindClientRegKey"));
const DWORD rgdwVendorVals[] = {
pDriverSettings->dwVendorId,
pDriverSettings->dwProductId,
pDriverSettings->dwReleaseNumber
};
const DWORD rgdwInterfaceVals[] = {
pDriverSettings->dwInterfaceNumber,
pDriverSettings->dwCollection
};
const DWORD rgdwTLCVals[] = {
pDriverSettings->dwUsagePage,
pDriverSettings->dwUsage
};
HKEY hKeyRoot = NULL;
HKEY hKeyRet = NULL;
LONG iErr;
DEBUGCHK(pDriverSettings != NULL);
DEBUGCHK(pDriverSettingsUsed != NULL);
memset(pDriverSettingsUsed, (BYTE) HID_NO_INFO, sizeof(HID_DRIVER_SETTINGS));
iErr = RegOpenKey(HKEY_LOCAL_MACHINE, LOAD_CLIENTS_PATH_SZ, &hKeyRoot);
if (iErr != ERROR_SUCCESS) {
DEBUGMSG(ZONE_ERROR, (_T("%s: Failed opening HID client key [HKLM\\%s]\r\n"),
pszFname, LOAD_CLIENTS_PATH_SZ));
goto EXIT;
}
// Now find the most specific match.
hKeyRet = OpenClientRegKey(hKeyRoot,
rgdwVendorVals, dim(rgdwVendorVals),
rgdwInterfaceVals, dim(rgdwInterfaceVals),
rgdwTLCVals, dim(rgdwTLCVals),
pDriverSettingsUsed);
if (hKeyRet == NULL) {
DEBUGMSG(ZONE_COMMENT, (_T("%s: Could not find a suitable HID client driver\r\n"),
pszFname));
goto EXIT;
}
EXIT:
if (hKeyRoot != NULL) RegCloseKey(hKeyRoot);
return hKeyRet;
}
// Check this collection's registry key to see if remote wakeup needs
// to be enabled for this device.
static
void
SetRemoteWakeup(
PHID_CONTEXT pHidContext,
HKEY hKeyClient
)
{
SETFNAME(_T("SetRemoteWakeup"));
DWORD dwRemoteWakeup;
DWORD cbdwRemoteWakeup;
BOOL fEnable;
DWORD dwType;
INT iErr;
DWORD dwErr;
DEBUGCHK(pHidContext != NULL);
PREFAST_DEBUGCHK(hKeyClient != NULL);
// Check to see if we should enable remote wakeup for this device.
// If any TLC client wishes to be a remote wakeup source, the device
// will be set as a remote wakeup source.
cbdwRemoteWakeup = sizeof(dwRemoteWakeup);
iErr = RegQueryValueEx(hKeyClient, REMOTE_WAKEUP_VALUE_SZ, NULL, &dwType,
(PBYTE) &dwRemoteWakeup, &cbdwRemoteWakeup);
if ( (iErr == ERROR_SUCCESS) && (dwType == REG_DWORD) &&
(cbdwRemoteWakeup == sizeof(dwRemoteWakeup)) && (dwRemoteWakeup != 0) ) {
// Enable remote wakeup
fEnable = TRUE;
dwErr = HidPdd_IssueCommand(pHidContext->hPddDevice,
HID_PDD_REMOTE_WAKEUP, fEnable);
if (dwErr != ERROR_SUCCESS) {
DEBUGMSG(ZONE_WARNING,
(_T("%s: Unable to set device as remote wakeup source\r\n"),
pszFname));
}
}
}
// Check this collection's registry key to see if the idle rate needs
// to be adjusted.
static
void
SetIdleRate(
PHID_CONTEXT pHidContext,
HKEY hKeyClient,
DWORD dwCollection
)
{
SETFNAME(_T("SetIdleRate"));
DWORD dwDuration;
DWORD cbdwDuration;
DWORD dwType;
INT iErr;
DWORD dwIdx;
DWORD dwErr;
DEBUGCHK(pHidContext != NULL);
PREFAST_DEBUGCHK(hKeyClient != NULL);
// Check to see if we should set an idle rate for the device.
cbdwDuration = sizeof(dwDuration);
iErr = RegQueryValueEx(hKeyClient, IDLE_RATE_VALUE_SZ, NULL, &dwType,
(PBYTE) &dwDuration, &cbdwDuration);
if ( (iErr == ERROR_SUCCESS) && (dwType == REG_DWORD) &&
(cbdwDuration == sizeof(dwDuration)) ) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -