Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2038 lines
63 KiB

/*****************************************************************************
*
* 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;
}