|
|
/*****************************************************************************
* * DIHidEnm.c * * Copyright (c) 1996 Microsoft Corporation. All Rights Reserved. * * Abstract: * * Support functions for HID enumeration. * * Contents: * * DIHid_BuildHidList * *****************************************************************************/
#include "dinputpr.h"
/*****************************************************************************
* * The sqiffle for this file. * *****************************************************************************/
#define sqfl sqflHid
/*****************************************************************************
* * @doc INTERNAL * * @global PHIDDEVICELIST | g_hdl | * * List of known HID devices. * * @global DWORD | g_tmLastHIDRebuild | * * The time we last rebuilt the HID list. Zero means that the * HID list has never been rebuilt. Watch out for wraparound; * a 32-bit value rolls over after about 30 days. * *****************************************************************************/
#define MSREBUILDRATE 20000 /* Twenty seconds */
#define MSREBUILDRATE_FIFTH 5000 /* Two seconds */
PHIDDEVICELIST g_phdl; DWORD g_tmLastHIDRebuild;
TCHAR g_tszIdLastRemoved[MAX_PATH]; DWORD g_tmLastRemoved = 0; TCHAR g_tszIdLastUnknown[MAX_PATH]; DWORD g_tmLastUnknown = 0;
#pragma BEGIN_CONST_DATA
/*****************************************************************************
* * @doc INTERNAL * * @func PHIDDEVICEINFO | phdiFindHIDInstanceGUID | * * Locates information given an instance GUID for a HID device. * * The parameters have already been validated. * * The DLL critical must be held across the call; once the * critical section is released, the returned pointer becomes * invalid. * * @parm IN PCGUID | pguid | * * The instance GUID to be located. * * @returns * * Pointer to the <t HIDDEVICEINFO> that describes * the device. * *****************************************************************************/
PHIDDEVICEINFO EXTERNAL phdiFindHIDInstanceGUID(PCGUID pguid) { PHIDDEVICEINFO phdi;
AssertF(InCrit());
if(g_phdl) { int ihdi;
for(ihdi = 0, phdi = g_phdl->rghdi; ihdi < g_phdl->chdi; ihdi++, phdi++) { if(IsEqualGUID(pguid, &phdi->guid) ) { goto done; } } /*
* Memphis Bug#68994. App does not detect USB device. * App was using product guid. * Fix: We allow match to HID guid, if product guid is specfied */ for(ihdi = 0, phdi = g_phdl->rghdi; ihdi < g_phdl->chdi; ihdi++, phdi++) { if(IsEqualGUID(pguid, &phdi->guidProduct) ) { RPF("Warning: Use instance GUID (NOT product GUID) to refer to a device."); goto done; } }
#ifdef WINNT
/*
* NT Bug#351951. * If they are directly asking for one of the predefined joystick * IDs then see if we have a device mapped to that ID. If so, * pretend they asked for that GUID instead. */
/*
* Weakly Assert the range of predefined static joystick instance GUIDs */ AssertF( ( rgGUID_Joystick[0].Data1 & 0x0f ) == 0 ); AssertF( ( rgGUID_Joystick[0x0f].Data1 & 0x0f ) == 0x0f );
/*
* Check the GUID is the same as the first static one ignoring LS 4 bits */ if( ( (pguid->Data1 & 0xf0) == (rgGUID_Joystick[0].Data1 & 0xf0) ) && !memcmp( ((PBYTE)&rgGUID_Joystick)+1, ((PBYTE)pguid)+1, sizeof(*pguid) - 1 ) ) { RPF("Using predefined instance GUIDs is bad and should not work!"); phdi = phdiFindJoyId( pguid->Data1 & 0x0f ); goto done; } #endif
} phdi = 0;
done:;
return phdi; }
/*****************************************************************************
* * @doc INTERNAL * * @func HRESULT | hresFindHIDInstanceGUID | * * Locates information given an instance GUID for a HID device. * * The parameters have already been validated. * * @parm IN PCGUID | pguid | * * The instance GUID to be located. * * @parm OUT CREATEDCB * | pcdcb | * * Receives pointer to the <f CreateDcb> function for the object. * *****************************************************************************/
STDMETHODIMP hresFindHIDInstanceGUID(PCGUID pguid, CREATEDCB *pcdcb) { HRESULT hres; PHIDDEVICEINFO phdi; EnterProc(hresFindHIDInstanceGUID, (_ "G", pguid));
AssertF(SUCCEEDED(hresFullValidGuid(pguid, 0)));
DllEnterCrit();
phdi = phdiFindHIDInstanceGUID(pguid); if(phdi) { *pcdcb = CHid_New; hres = S_OK; } else { hres = DIERR_DEVICENOTREG; }
DllLeaveCrit();
/*
* Don't use ExitOleProcPpv because that will validate that * *pcdcb == 0 if FAILED(hres), but that's not our job. */ ExitOleProc();
return hres; }
/*****************************************************************************
* * @doc INTERNAL * * @func PHIDDEVICEINFO | phdiFindHIDDeviceId | * * Locates information given a deviceID * (in other words, <enumerator>\<enumerator-specific-device-ID>) for a HID device. * * The parameters have already been validated. * * The DLL critical must be held across the call; once the * critical section is released, the returned pointer becomes * invalid. * * @parm IN LPCTSTR | ptszId | * * The interface device to be located. * * @returns * * Pointer to the <t HIDDEVICEINFO> that describes * the device. * *****************************************************************************/
PHIDDEVICEINFO EXTERNAL phdiFindHIDDeviceId(LPCTSTR ptszId) { PHIDDEVICEINFO phdi;
AssertF(InCrit());
if(g_phdl) { int ihdi;
for(ihdi = 0, phdi = g_phdl->rghdi; ihdi < g_phdl->chdi; ihdi++, phdi++) { if(phdi->pdidd && lstrcmpi(phdi->ptszId, ptszId) == 0) { goto done; } } } phdi = 0;
done:;
return phdi; }
/*****************************************************************************
* * @doc INTERNAL * * @func PHIDDEVICEINFO | phdiFindHIDDeviceInterface | * * Locates information given a device interface * (in other words, a \\.\... thing) for a HID device. * * The parameters have already been validated. * * The DLL critical must be held across the call; once the * critical section is released, the returned pointer becomes * invalid. * * @parm IN LPCTSTR | ptszPath | * * The interface device to be located. * * @returns * * Pointer to the <t HIDDEVICEINFO> that describes * the device. * *****************************************************************************/
PHIDDEVICEINFO EXTERNAL phdiFindHIDDeviceInterface(LPCTSTR ptszPath) { PHIDDEVICEINFO phdi;
AssertF(InCrit());
if(g_phdl) { int ihdi;
for(ihdi = 0, phdi = g_phdl->rghdi; ihdi < g_phdl->chdi; ihdi++, phdi++) { if(phdi->pdidd && lstrcmpi(phdi->pdidd->DevicePath, ptszPath) == 0) { goto done; } } } phdi = 0;
done:;
return phdi; }
/*****************************************************************************
* * @doc INTERNAL * * @func HRESULT | hresFindHIDDeviceInterface | * * Locates information given a device interface * (in other words, a \\.\... thing) for a HID device. * * The parameters have already been validated. * * @parm IN LPCTSTR | ptszPath | * * The interface device to be located. * * @parm OUT LPGUID | pguidOut | * * Receives the instance GUID of the device found. * *****************************************************************************/
STDMETHODIMP hresFindHIDDeviceInterface(LPCTSTR ptszPath, LPGUID pguidOut) { HRESULT hres; PHIDDEVICEINFO phdi; EnterProc(hresFindHIDDeviceInterface, (_ "s", ptszPath));
DllEnterCrit();
phdi = phdiFindHIDDeviceInterface(ptszPath);
if(phdi) { *pguidOut = phdi->guid; hres = S_OK; } else { hres = DIERR_DEVICENOTREG; }
DllLeaveCrit();
ExitOleProc();
return hres; }
/*****************************************************************************
* * @doc INTERNAL * * @func void | DIHid_ProbeMouse | * * That this function exists at all is a total hack to work * around bugs in Memphis and NT5. * * If you call GetSystemMetrics(SM_WHEELPRESENT) or * GetSystemMetrics(SM_MOUSEBUTTONS), USER32 does not * return the correct values if your HID mouse is * not the same as your PS/2 mouse (if any). * * For example, if your PS/2 mouse is a regular two-button * mouse but your HID mouse is a wheel mouse, GetSystemMetrics * will still say "No wheel, 2 buttons" even though it's wrong. * * So what we have to do is wander through all the HID mice in * the system and record the number of buttons they have, * and whether they have a wheel. * * That way, when we create a system mouse, we can take the * maximum of every supported device. * *****************************************************************************/
void INTERNAL DIHid_ProbeMouse(PHIDDEVICEINFO phdi, PHIDP_CAPS pcaps, PHIDP_PREPARSED_DATA ppd) { LPVOID pvReport; HRESULT hres;
/*
* Get the number of buttons in the generic button page. * This is the only page the MOUHID uses. */ phdi->osd.uiButtons = HidP_MaxUsageListLength(HidP_Input, HID_USAGE_PAGE_BUTTON, ppd);
/*
* See if there is a HID_USAGE_GENERIC_WHEEL. * This is the way that MOUHID detects a wheel. */ hres = AllocCbPpv(pcaps->InputReportByteLength, &pvReport); if(SUCCEEDED(hres)) { ULONG ul; NTSTATUS stat;
stat = HidP_GetUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_WHEEL, &ul, ppd, pvReport, pcaps->InputReportByteLength); if(SUCCEEDED(stat)) { phdi->osd.uiAxes = 3; }
FreePv(pvReport); } }
/*****************************************************************************
* * @doc INTERNAL * * @func void | DIHid_ParseUsagePage | * * ISSUE-2001/02/06-TimGill Enumeration needs to know device type * Parse the usage page information and create a first pass at DI * style type information. * This really is not enough information to be useful as * enumeration needs to know the final device type. * *****************************************************************************/
void INTERNAL DIHid_ParseUsagePage(PHIDDEVICEINFO phdi, PHIDP_CAPS pcaps, PHIDP_PREPARSED_DATA ppd)
{ switch(pcaps->UsagePage) { case HID_USAGE_PAGE_GENERIC: switch(pcaps->Usage) {
/*
* MouHID accepts either HID_USAGE_GENERIC_MOUSE or * HID_USAGE_GENERIC_POINTER, so we will do the same. */ case HID_USAGE_GENERIC_MOUSE: case HID_USAGE_GENERIC_POINTER: DIHid_ProbeMouse(phdi, pcaps, ppd); phdi->osd.dwDevType = MAKE_DIDEVICE_TYPE(DI8DEVTYPE_MOUSE, DI8DEVTYPEMOUSE_UNKNOWN) | DIDEVTYPE_HID; break;
case HID_USAGE_GENERIC_JOYSTICK: phdi->osd.dwDevType = MAKE_DIDEVICE_TYPE(DI8DEVTYPE_JOYSTICK, DI8DEVTYPEJOYSTICK_STANDARD) | DIDEVTYPE_HID; break;
case HID_USAGE_GENERIC_GAMEPAD: phdi->osd.dwDevType = MAKE_DIDEVICE_TYPE(DI8DEVTYPE_GAMEPAD, DI8DEVTYPEGAMEPAD_STANDARD) | DIDEVTYPE_HID; break;
case HID_USAGE_GENERIC_KEYBOARD: phdi->osd.dwDevType = MAKE_DIDEVICE_TYPE(DI8DEVTYPE_KEYBOARD, DI8DEVTYPEKEYBOARD_UNKNOWN) | DIDEVTYPE_HID; break;
default: phdi->osd.dwDevType = DI8DEVTYPE_DEVICE | DIDEVTYPE_HID; break; } break;
default: phdi->osd.dwDevType = DI8DEVTYPE_DEVICE | DIDEVTYPE_HID; break; } }
/*****************************************************************************
* * @doc INTERNAL * * @func BOOL | DIHid_GetDevicePath | * * Obtain the path for the device. This is a simple wrapper * function to keep DIHid_BuildHidListEntry from getting too * annoying. * * This also gets the devinfo so we can get the * instance ID string for subsequent use to get the * friendly name, etc. * *****************************************************************************/
BOOL EXTERNAL DIHid_GetDevicePath(HDEVINFO hdev, PSP_DEVICE_INTERFACE_DATA pdid, PSP_DEVICE_INTERFACE_DETAIL_DATA *ppdidd, OPTIONAL PSP_DEVINFO_DATA pdinf) { HRESULT hres; BOOL fRc; DWORD cbRequired; EnterProcI(DIHid_GetDevicePath, (_ "xp", hdev, pdid));
AssertF(*ppdidd == 0);
/*
* Ask for the required size then allocate it then fill it. * * Note that we don't need to free the memory on the failure * path; our caller will do the necessary memory freeing. * * Sigh. Windows NT and Windows 98 implement * SetupDiGetDeviceInterfaceDetail differently if you are * querying for the buffer size. * * Windows 98 returns FALSE, and GetLastError() returns * ERROR_INSUFFICIENT_BUFFER. * * Windows NT returns TRUE. * * So we allow the cases either where the call succeeds or * the call fails with ERROR_INSUFFICIENT_BUFFER. */ if(SetupDiGetDeviceInterfaceDetail(hdev, pdid, 0, 0, &cbRequired, 0) || GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
hres = AllocCbPpv(cbRequired, ppdidd);
// Keep prefix happy, manbug 29341
if(SUCCEEDED(hres) && ( *ppdidd != NULL) ) { PSP_DEVICE_INTERFACE_DETAIL_DATA pdidd = *ppdidd;
/*
* Note, The cbSize field always contains the size of the fixed * part of the data structure, not a size reflecting the * variable-length string at the end. */
pdidd->cbSize = cbX(SP_DEVICE_INTERFACE_DETAIL_DATA);
fRc = SetupDiGetDeviceInterfaceDetail(hdev, pdid, pdidd, cbRequired, &cbRequired, pdinf);
if(!fRc) { FreePpv(ppdidd);
/*
* Set fRc = FALSE again, so the compiler doesn't need * to blow a register to cache the value zero. */ fRc = FALSE; } } else { fRc = FALSE; } } else { SquirtSqflPtszV(sqfl | sqflError, TEXT("%hs: SetupDiGetDeviceInterfaceDetail failed 1, ") TEXT("Error = %d"), s_szProc, GetLastError()); fRc = FALSE; }
ExitProcF(fRc); return fRc; }
/*****************************************************************************
* * @doc INTERNAL * * @func BOOL | DIHid_GetDeviceInstanceId | * * Obtain the instance ID for the device. * * The instance ID allows us to get access to device * properties later. * *****************************************************************************/
BOOL EXTERNAL DIHid_GetDeviceInstanceId(HDEVINFO hdev, PSP_DEVINFO_DATA pdinf, LPTSTR *pptszId) { BOOL fRc; DWORD ctchRequired;
AssertF(*pptszId == 0);
/*
* Ask for the required size then allocate it then fill it. * * Note that we don't need to free the memory on the failure * path; our caller will do the necessary memory freeing. */ if(SetupDiGetDeviceInstanceId(hdev, pdinf, NULL, 0, &ctchRequired) == 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER) { HRESULT hres;
hres = AllocCbPpv(cbCtch(ctchRequired), pptszId);
if(SUCCEEDED(hres)) { fRc = SetupDiGetDeviceInstanceId(hdev, pdinf, *pptszId, ctchRequired, NULL); } else { fRc = FALSE; } } else { fRc = FALSE; } return fRc; }
/*****************************************************************************
* * @doc INTERNAL * * @func BOOL | DIHid_GetInstanceGUID | * * Read the instance GUID from the registry, or create one if * necessary. * *****************************************************************************/
BOOL INTERNAL DIHid_GetInstanceGUID(HKEY hk, LPGUID pguid) { LONG lRc; DWORD cb;
cb = cbX(GUID); lRc = RegQueryValueEx(hk, TEXT("GUID"), 0, 0, (PV)pguid, &cb);
if(lRc != ERROR_SUCCESS) { DICreateGuid(pguid); lRc = RegSetValueEx(hk, TEXT("GUID"), 0, REG_BINARY, (PV)pguid, cbX(GUID)); }
return lRc == ERROR_SUCCESS; }
/*****************************************************************************
* * @doc INTERNAL * * @func void | DIHid_EmptyHidList | * * Empty the list of HID devices. * * This function must be called under the DLL critical section. * *****************************************************************************/
void EXTERNAL DIHid_EmptyHidList(void) { AssertF(InCrit());
/*
* Free all the old strings and things in the HIDDEVICEINFO's. */ if(g_phdl) { int ihdi;
for(ihdi = 0; ihdi < g_phdl->chdi; ihdi++) { FreePpv(&g_phdl->rghdi[ihdi].pdidd); FreePpv(&g_phdl->rghdi[ihdi].ptszId); if(g_phdl->rghdi[ihdi].hk) { RegCloseKey(g_phdl->rghdi[ihdi].hk); } }
/*
* We invalidated all the pointers, so make sure * nobody looks at them. */ g_phdl->chdi = 0; }
}
/*****************************************************************************
* * @doc INTERNAL * * @func void | DIHid_CheckHidList | * * Check the list of HID devices, and free whose what can not be opened * * This function must be called under the DLL critical section. * *****************************************************************************/
void INTERNAL DIHid_CheckHidList(void) { HANDLE hf;
EnterProcI(DIHid_CheckList, (_ "x", 0x0) );
AssertF(InCrit());
/*
* Free all the information of the device cannot be opened */ if(g_phdl) { int ihdi;
for(ihdi = 0, g_phdl->chdi = 0; ihdi < g_phdl->chdiAlloc; ihdi++) {
if( g_phdl->rghdi[ihdi].pdidd ) {
/*
* Open the device */ hf = CreateFile(g_phdl->rghdi[ihdi].pdidd->DevicePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, /* no SECURITY_ATTRIBUTES */ OPEN_EXISTING, 0, /* attributes */ 0); /* template */
if(hf == INVALID_HANDLE_VALUE) { #if 0
PHIDDEVICEINFO phdi; PBUSDEVICEINFO pbdi;
CloseHandle(hf); phdi = &g_phdl->rghdi[ihdi]; DllEnterCrit();
phdi = phdiFindHIDDeviceInterface(g_phdl->rghdi[ihdi].pdidd->DevicePath); AssertF(phdi != NULL); pbdi = pbdiFromphdi(phdi);
DllLeaveCrit();
if( pbdi != NULL ) { if( pbdi->fDeleteIfNotConnected == TRUE ) { lstrcpy( g_tszIdLastRemoved, pbdi->ptszId ); g_tmLastRemoved = GetTickCount();
DIBusDevice_Remove(pbdi); pbdi->fDeleteIfNotConnected = FALSE; } } #endif
FreePpv(&g_phdl->rghdi[ihdi].pdidd); FreePpv(&g_phdl->rghdi[ihdi].ptszId);
if(g_phdl->rghdi[ihdi].hk) { RegCloseKey(g_phdl->rghdi[ihdi].hk); } ZeroX( g_phdl->rghdi[ihdi] );
SquirtSqflPtszV(sqfl | sqflError, TEXT("%hs: CreateFile(%s) failed? le=%d"), s_szProc, g_phdl->rghdi[ihdi].pdidd->DevicePath, GetLastError()); /* Skip erroneous items */
} else { CloseHandle(hf); g_phdl->chdi++; } } }
//re-order the existing devices, put them at the front of the hid list.
for(ihdi = 0; ihdi < g_phdl->chdi; ihdi++) { if( !g_phdl->rghdi[ihdi].pdidd ) { int ihdi2;
//find the existing device from the biggest index in the hid list.
for( ihdi2 = g_phdl->chdiAlloc; ihdi2 >= ihdi+1; ihdi2-- ) { if( g_phdl->rghdi[ihdi2].pdidd ) { memcpy( &g_phdl->rghdi[ihdi], &g_phdl->rghdi[ihdi2], sizeof(HIDDEVICEINFO) ); ZeroX( g_phdl->rghdi[ihdi2] ); } } } }
}
ExitProc(); return; }
/*****************************************************************************
* * @doc INTERNAL * * @func HRESULT | DIHid_CreateDeviceInstanceKeys | * * Create the keys associaciated with a particular device. * * @parm IN OUT PHIDDEVICEINFO | phdi | * * HIDDEVICEINFO we to use/update. * * @returns * * S_OK for a success * A COM error for a failure * *****************************************************************************/
#define DIHES_UNDEFINED 0
#define DIHES_UNKNOWN 1
#define DIHES_KNOWN 2
HRESULT INTERNAL DIHid_CreateDeviceInstanceKeys( PHIDDEVICEINFO phdi, PBYTE pState ) { HRESULT hres; HKEY hkDin;
USHORT uVid, uPid;
TCHAR tszName[MAX_PATH]; PTCHAR ptszInstance = NULL;
EnterProcI(DIHid_CreateDeviceInstanceKeys, (_ "p", phdi));
/*
* Unfortunately, we need to open our registry keys before we can open * the device so rather than ask the device for its VID and PID, we * parse it from the */
/*
* ISSUE-2001/01/27-MarcAnd Should avoid parsing device ID ourselves * Need to find some documented way to parse out the three parts of the * device ID: class, device and instance in case the form changes. */
/*
* The format of the device ID is a set of strings in the form * "<class>\<device>\<instance>" with variable case. * For HID devices, the class should be "hid" and the instance is a set * of hex values separated by ampersands. The device string should be * of the form vid_9999&pid_9999 but devices exposed via a gameport can * use any string that PnP accepts. */
AssertF( ( phdi->ptszId[0] == TEXT('H') ) || ( phdi->ptszId[0] == TEXT('h') ) ); AssertF( ( phdi->ptszId[1] == TEXT('I') ) || ( phdi->ptszId[1] == TEXT('i') ) ); AssertF( ( phdi->ptszId[2] == TEXT('D') ) || ( phdi->ptszId[2] == TEXT('d') ) );
/*
* Assert that a defined state and a valid VID/PID on entry must both be * true or both false (so we can use the state after getting VID/PID). */ if( ( phdi->ProductID != 0 ) && ( phdi->VendorID !=0 ) ) { AssertF( *pState != DIHES_UNDEFINED ); } else { AssertF( *pState == DIHES_UNDEFINED ); }
if( phdi->ptszId[3] == TEXT('\\') ) { PTCHAR ptcSrc; PTCHAR ptDevId;
ptcSrc = ptDevId = &phdi->ptszId[4];
if( ( phdi->ProductID != 0 ) && ( phdi->VendorID !=0 ) ) { int iLen; /*
* Create the key name from VID / PID (because it may be * different from the value derived from the device ID). */ iLen = wsprintf(tszName, VID_PID_TEMPLATE, phdi->VendorID, phdi->ProductID);
while( *ptcSrc != TEXT('\\') ) { if( *ptcSrc == TEXT('\0') ) { break; } ptcSrc++; }
if( ( *ptcSrc != TEXT('\0') ) && ( ptcSrc != ptDevId ) ) { ptszInstance = &tszName[iLen+2]; lstrcpy( ptszInstance, ptcSrc+1 ); } } else { PTCHAR ptcDest; BOOL fFirstAmpersand = FALSE;
/*
* Copy the device ID (minus "HID\") so we can corrupt the copy. * Convert to upper case and find the other slash on the way. * Terminate the string at either the separator slash or a second * ampersand if one is found (VID_1234&PID_1234&COL_12 or REV_12). * These are device IDs so we're only really interested in keeping * real VID\PID style IDs in upper case so we don't care if this * is imperfect for other cases as long as it is reproducable. */
for( ptcDest = tszName; *ptcSrc != TEXT('\0'); ptcSrc++, ptcDest++ ) { if( ( *ptcSrc >= TEXT('a') ) && ( *ptcSrc <= TEXT('z') ) ) { *ptcDest = *ptcSrc - ( TEXT('a') - TEXT('A') ); } else if( *ptcSrc == TEXT('&') ) { if( ( ptszInstance != NULL ) || !fFirstAmpersand ) { fFirstAmpersand = TRUE; *ptcDest = TEXT('&'); } else { *ptcDest = TEXT('\0'); } } else if( *ptcSrc != TEXT('\\') ) { *ptcDest = *ptcSrc; } else { /*
* Only one slash and not the first char */ if( ( ptszInstance != NULL ) || ( ptcDest == tszName ) ) { ptszInstance = NULL; break; }
/*
* Separate the device ID and the instance */ *ptcDest = TEXT('\0'); ptszInstance = ptcDest + 1; } }
if( ptszInstance != NULL ) { #ifndef UNICODE
/*
* ISSUE-2001/02/06-MarcAnd Should have a ParseVIDPIDA * ParseVIDPID is going to convert this string back to ANSI ifndef UNICODE */ WCHAR wszName[cbszVIDPID]; AToU( wszName, cA(wszName), ptDevId );
if( ( ptszInstance - tszName == cbszVIDPID ) && ( ParseVIDPID( &uVid, &uPid, wszName ) ) ) #else
if( ( ptszInstance - tszName == cbszVIDPID ) && ( ParseVIDPID( &uVid, &uPid, ptDevId ) ) ) #endif
{ phdi->VendorID = uVid; phdi->ProductID = uPid; } else { SquirtSqflPtszV(sqfl | sqflBenign, TEXT("%hs: ID %s, len %d not VID PID"), s_szProc, ptDevId, ptszInstance - tszName ); }
/*
* Terminate the instance string */ *ptcDest = TEXT('\0'); } } } if( ptszInstance == NULL ) { hres = E_INVALIDARG; RPF( "Ignoring invalid device ID handle \"%s\" enumerated by system" ); } else { //Open our own reg key under MediaProperties\DirectInput,
//creating it if necessary.
hres = hresMumbleKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_DITYPEPROP, DI_KEY_ALL_ACCESS, REG_OPTION_NON_VOLATILE, &hkDin);
if( SUCCEEDED(hres) ) { HKEY hkVidPid; hres = hresMumbleKeyEx(hkDin, (LPTSTR)tszName, DI_KEY_ALL_ACCESS, REG_OPTION_NON_VOLATILE, &hkVidPid); if( SUCCEEDED(hres) ) { HKEY hkDevInsts;
/*
* Need to create user subkeys even if the device ID is not * VID/PID as an auto-detect device could use the auto-detect * HW ID for whatever device it detects. */
//Create the key for Calibration
HKEY hkCal;
hres = hresMumbleKeyEx(hkVidPid, TEXT("Calibration"), DI_KEY_ALL_ACCESS, REG_OPTION_NON_VOLATILE, &hkCal);
if (SUCCEEDED(hres)) { //Create the key for the instance (as the user sees it).
//For this, need to find out how many instances of the particular device
//we have already enumerated.
int ihdi; int iNum = 0; TCHAR tszInst[3];
for( ihdi = 0; ihdi < g_phdl->chdi; ihdi++) { if ((g_phdl->rghdi[ihdi].VendorID == phdi->VendorID) && (g_phdl->rghdi[ihdi].ProductID == phdi->ProductID)) { iNum++; } }
wsprintf(tszInst, TEXT("%u"), iNum);
hres = hresMumbleKeyEx(hkCal, tszInst, DI_KEY_ALL_ACCESS, REG_OPTION_NON_VOLATILE, &phdi->hk); if (SUCCEEDED(hres)) { DIHid_GetInstanceGUID(phdi->hk, &phdi->guid); } else { SquirtSqflPtszV(sqfl | sqflError, TEXT("%hs: RegCreateKeyEx failed on Instance reg key"), s_szProc); }
RegCloseKey(hkCal); } else { SquirtSqflPtszV(sqfl | sqflError, TEXT("%hs: RegCreateKeyEx failed on Calibration reg key"), s_szProc); }
/*
* Try to open the device (plug) instances to find out or * update the staus of processing on this device but don't * return any failures. */ if( SUCCEEDED( hresMumbleKeyEx(hkVidPid, TEXT("DeviceInstances"), DI_KEY_ALL_ACCESS, REG_OPTION_NON_VOLATILE, &hkDevInsts) ) ) { LONG lRc;
if( *pState == DIHES_UNDEFINED ) { DWORD cb = cbX( *pState );
lRc = RegQueryValueEx( hkDevInsts, ptszInstance, 0, 0, pState, &cb );
if( lRc != ERROR_SUCCESS ) { SquirtSqflPtszV(sqfl | sqflBenign, TEXT("%hs: RegQueryValueEx failed (%d) to get state for instance %s"), s_szProc, lRc, ptszInstance ); /*
* Make sure it was not trashed in the failure */ *pState = DIHES_UNDEFINED; } } else { lRc = RegSetValueEx( hkDevInsts, ptszInstance, 0, REG_BINARY, pState, cbX( *pState ) );
if( lRc != ERROR_SUCCESS) { SquirtSqflPtszV(sqfl | sqflBenign, TEXT("%hs: RegSetValueEx failed (%d) to update state for instance %S"), s_szProc, lRc, ptszInstance ); } }
RegCloseKey(hkDevInsts); } else { SquirtSqflPtszV(sqfl | sqflBenign, TEXT("%hs: Failed to open DeviceInstances key"), s_szProc ); }
RegCloseKey(hkVidPid); } else { SquirtSqflPtszV(sqfl | sqflError, TEXT("%hs: RegCreateKeyEx failed on Device reg key"), s_szProc); }
RegCloseKey(hkDin); } else { SquirtSqflPtszV(sqfl | sqflError, TEXT("%hs: RegOpenKeyEx failed on DirectInput reg key"), s_szProc); } }
ExitOleProc();
return hres; }
/*****************************************************************************
* * @doc INTERNAL * * @func void | DIHid_GetDevInfo | * * Get the HIDDEVICEINFO info from the device * * @parm HDEVINFO | hdev | * * Device to get information * * @parm PSP_DEVICE_INTERFACE_DATA | pdid | * * Describes the device * * @parm OUT PHIDDEVICEINFO | phdi | * * HIDDEVICEINFO we need to collect * * @returns * * Nonzero on success. * *****************************************************************************/
BOOL INTERNAL DIHid_GetDevInfo( HDEVINFO hdev, PSP_DEVICE_INTERFACE_DATA pdid, PHIDDEVICEINFO phdi ) { BOOL fRc = FALSE; SP_DEVINFO_DATA dinf;
EnterProcI(DIHid_GetDevInfo, (_ "xpp", hdev, pdid, phdi));
/*
* Open the registry key for the device so we can obtain * auxiliary information. */
dinf.cbSize = cbX(SP_DEVINFO_DATA);
/*
* Get the instance GUID and the path to * the HID device so we can talk to it. */ if (DIHid_GetDevicePath(hdev, pdid, &phdi->pdidd, &dinf) && DIHid_GetDeviceInstanceId(hdev, &dinf, &phdi->ptszId)) { HANDLE hf; TCHAR tszName[MAX_PATH]; ULONG len = sizeof(tszName); BOOL fCreateOK = FALSE; BYTE bNewState = DIHES_UNDEFINED; BYTE bPreviousState = DIHES_UNDEFINED;
DIHid_CreateDeviceInstanceKeys( phdi, &bPreviousState );
//////////////////// BEGIN OF PATCH ////////////////////////
/*
* This part is to keep the compatibility with Win2k Gold. * Some driver, like Thrustmaster driver, tries to get Joystick ID * from old registry key: * HKLM\SYSTEM\CurrentControlSet\Control\DeviceClasses\{4d1e55b2-f16f-11cf-88cb-001111000030}\<?????????>\#\Device Parameters\DirectX * See Windows bug 395416 for detail. */ { HKEY hkDev; HRESULT hres; /*
* Open the registry key for the device so we can obtain * auxiliary information, creating it if necessary. */ hkDev = SetupDiCreateDeviceInterfaceRegKey(hdev, pdid, 0, MAXIMUM_ALLOWED, NULL, NULL); if(hkDev && hkDev != INVALID_HANDLE_VALUE) { hres = hresMumbleKeyEx(hkDev, TEXT("DirectX"), KEY_ALL_ACCESS, REG_OPTION_NON_VOLATILE, &phdi->hkOld); if(SUCCEEDED(hres) ) { LONG lRc; lRc = RegSetValueEx(phdi->hkOld, TEXT("GUID"), 0, REG_BINARY, (PV)&phdi->guid, cbX(GUID)); } } } /////////////////// END OF PATCH /////////////////////
/*
* Before we CreateFile on the device, we need to make sure the * device is not in the middle of the setup process. If a device has * no device description it is probably still being setup (plugged * in) so delay opening it to avoid having an open handle on a device * that setup is trying to update. If that happens, setup will prompt * the user to reboot to use the device. * Since HIDs are "RAW" devices (don't need drivers) don't ignore * such a device forever. */ if( CM_Get_DevNode_Registry_Property(dinf.DevInst, CM_DRP_DEVICEDESC, NULL, tszName, &len, 0) == CR_SUCCESS) { /*
* Known device. */ fCreateOK = TRUE; bNewState = DIHES_KNOWN; } else { /*
* Unknown device. This either means that setup has not yet * completed processing the HID or that no device description * was set for the device where it matched. * * For now, see if we've seen this device instance before. * If we have, then if it was previously unknown assume it's * going to stay that way and start using the device. If it * was previously known or we've never seen it before wait for * a while in case setup is just taking it's time. * * If the device was the last one we tried to remove, then it's * OK to open it while it's still showing up. * ISSUE-2001/02/06-MarcAnd Opening removed devices * I assume this is so we get a read failure when it really goes * away but I don't know how that's better than ignoring it. */ /*
* ISSUE-2001/01/27-MarcAnd Should keep real list with status * We should keep a complete list of all the devices we know * about with status indicating things like "pending on * setup since X" or "removed" not these globals. */ if( lstrcmpi(phdi->ptszId, g_tszIdLastRemoved) == 0 ) { fCreateOK = TRUE; bNewState = bPreviousState; } else { if( bPreviousState == DIHES_UNKNOWN ) { /*
* We've seen this device before and it was Unknown so * don't expect that to change (as Setup does not retry * the ID search through the INFs) so just use it. */ fCreateOK = TRUE; bNewState = DIHES_UNKNOWN; } else { if( lstrcmpi(phdi->ptszId, g_tszIdLastUnknown) == 0 ) { if( GetTickCount() - g_tmLastUnknown < MSREBUILDRATE ) { SquirtSqflPtszV(sqfl | sqflBenign, TEXT("%hs: DIHid_BuildHidListEntry: %s pending on setup."), s_szProc, phdi->ptszId ); fRc = FALSE; } else { /*
* Don't wait any longer but we'll need to update * the state to "Unknown". */ fCreateOK = TRUE; bNewState = DIHES_UNKNOWN; g_tszIdLastUnknown[0] = TEXT('0'); #ifdef XDEBUG
if( bPreviousState == DIHES_KNOWN ) { SquirtSqflPtszV(sqfl | sqflBenign, TEXT("%hs: %s was known but is now unknown!"), s_szProc, phdi->ptszId ); } #endif
} } else { lstrcpy( g_tszIdLastUnknown, phdi->ptszId ); g_tmLastUnknown = GetTickCount(); fRc = FALSE; } } } }
if( fCreateOK ) { /*
* bNewState should always have been set if OK to create */
AssertF( bNewState != DIHES_UNDEFINED ); /*
* Open the device so we can get its (real) usage page / usage. */ hf = CreateFile(phdi->pdidd->DevicePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, /* no SECURITY_ATTRIBUTES */ OPEN_EXISTING, 0, /* attributes */ 0); /* template */
if(hf != INVALID_HANDLE_VALUE) { PHIDP_PREPARSED_DATA ppd; HIDD_ATTRIBUTES attr;
if(HidD_GetPreparsedData(hf, &ppd)) { HIDP_CAPS caps;
if( SUCCEEDED(HidP_GetCaps(ppd, &caps)) ) { DIHid_ParseUsagePage(phdi, &caps, ppd); SquirtSqflPtszV(sqfl, TEXT("%hs: Have %s"), s_szProc, phdi->pdidd->DevicePath); /*
* ISSUE-2001/02/06-MarcAnd Incorrect return possible * There's nothing to reset this to false if any of * the following code fails. */ fRc = TRUE; } else { SquirtSqflPtszV(sqfl | sqflError, TEXT("%hs: HidP_GetCaps(%s) failed, le= %d"), s_szProc, phdi->pdidd->DevicePath, GetLastError()); }
HidD_FreePreparsedData(ppd);
} else { SquirtSqflPtszV(sqfl | sqflError, TEXT("%hs: GetPreparsedData(%s) failed, le= %d"), s_szProc, phdi->pdidd->DevicePath, GetLastError()); }
attr.Size = cbX(attr); if( HidD_GetAttributes(hf, &attr) ) { if( ( phdi->ProductID != attr.ProductID ) || ( phdi->VendorID != attr.VendorID ) ) { SquirtSqflPtszV(sqfl | sqflBenign, TEXT("%hs: Device changed from VID_%04X&PID_%04X to VID_%04X&PID_%04X"), s_szProc, phdi->VendorID, phdi->ProductID, attr.VendorID, attr.ProductID );
phdi->ProductID = attr.ProductID; phdi->VendorID = attr.VendorID;
/*
* Make sure we update the registry. */ bPreviousState = DIHES_UNDEFINED; }
if( bPreviousState != bNewState ) { DIHid_CreateDeviceInstanceKeys( phdi, &bNewState ); } } else { SquirtSqflPtszV(sqfl | sqflError, TEXT("%hs: HidD_GetAttributes(%s) failed"), s_szProc, phdi->pdidd->DevicePath); }
DICreateStaticGuid(&phdi->guidProduct, attr.ProductID, attr.VendorID);
CloseHandle(hf);
} else { SquirtSqflPtszV(sqfl | sqflError, TEXT("%hs: CreateFile(%s) failed? le=%d"), s_szProc, phdi->pdidd->DevicePath, GetLastError()); } }
#ifndef WINNT
/*
* The following part is to resolve the swap id problem in OSR. * If a USB joystick is not in the lowest available id, after unplug and replug, * VJOYD will assign the same joystick to the lowest available id, but DINPUT still * remembers its last id. To resolve this conflict, we need this code. */
{ VXDINITPARMS vip; CHAR sz[MAX_PATH]; LPSTR pszPath; BOOL fFindIt = FALSE;
for( phdi->idJoy = 0; phdi->idJoy < cJoyMax; phdi->idJoy++ ) { pszPath = JoyReg_JoyIdToDeviceInterface_95(phdi->idJoy, &vip, sz); if(pszPath) { if( lstrcmpiA(pszPath, (PCHAR)phdi->pdidd->DevicePath) == 0x0 ) { fFindIt = TRUE; break; } } }
if( fFindIt ) { if( phdi->hk != 0x0 ) { /*
* Don't worry if the registry write fails */ RegSetValueEx(phdi->hk, TEXT("Joystick Id"), 0, 0, (PV)&phdi->idJoy, cbX(phdi->idJoy) ); } } else { /*
* Post Dx7 Patch. Win9x only * Some IHVs have created VJoyD mini drivers for their * USB game controllers that are used instead of JoyHID. * Since the interface to communicate hot-plugging with * VJoyD has not been made public, these devices cannot * be matched with a HID alias. To avoid both instances * of the same device being enumerated, mark any HID * game controller that does not have a VJoyD alias as * a (unknown) device type. * * Post DX7a! Only do this for game controllers */ if( GET_DIDEVICE_TYPE( phdi->osd.dwDevType ) >= DI8DEVTYPE_GAMEMIN ) { /*
* Change type from joystick to device */ phdi->osd.dwDevType &= ~(DIDEVTYPE_TYPEMASK | DIDEVTYPE_SUBTYPEMASK); phdi->osd.dwDevType |= DI8DEVTYPE_DEVICE; } /*
* Set to the invalid id anyway */ phdi->idJoy = -1; } } #else
// Executed only on WINNT
if( phdi->hk != 0x0 ) { DWORD cb;
cb = cbX(phdi->idJoy);
if( RegQueryValueEx(phdi->hk, TEXT("Joystick Id"), 0, 0, (PV)&phdi->idJoy, &cb ) != ERROR_SUCCESS ) { phdi->idJoy = JOY_BOGUSID; } } #endif // WINNT
} else { SquirtSqflPtszV(sqfl | sqflError, TEXT("%hs: Unable to get GUID or device path"), s_szProc); }
/*
* If we failed, then free the goo we already got. */ if(!fRc) { if( phdi->hk ) { RegCloseKey(phdi->hk); } phdi->hk = 0; FreePpv(&phdi->pdidd); FreePpv(&phdi->ptszId); }
ExitProcF(fRc);
return fRc; }
#undef DIHES_UNDEFINED
#undef DIHES_UNKNOWN
#undef DIHES_KNOWN
/*****************************************************************************
* * @doc INTERNAL * * @func void | DIHid_BuildHidListEntry | * * Builds a single entry in the list of HID devices. * * @parm HDEVINFO | hdev | * * Device list being enumerated. * * @parm PSP_DEVICE_INTERFACE_DATA | pdid | * * Describes the device that was enumerated. * * @returns * * Nonzero on success. * *****************************************************************************/
BOOL INTERNAL DIHid_BuildHidListEntry(HDEVINFO hdev, PSP_DEVICE_INTERFACE_DATA pdid) { BOOL fRc = TRUE; BOOL fAlreadyExist = FALSE; PHIDDEVICEINFO phdi; HIDDEVICEINFO hdi;
EnterProcI(DIHid_BuildHidListEntry, (_ "xp", hdev, pdid));
if(g_phdl) { PSP_DEVICE_INTERFACE_DETAIL_DATA pdidd;
/* GetDevicePath is expecting a NULL */ pdidd = NULL; if( DIHid_GetDevicePath(hdev, pdid, &pdidd, NULL) ) { PHIDDEVICEINFO phdi; int ihdi; //Check whether the device has been in the list
for(ihdi = 0, phdi = g_phdl->rghdi; ihdi < g_phdl->chdi; ihdi++, phdi++) { if( phdi->pdidd ) { if( lstrcmp( pdidd->DevicePath, phdi->pdidd->DevicePath ) == 0 ) { //already in the list
fAlreadyExist = TRUE;
#ifdef WINNT
// Id may be changed, so refresh here.
// See Windows bug 321711. -qzheng
if( phdi->hk != 0x0 ) { DWORD cb; cb = cbX(phdi->idJoy);
if( RegQueryValueEx(phdi->hk, TEXT("Joystick Id"), 0, 0, (PV)&phdi->idJoy, &cb ) != ERROR_SUCCESS ) { phdi->idJoy = JOY_BOGUSID; } } #endif
SquirtSqflPtszV(sqfl | sqflTrace, TEXT("%hs: Device %s Already Exists in HID List "), s_szProc, pdidd->DevicePath); break; } } } FreePpv(&pdidd); } else { SquirtSqflPtszV(sqfl | sqflError, TEXT("%hs: DIHid_GetDevicePath failed"), s_szProc, pdidd->DevicePath); }
if( fAlreadyExist ) { //device is already there, needn't do anything, just leave
} else { PBUSDEVICEINFO pbdi;
if( g_phdl->chdi >= g_phdl->chdiAlloc ) { /*
* Make sure there is room for this device in the list. * Grow by doubling. */
HRESULT hres;
/*
* Prefix warns that g_phdl would be NULL if the requested * size is zero (mb:35353). Assert that it is never zero. */ AssertF( cbHdlChdi(g_phdl->chdiAlloc * 2) ); hres = ReallocCbPpv(cbHdlChdi(g_phdl->chdiAlloc * 2), &g_phdl);
if(FAILED(hres)) { SquirtSqflPtszV(sqfl | sqflError, TEXT("%hs: Realloc failed"), s_szProc);
fRc = FALSE; goto done; }
g_phdl->chdiAlloc *= 2; }
phdi = &g_phdl->rghdi[g_phdl->chdi];
memset( &hdi, 0, sizeof(hdi) );
if( DIHid_GetDevInfo(hdev, pdid, &hdi) ) { hdi.fAttached = TRUE; pbdi = pbdiFromphdi(&hdi); if( pbdi != NULL ) {
/*
* If the devices is just being removed, and PnP is working on the * removing, then we won't include it in our list. */ if( lstrcmpi(pbdi->ptszId, g_tszIdLastRemoved) == 0 && GetTickCount() - g_tmLastRemoved < MSREBUILDRATE_FIFTH ) { SquirtSqflPtszV(sqfl | sqflBenign, TEXT("%hs: DIHid_BuildHidListEntry: %s pending on removal."), s_szProc, pbdi->ptszId ); fRc = FALSE; } else { BUS_REGDATA RegData;
memcpy( phdi, &hdi, sizeof(hdi) ); g_phdl->chdi++;
if( SUCCEEDED(DIBusDevice_GetRegData(pbdi->hk, &RegData)) ) { RegData.fAttachOnReboot = TRUE; DIBusDevice_SetRegData(pbdi->hk, &RegData); } } } else { memcpy( phdi, &hdi, sizeof(hdi) ); g_phdl->chdi++; } } else { SquirtSqflPtszV(sqfl | sqflBenign, TEXT("%hs: DIHid_GetDevInfo failed, ignoring device"), s_szProc ); fRc = FALSE; } } } // end of if(g_phdl)
done:
ExitProcF(fRc);
return fRc; }
/*****************************************************************************
* * @doc INTERNAL * * @func void | DIHid_BuildHidList | * * Builds the list of HID devices. * * @parm BOOL | fForce | * * If nonzero, we force a rebuild of the HID list. * Otherwise, we rebuild only if the list hasn't * been rebuilt recently. * *****************************************************************************/
void EXTERNAL DIHid_BuildHidList(BOOL fForce) { HRESULT hres; DWORD dwTick; EnterProcI(DIHid_BuildHidList, (_ "u", fForce));
DllEnterCrit();
// Force implies a complete rebuild of the list
// No hanging onto old entries.
if( fForce ) { DIHid_EmptyHidList(); } //ISSUE-2001/03/29-timgill Meltdown code change may be unnecessary
dwTick = GetTickCount(); if(!(g_flEmulation & 0x80000000) && /* Not disabled by emulation */ HidD_GetHidGuid && /* HID actually exists */ (fForce || /* Forcing rebuild, or */ g_tmLastHIDRebuild == 0 || /* Never built before, or */ dwTick - g_tmLastHIDRebuild > MSREBUILDRATE /* Haven't built for a while */ #ifdef WINNT
|| dwTick - Excl_GetConfigChangedTime() < MSREBUILDRATE /* joyConfig just Changed */ #endif
)) { GUID guidHid; HDEVINFO hdev;
HidD_GetHidGuid(&guidHid);
hdev = SetupDiGetClassDevs(&guidHid, 0, 0, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); if(hdev != INVALID_HANDLE_VALUE) { DIHid_CheckHidList();
/*
* There is no way to query the number of devices. * You just have to keep incrementing until you run out. * * If we already have a g_phdl, then re-use it. Else, create * a new one. But always realloc up to the minimum starting * point. (This upward realloc is important in case there * were no HID devices the last time we checked.) */
if( !g_phdl ) { hres = AllocCbPpv(cbHdlChdi(chdiInit), &g_phdl);
if(SUCCEEDED(hres)) { g_phdl->chdi = 0; g_phdl->chdiAlloc = chdiInit; } else { /* Skip erroneous items */ SquirtSqflPtszV(sqfl | sqflError, TEXT("DIHid_BuildHidListEntry ") TEXT("Realloc g_phdl fails.")); } } else { hres = S_OK; }
if(SUCCEEDED(hres)) { int idev;
/*
* To avoid infinite looping on * internal *boo-boo's*, break on any * error once we have tried more than * chdiMax devices, since that's the most * HID will ever give us. */ for(idev = 0; idev < chdiMax; idev++) { SP_DEVICE_INTERFACE_DATA did;
AssertF(g_phdl->chdi <= g_phdl->chdiAlloc);
/*
* SetupDI requires that the caller initialize cbSize. */ did.cbSize = cbX(did); if(SetupDiEnumDeviceInterfaces(hdev, 0, &guidHid, idev, &did)) { if(!DIHid_BuildHidListEntry(hdev, &did)) { /* Skip erroneous items */ SquirtSqflPtszV(sqfl | sqflError, TEXT("DIHid_BuildHidListEntry ") TEXT("failed?")); }
} else if(GetLastError() == ERROR_NO_MORE_ITEMS) { break; } else { /* Skip erroneous items */ SquirtSqflPtszV(sqfl | sqflError, TEXT("SetupDiEnumDeviceInterface ") TEXT("failed? le=%d"), GetLastError()); }
}
}
SetupDiDestroyDeviceInfoList(hdev); } else { SquirtSqflPtszV(sqfl | sqflError, TEXT("SetupDiGetClassDevs failed le=%d"), GetLastError()); }
#ifdef WINNT
if( g_phdl && g_phdl->chdi ) { DIWdm_InitJoyId(); } #endif
g_tmLastHIDRebuild = GetTickCount(); }
DllLeaveCrit();
ExitProc();
}
DWORD DIHid_DetectHideAndRevealFlags( PCHID this ) { // Get hold of the Devices Parent
// If the Parent a HID device that we know about ?
// If the parent is a gaming device ( joystick / gamepad, etc )
// Mark the device as being "fictional"
DWORD dwFlags2 = 0x0; HDEVINFO hdev;
// We only detect and hide mice and keyboard,
// For now, there is no reason to hide other HID devices.
// Futhermore the scheme to detect which devices to hide is based
// on MSHs filter driver implementation where they eject PDOs for
// "fictional" devices. These fictional devices show up as childern of
// gaming devices. FredBh (HID/PM) informs us that most multi-function devices he
// has seen (mice and kbd) have had multiple top level collections.
AssertF( ( GET_DIDEVICE_TYPE( this->dwDevType ) == DI8DEVTYPE_MOUSE ) || ( GET_DIDEVICE_TYPE( this->dwDevType ) == DI8DEVTYPE_KEYBOARD ) );
hdev = SetupDiCreateDeviceInfoList(NULL, NULL); if (hdev != INVALID_HANDLE_VALUE) { SP_DEVINFO_DATA dinf; dinf.cbSize = cbX(SP_DEVINFO_DATA); if (SetupDiOpenDeviceInfo(hdev, this->ptszId, NULL, 0, &dinf)) { DEVINST DevInst; if ( (CM_Get_Parent(&DevInst, dinf.DevInst, 0x0)) == CR_SUCCESS ) { TCHAR tszId[MAX_PATH]; if ( CR_SUCCESS == CM_Get_Device_ID(DevInst, tszId, MAX_PATH, 0x0 )) { PHIDDEVICEINFO phdi; DllEnterCrit(); phdi = phdiFindHIDDeviceId(tszId);
if ( phdi ) { DWORD dwDevType = phdi->osd.dwDevType; // If the device is a HID gaming device (in the broadest definition)
if ( ( GET_DIDEVICE_TYPE(dwDevType) >= DI8DEVTYPE_GAMEMIN ) && ( dwDevType & DIDEVTYPE_HID ) ) { dwFlags2 = JOYTYPE_HIDEACTIVE; switch (GET_DIDEVICE_TYPE( this->dwDevType )) { case DI8DEVTYPE_MOUSE: dwFlags2 |= JOYTYPE_MOUSEHIDE; break; case DI8DEVTYPE_KEYBOARD: dwFlags2 |= JOYTYPE_KEYBHIDE; break; default: AssertF(0); } } }
DllLeaveCrit();
} } } SetupDiDestroyDeviceInfoList(hdev); } return dwFlags2; }
|