📄 usbhid.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:
usbhid.cpp
Abstract:
USB Client Driver for Human Interface Device (HID) Class.
Functions:
Notes:
--*/
#include "usbhid.h"
#include <devload.h>
#define USB_HID_MAX_REPORT_LENGTH 0xFFFF // HID standard allows 2-byte transfer size
/*
* DLL entry point
*/
extern "C"
BOOL
DllEntry(
HANDLE hDllHandle,
DWORD dwReason,
LPVOID lpReserved
)
{
UNREFERENCED_PARAMETER(lpReserved);
switch (dwReason) {
case DLL_PROCESS_ATTACH:
DEBUGREGISTER((HINSTANCE)hDllHandle);
DEBUGMSG(ZONE_INIT, (_T("Hid DllEntry Attach\r\n")));
DisableThreadLibraryCalls((HMODULE) hDllHandle);
break;
case DLL_PROCESS_DETACH:
DEBUGMSG(ZONE_INIT, (_T("Hid DllEntry Detach\r\n")));
break;
}
return HidMdd_DllEntry(dwReason);
}
/*
* USBInstallDriver
*
* USB driver install routine - set up client registry settings so we will be loaded
* by USBD for HID devices. This function should not be called for systems in which the
* OEM ships the HID driver.
*
* Return value:
* Return TRUE if install succeeds, or FALSE if there is some error.
*/
extern "C"
BOOL
USBInstallDriver(
LPCWSTR szDriverLibFile // Contains client driver DLL name
)
{
SETFNAME(_T("USBInstallDriver"));
BOOL fRet = FALSE;
USB_DRIVER_SETTINGS usbDriverSettings = { DRIVER_SETTINGS };
REG_VALUE_DESCR rgUsbHidInstanceValues[] = {
DEVLOAD_DLLNAME_VALNAME, DEVLOAD_DLLNAME_VALTYPE, 0, (PBYTE)(DRIVER_NAME),
NULL, 0, 0, NULL
};
DWORD dwQueuedTransfers = DEFAULT_QUEUED_TRANSFERS;
REG_VALUE_DESCR rgUsbHidPublicValues[] = {
DEVLOAD_DLLNAME_VALNAME, DEVLOAD_DLLNAME_VALTYPE, 0, (PBYTE)(DRIVER_NAME),
DEVLOAD_PREFIX_VALNAME, DEVLOAD_PREFIX_VALTYPE, 0, (PBYTE)(DEVICE_PREFIX),
QUEUED_TRANSFERS_SZ, REG_DWORD, 0, (PBYTE)(&dwQueuedTransfers),
NULL, 0, 0, NULL
};
DEBUGCHK(szDriverLibFile != NULL);
DEBUGMSG(ZONE_INIT, (_T("%s: Install function called (driver: %s)\r\n"),
pszFname, szDriverLibFile));
// register with USBD
fRet = RegisterClientDriverID(CLASS_NAME_SZ);
if (fRet == FALSE) {
DEBUGMSG(ZONE_ERROR, (_T("%s: RegisterClientDriverID error:%d\r\n"),
pszFname, GetLastError()));
goto EXIT;
}
fRet = RegisterClientSettings( szDriverLibFile,
CLASS_NAME_SZ,
NULL,
&usbDriverSettings );
if (fRet == FALSE) {
UnRegisterClientDriverID(CLASS_NAME_SZ);
DEBUGMSG(ZONE_ERROR, (_T("%s: RegisterClientSettings error:%d\r\n"),
pszFname, GetLastError()));
goto EXIT;
}
// Add our default values to the reg
if ( !GetSetKeyValues( HID_REGKEY_SZ,
rgUsbHidInstanceValues,
SET,
TRUE ) ) {
DEBUGMSG(ZONE_ERROR, (_T("%s: GetSetKeyValues failed!\r\n"), pszFname));
goto EXIT;
}
if ( !GetSetKeyValues( CLIENT_REGKEY_SZ,
rgUsbHidPublicValues,
SET,
TRUE ) ) {
DEBUGMSG(ZONE_ERROR, (_T("%s: GetSetKeyValues failed!\r\n"), pszFname));
goto EXIT;
}
fRet = TRUE;
EXIT:
return fRet;
}
/*
* USBUnInstallDriver
*
* This function can be called by a client driver to deregister itself
* with USBD.
*/
extern "C"
BOOL
USBUnInstallDriver(
VOID
)
{
SETFNAME(_T("USBUnInstallDriver"));
BOOL fRet = FALSE;
const WCHAR szUsbDeviceID[] = CLASS_NAME_SZ;
USB_DRIVER_SETTINGS usbDriverSettings = { DRIVER_SETTINGS };
DEBUGMSG(ZONE_INIT, (_T("%s: Uninstall function called\r\n"), pszFname));
fRet = UnRegisterClientSettings( szUsbDeviceID,
NULL,
&usbDriverSettings );
if (fRet == TRUE) {
fRet = UnRegisterClientDriverID(szUsbDeviceID);
}
return fRet;
}
/*
* USBDeviceNotifications
*
* Process notifications from USBD. Currently, the only notification
* supported is a device removal. Unused parameters are reserved for
* future use.
*/
extern "C"
BOOL
USBDeviceNotifications(
LPVOID lpvNotifyParameter,
DWORD dwCode,
LPDWORD * dwInfo1,
LPDWORD * dwInfo2,
LPDWORD * dwInfo3,
LPDWORD * dwInfo4
)
{
SETFNAME(_T("USBDeviceNotifications"));
BOOL fRet = FALSE;
PUSBHID_CONTEXT pUsbHid = (PUSBHID_CONTEXT) lpvNotifyParameter;
UNREFERENCED_PARAMETER(dwInfo1);
UNREFERENCED_PARAMETER(dwInfo2);
UNREFERENCED_PARAMETER(dwInfo3);
UNREFERENCED_PARAMETER(dwInfo4);
DEBUGCHK(pUsbHid != NULL);
ValidateUsbHidContext(pUsbHid);
switch(dwCode)
{
case USB_CLOSE_DEVICE:
DEBUGMSG(ZONE_INIT, (_T("%s: USB_CLOSE_DEVICE\r\n"), pszFname));
// Remove and free the device context;
RemoveDeviceContext(pUsbHid);
fRet = TRUE;
break;
default:
DEBUGMSG(ZONE_ERROR, (_T("%s: Unhandled code:%d\n"),
pszFname, dwCode));
break;
}
return fRet;
}
/*
* USBDeviceAttach
*
* USB device attach routine. This function is called by USBD when a device is attached
* to the USB, and a matching registry key is found off the LoadClients registry key.
* We must determine whether the device may be controlled by this driver, and load
* drivers for any uncontrolled interfaces.
*
* Return Value:
* Return TRUE upon success, or FALSE if an error occurs.
*/
extern "C"
BOOL
USBDeviceAttach(
USB_HANDLE hDevice, // USB device handle
LPCUSB_FUNCS UsbFuncs, // Pointer to USBDI function table.
LPCUSB_INTERFACE UsbInterface,// If client is being loaded as an interface driver, contains a pointer to the USB_INTERFACE
// structure that contains interface information. If client is not loaded for a specific interface,
// this parameter will be NULL.
LPCWSTR UniqueDriverId, // Contains client driver id string.
LPBOOL AcceptControl, // Filled in with TRUE if we accept control of the device,
// or FALSE if USBD should continue to try to load client drivers.
LPCUSB_DRIVER_SETTINGS UsbDriverSettings,// Contains pointer to USB_DRIVER_SETTINGS struct that indicates how we were loaded.
DWORD Unused // Reserved for use with future versions of USBD
)
{
SETFNAME(_T("USBDeviceAttach"));
BOOL fRet = FALSE;
LPCUSB_INTERFACE pUsbInterface = NULL;
PUSBHID_CONTEXT pUsbHid = NULL;
DEBUGMSG(ZONE_INIT, (_T("+%s: 0x%x, %s\r\n"), pszFname, hDevice,
UniqueDriverId));
UNREFERENCED_PARAMETER(UniqueDriverId);
UNREFERENCED_PARAMETER(UsbDriverSettings);
UNREFERENCED_PARAMETER(Unused);
PREFAST_DEBUGCHK(UsbFuncs != NULL);
DEBUGCHK(UniqueDriverId != NULL);
PREFAST_DEBUGCHK(AcceptControl != NULL);
DEBUGCHK(UsbDriverSettings != NULL);
// Determine if we control this USB peripheral...
*AcceptControl = FALSE;
pUsbInterface = ParseUsbDescriptors(UsbInterface);
if (pUsbInterface == NULL) {
goto EXIT;
}
// Tell USBD to stop looking for drivers
*AcceptControl = TRUE;
// We found a device and interface we control, so create our device context.
pUsbHid = CreateUsbHidDevice(hDevice, UsbFuncs, pUsbInterface);
if (pUsbHid == NULL) {
goto EXIT;
}
// Register to receive notifications regarding this device from USB.
(*UsbFuncs->lpRegisterNotificationRoutine)(hDevice,
USBDeviceNotifications, pUsbHid);
fRet = TRUE;
EXIT:
if (fRet == FALSE) {
if (pUsbHid != NULL) {
RemoveDeviceContext(pUsbHid);
}
}
DEBUGMSG(ZONE_INIT, (_T("-%s\r\n"), pszFname));
return fRet;
}
// Clear the stall feature on endpoint 0.
static
DWORD
ClearEndpointZeroStall(
PUSBHID_CONTEXT pUsbHid
)
{
DWORD dwErr;
PREFAST_DEBUGCHK(pUsbHid != NULL);
dwErr = ClearOrSetFeature(
pUsbHid->pUsbFuncs,
pUsbHid->hUsbDevice,
NULL,
NULL,
USB_SEND_TO_ENDPOINT,
USB_FEATURE_ENDPOINT_STALL,
0, // Endpoint 0
0,
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
)
{
PREFAST_DEBUGCHK(pUsbHid != NULL);
PREFAST_DEBUGCHK(pbmRequestType != NULL);
PREFAST_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 an Input, Output, or Feature report from the device. This is not to be
// be used for recurring interrupt reports. The PDD calls into the MDD with
// HidMdd_ProcessReport for that.
DWORD
WINAPI
HidPdd_GetReport(
HID_PDD_HANDLE hPddDevice,
HIDP_REPORT_TYPE type,
PCHAR pbBuffer,
DWORD cbBuffer,
PDWORD pcbTransferred,
DWORD dwTimeout,
BYTE bReportID
)
{
SETFNAME(_T("HidPdd_GetReport"));
PUSBHID_CONTEXT pUsbHid = (PUSBHID_CONTEXT) hPddDevice;
USB_DEVICE_REQUEST udr;
DWORD dwErr = ERROR_SUCCESS;
LPTRANSFER_NOTIFY_ROUTINE pfnNotify = NULL;
HANDLE hEvent = NULL;
USB_ERROR usbErr;
PREFAST_DEBUGCHK(VALID_CONTEXT(pUsbHid));
ValidateUsbHidContext(pUsbHid);
// Mdd guarantees the following
DEBUGCHK(pbBuffer != NULL);
DEBUGCHK(pcbTransferred != NULL);
DEBUGCHK(IS_HID_REPORT_TYPE_VALID(type) == TRUE);
if (cbBuffer > USB_HID_MAX_REPORT_LENGTH) {
// Reduce to maximum possible value.
cbBuffer = USB_HID_MAX_REPORT_LENGTH;
}
if (dwTimeout != INFINITE) {
// Set up the wait variables.
hEvent = CreateEvent(NULL, MANUAL_RESET_EVENT, FALSE, NULL);
if (hEvent == NULL) {
dwErr = GetLastError();
DEBUGMSG(ZONE_ERROR, (_T("%s: CreateEvent error:%d\r\n"),
pszFname, dwErr));
goto EXIT;
}
pfnNotify = DefaultTransferComplete;
if (dwTimeout == 0) {
// IssueVendorTransfer has a special case for 0 so change it to 1.
dwTimeout = 1;
}
}
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 = USB_DESCRIPTOR_MAKE_TYPE_AND_INDEX(type, bReportID);
udr.wLength = (USHORT) cbBuffer;
dwErr = IssueVendorTransfer(
pUsbHid->pUsbFuncs,
pUsbHid->hUsbDevice,
pfnNotify,
hEvent,
USB_OUT_TRANSFER,
&udr,
pbBuffer,
0,
pcbTransferred,
dwTimeout,
&usbErr);
if (dwErr == ERROR_SUCCESS) {
if (usbErr == USB_STALL_ERROR) {
// GET_REPORT is not required according to the HID spec. If it is not
// present, the device may return a stall handshake.
dwErr = ERROR_NOT_SUPPORTED;
}
else if (usbErr != USB_NO_ERROR) {
dwErr = ERROR_GEN_FAILURE;
}
}
EXIT:
if (hEvent != NULL) CloseHandle(hEvent);
return dwErr;
}
// Set an Input, Output, or Feature report on the device.
DWORD
WINAPI
HidPdd_SetReport(
HID_PDD_HANDLE hPddDevice,
HIDP_REPORT_TYPE type,
PCHAR pbBuffer,
DWORD cbBuffer,
DWORD dwTimeout,
BYTE bReportID
)
{
SETFNAME(_T("HidPdd_SetReport"));
PUSBHID_CONTEXT pUsbHid = (PUSBHID_CONTEXT) hPddDevice;
USB_DEVICE_REQUEST udr;
DWORD dwBytesTransferred;
DWORD dwErr = ERROR_SUCCESS;
LPTRANSFER_NOTIFY_ROUTINE pfnNotify = NULL;
HANDLE hEvent = NULL;
USB_ERROR usbErr;
PREFAST_DEBUGCHK(VALID_CONTEXT(pUsbHid));
ValidateUsbHidContext(pUsbHid);
// Mdd guarantees the following
DEBUGCHK(pbBuffer != NULL);
DEBUGCHK(IS_HID_REPORT_TYPE_VALID(type) == TRUE);
if (cbBuffer > USB_HID_MAX_REPORT_LENGTH) {
DEBUGMSG(ZONE_ERROR, (_T("%s: Buffer is too large\r\n"), pszFname));
dwErr = ERROR_MESSAGE_EXCEEDS_MAX_SIZE;
goto EXIT;
}
if (dwTimeout != INFINITE) {
// Set up the wait variables.
hEvent = CreateEvent(NULL, MANUAL_RESET_EVENT, FALSE, NULL);
if (hEvent == NULL) {
dwErr = GetLastError();
DEBUGMSG(ZONE_ERROR, (_T("%s: CreateEvent error:%d\r\n"),
pszFname, dwErr));
goto EXIT;
}
pfnNotify = DefaultTransferComplete;
if (dwTimeout == 0) {
// IssueVendorTransfer has a special case for 0 so change it to 1.
dwTimeout = 1;
}
}
DetermineDestination(pUsbHid, &udr.bmRequestType, &udr.wIndex);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -