📄 hid.cpp
字号:
/*++
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.
Copyright (c) 1995-2000 Microsoft Corporation. All rights reserved.
Module Name:
hid.cpp
Abstract:
USB Human Interface Device (HID) class driver sample.
Functions:
Notes:
--*/
#define RETAIL_ZONE_HID 0
// There are four warnings that I like from Warning level 4. Since we build
// at warning level 3, I'm setting these four down to level 3 so I still get
// them.
// C4100 unrefrenced formal parameter
// C4101 unrefrenced local variable
// C4705 statement has no effect
// C4706 assignment in conditional
#pragma warning (3 : 4101 4705 4706)
#include <windows.h>
#include <windev.h>
#include <nkintr.h>
#include <usbdi.h>
#include "dinputce.h"
#include "keybd.hpp"
#include "hid.hpp"
#include "parse.hpp"
// The total configuration descriptor is of variable length. We'll first
// try the most likely length and then go back and redo it if that wasn't
// enough.
// Leave room for One configuration, one interfaces, one endpoints and a
// HID descriptor. That should minimize retries.
const UINT gcAssumedConfigurationDescriptorLength =
sizeof(USB_CONFIGURATION_DESCRIPTOR) +
sizeof(USB_INTERFACE_DESCRIPTOR) +
sizeof(USB_ENDPOINT_DESCRIPTOR) + sizeof(HID_DESCRIPTOR);
CHid *gpobHid;
/*++
*******************************************************************************
Hid Class Static Member Functions
*******************************************************************************
--*/
ULONG CALLBACK
CHid::HidThreadStub(
PVOID context)
{
CHid* pHid = (CHid*)context;
return (pHid->HidThread());
}
/*++
*******************************************************************************
Hid Class Member Functions
*******************************************************************************
--*/
CHid::CHid()
{
UINT device;
InitializeCriticalSection(&m_csDeviceLock);
m_bClosing = FALSE;
m_curMaxNumDevices = gcInitialMaxNumDevices;
m_ppDevices = new SHidDevice*[gcInitialMaxNumDevices];
for (device = 0; device < gcInitialMaxNumDevices; device++)
m_ppDevices[device] = NULL;
m_hEvent = NULL;
m_hThread = NULL;
m_curDevices = 0;
m_lpUsbFuncs = NULL;
// can't fail a constructor so activate this device in the Initialize method.
}
CHid::~CHid()
{
UINT device;
DWORD dwWaitReturn;
DeactivateDevice(m_hOSDevice); // inform the OS that we're going away
for (device = 0; device < m_curMaxNumDevices; device++) {
if (m_ppDevices[device] != NULL)
m_ppDevices[device]->bValid = FALSE;
}
// Now, when the thread wakes up it'll go through and remove all the
// devices.
m_bClosing = TRUE;
// Wake up the connection thread again and give it time to die.
if (m_hEvent != NULL) {
SetEvent(m_hEvent);
if (m_hThread != NULL) {
dwWaitReturn = WaitForSingleObject(m_hThread, 10000);
if (dwWaitReturn != WAIT_OBJECT_0) {
DEBUGMSG(ZONE_ERROR,(TEXT("!Timed out waiting for HID thread\r\n")));
TerminateThread(m_hThread, DWORD(-1));
}
m_hThread = NULL;
}
CloseHandle(m_hEvent);
m_hEvent = NULL;
}
DeleteCriticalSection(&m_csDeviceLock);
delete[] m_ppDevices;
}
BOOL
CHid::Initialize(LPCUSB_FUNCS lpUsbFuncs)
{
WCHAR const *key = TEXT("Drivers\\USB\\LoadClients\\Default\\Default\\3\\Generic_Sample_Hid_Class_Driver");
m_hOSDevice = ActivateDevice(key, 0);
if (m_hOSDevice == NULL) {
DEBUGMSG(ZONE_ERROR,
(TEXT("HID: Error on ActivateDevice(%s)\r\n"), key));
return (FALSE);
}
m_lpUsbFuncs = lpUsbFuncs;
m_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (m_hEvent == NULL) {
DEBUGMSG(ZONE_ERROR,
(TEXT("HID: Error on CreateEvent for connect event\r\n")));
return (FALSE);
}
m_hThread = CreateThread(0, 0, HidThreadStub, this, 0, NULL);
if (m_hThread == NULL) {
DEBUGMSG(ZONE_ERROR,
(TEXT("HID: Error on CreateThread\r\n")));
return (FALSE);
}
return (TRUE);
}
static inline DWORD MakeID (UINT idx)
{
static UINT ctr = 0;
++ctr;
return (LOBYTE(ctr) << 8) | LOBYTE(idx+1);
}
SHidDevice *
CHid::HidNewDevice(USB_HANDLE hDevice, // USB handle to the device
LPCUSB_INTERFACE lpInterface, // Pointer to the interface we are using
UCHAR bMaxPacketSize0) // Max size of transfers on EP0
{
UINT device;
BOOL bFoundSlot = FALSE;
SHidDevice *pstHidDevice;
PHID_DESCRIPTOR pHd;
LPCUSB_ENDPOINT_DESCRIPTOR pEd;
DEBUGMSG(ZONE_ATTACH || ZONE_FUNCTION,(TEXT("HidNewDevice, hDevice = 0x%X, MaxPacketSize0: %u\r\n"),hDevice,bMaxPacketSize0));
DEBUGMSG(ZONE_ATTACH,(TEXT("Interface: IFNum: %u, NumEP: %u, Class: 0x%X, SubClass: 0x%X, Protocol: 0x%X\r\n"),
lpInterface->Descriptor.bInterfaceNumber, lpInterface->Descriptor.bNumEndpoints,
lpInterface->Descriptor.bInterfaceClass,lpInterface->Descriptor.bInterfaceSubClass,
lpInterface->Descriptor.bInterfaceProtocol));
for (device = 0; device < m_curMaxNumDevices; device++) {
if (m_ppDevices[device] == NULL) {
bFoundSlot = TRUE;
break;
}
}
if (!bFoundSlot) {
UINT oldNumDevices;
SHidDevice **ppOldDevices;
// We've got more devices than we bargained for. Grow the device array
oldNumDevices = m_curMaxNumDevices;
ppOldDevices = m_ppDevices;
m_curMaxNumDevices += gcInitialMaxNumDevices;
DEBUGMSG(ZONE_WARNING,
(TEXT("HID: Growing the device array from %d to %d\r\n"),
oldNumDevices, m_curMaxNumDevices));
m_ppDevices = new SHidDevice*[m_curMaxNumDevices];
// First copy over the old devices
for (device = 0; device < oldNumDevices; device++) {
m_ppDevices[device] = ppOldDevices[device];
}
// Now NULL out the new spaces
for (/* already set */; device < m_curMaxNumDevices; device++) {
m_ppDevices[device] = NULL;
}
// Now delete the old space used.
delete[] ppOldDevices;
// Finally, we can be sure (having just allocated the space) that
// this spot is free.
device = oldNumDevices;
}
pstHidDevice = new SHidDevice;
if (pstHidDevice == NULL) {
DEBUGMSG(ZONE_ERROR, (TEXT("Error out of memory\r\n")));
goto Error;
}
memset(pstHidDevice,0,sizeof(SHidDevice));
// Device initialization
pstHidDevice->hDevice = hDevice;
pstHidDevice->configStatus = gcConfigStatusNewDevice;
pstHidDevice->signalStatus = gcSignalStatusNone;
pstHidDevice->dwRepeatDeadline = INFINITE;
pstHidDevice->intrBufferSize = 0;
pstHidDevice->bValid = TRUE;
InitializeCriticalSection(&pstHidDevice->csLock);
pstHidDevice->bMaxPacketSize = bMaxPacketSize0;
// HID devices should have a control endpoint (EP0) and an interrupt endpoint
if (lpInterface->lpEndpoints == NULL) {
DEBUGMSG(ZONE_ERROR,(TEXT("!CHid::HidNewDevice - missing endpoint descriptors\r\n")));
goto Error;
}
pEd = &lpInterface->lpEndpoints[0].Descriptor;
// HCD should guarantee this
ASSERT (pEd->bDescriptorType == USB_ENDPOINT_DESCRIPTOR_TYPE);
// Make sure endpoint is right type
if ((pEd->bmAttributes & USB_ENDPOINT_TYPE_MASK) != USB_ENDPOINT_TYPE_INTERRUPT ) {
DEBUGMSG(ZONE_ERROR,(TEXT("!CHid::HidNewDevice - 1st endpoint not interrupt (attr %u)\r\n"),
pEd->bmAttributes & USB_ENDPOINT_TYPE_MASK));
goto Error;
}
// Regarding bSendToInterface. 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.
pHd = (PHID_DESCRIPTOR) lpInterface->lpvExtended;
if (pHd == NULL) {
pHd = (PHID_DESCRIPTOR) lpInterface->lpEndpoints->lpvExtended;
if (pHd == NULL) {
DEBUGMSG(ZONE_ERROR,(TEXT("!CHid::HidNewDevice - missing HID descriptor\r\n")));
#ifdef OHCI_DESC_BUG_WORKAROUND
// But wait, there is a bug in the OHCI driver descriptor parsing logic where
// class specific descriptors are not indicated if they follow the EP descriptors.
// This code should be removed when we ship the fix to OHCI in SP2.
if (! GetConfigDesc(pstHidDevice,gcAssumedConfigurationDescriptorLength)) {
goto Error;
}
pstHidDevice->dwId = MakeID(device);
m_ppDevices[device] = pstHidDevice;
m_curDevices++;
return (pstHidDevice);
#else
goto Error;
#endif
}
else {
pstHidDevice->endptOrIntrNum = pEd->bEndpointAddress;
pstHidDevice->bSendToInterface = FALSE;
}
}
else {
pstHidDevice->endptOrIntrNum = lpInterface->Descriptor.bInterfaceNumber;
pstHidDevice->bSendToInterface = TRUE;
}
DEBUGMSG(ZONE_ATTACH,(TEXT("HID Descriptor: bLength: %u, bcdHID: 0x%X, bCountryCode: 0x%X\r\n"),pHd->bLength, pHd->bcdHID, pHd->bCountryCode));
DEBUGMSG(ZONE_ATTACH,(TEXT(" bNumDescriptors: %u, bClassDescriptorType: 0x%X, wDescriptorLength: %u\r\n"),
pHd->bNumDescriptors,pHd->bClassDescriptorType,pHd->wDescriptorLength));
pstHidDevice->wReportDescriptorLength = pHd->wDescriptorLength;
// Open interrupt pipe
pstHidDevice->hIntrPipe = m_lpUsbFuncs->lpOpenPipe(hDevice,pEd);
if (pstHidDevice->hIntrPipe == NULL) {
DEBUGMSG(ZONE_ERROR,(TEXT("!CHid::HidNewDevice - Error %u opening interrupt pipe for device\r\n"),GetLastError()));
goto Error;
}
pstHidDevice->dwId = MakeID(device);
m_ppDevices[device] = pstHidDevice;
m_curDevices++;
DEBUGMSG(ZONE_CONFIG,(TEXT("HID: Dev 0x%X state: GettingReport\r\n"),pstHidDevice));
if (! GetReport(pstHidDevice)) {
goto Error;
}
return (pstHidDevice);
Error:
RETAILMSG(1,(TEXT("!CHid::HidNewDevice - Error setting up new HID device\r\n")));
if (pstHidDevice) {
if (pstHidDevice->hIntrPipe)
m_lpUsbFuncs->lpClosePipe(pstHidDevice->hIntrPipe);
delete pstHidDevice;
}
return NULL;
}
BOOL
CHid::HidCloseDevice(
SHidDevice *pHidDevice)
{
DEBUGMSG(ZONE_ATTACH || ZONE_FUNCTION,
(TEXT("HidCloseDevice, pHidDevice = 0x%X\r\n"), pHidDevice));
ASSERT(m_curDevices);
m_curDevices--;
if (pHidDevice->hIntrPipe) {
m_lpUsbFuncs->lpClosePipe(pHidDevice->hIntrPipe);
pHidDevice->hIntrPipe = NULL;
}
// To keep this thread safe without using critical sections,
// mark him as invalid and let the main thread handle it.
pHidDevice->bValid = FALSE;
SetEvent(m_hEvent);
return (TRUE);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -