1912 lines
56 KiB
C
1912 lines
56 KiB
C
/*****************************************************************************
|
|
*
|
|
* DIPort.c
|
|
*
|
|
* Copyright (c) 1996 Microsoft Corporation. All Rights Reserved.
|
|
*
|
|
* Abstract:
|
|
*
|
|
* Support functions for Gameport/Serialport enumeration.
|
|
*
|
|
* Contents:
|
|
*
|
|
*
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#include "dinputpr.h"
|
|
|
|
/*
|
|
* We can reuse some code from diHidEnm.c
|
|
*/
|
|
#define DIPort_GetDevicePath(hdev, pdid, didd, dinf) \
|
|
DIHid_GetDevicePath(hdev, pdid, didd, dinf)
|
|
|
|
#define DIPort_GetDeviceInstanceId(hdev, pdinf, tszId) \
|
|
DIHid_GetDeviceInstanceId(hdev, pdinf, tszId)
|
|
|
|
#define DIPort_GetInstanceGUID(hk, lpguid) \
|
|
DIHid_GetInstanceGUID(hk, lpguid)
|
|
|
|
#define DIPort_GetRegistryProperty(ptszId, dwProperty, pdiph) \
|
|
DIHid_GetRegistryProperty(ptszId, dwProperty, pdiph)
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* The sqiffle for this file.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#undef sqfl
|
|
#define sqfl sqflPort
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @global PBUSDEVICE | g_pBusDevice |
|
|
*
|
|
* List of known GamePort/SerialPort devices.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
static BUSDEVICE g_pBusDevice[] =
|
|
{
|
|
{
|
|
D(TEXT("GamePort Bus") comma)
|
|
NULL,
|
|
&GUID_GAMEENUM_BUS_ENUMERATOR,
|
|
0x0,
|
|
IOCTL_GAMEENUM_EXPOSE_HARDWARE,
|
|
IOCTL_GAMEENUM_REMOVE_HARDWARE,
|
|
IOCTL_GAMEENUM_PORT_DESC,
|
|
IOCTL_GAMEENUM_PORT_PARAMETERS,
|
|
IOCTL_GAMEENUM_EXPOSE_SIBLING,
|
|
IOCTL_GAMEENUM_REMOVE_SELF,
|
|
IDS_STDGAMEPORT,
|
|
JOY_HWS_ISGAMEPORTBUS
|
|
},
|
|
|
|
/***************
|
|
No defination for serial port devices yet !
|
|
{
|
|
D(TEXT("SerialPort Bus") comma )
|
|
NULL,
|
|
&GUID_SERENUM_BUS_ENUMERATOR,
|
|
0x0,
|
|
IOCTL_SERIALENUM_EXPOSE_HARDWARE,
|
|
IOCTL_SERIALENUM_REMOVE_HARDWARE,
|
|
IOCTL_SERIALENUM_PORT_DESC,
|
|
IOCTL_SERIALENUM_PORT_PARAMETERS,
|
|
IOCTL_SERIALENUM_EXPOSE_SIBLING,
|
|
IOCTL_SERIALENUM_REMOVE_SELF,
|
|
IDS_STDSERIALPORT,
|
|
JOY_HWS_ISSERIALPORTBUS
|
|
},
|
|
****************/
|
|
};
|
|
|
|
|
|
#pragma BEGIN_CONST_DATA
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func BOOL INTERNAL | SearchDevTree |
|
|
*
|
|
* Helper routine that searches the device tree for
|
|
* a desired device device.
|
|
*
|
|
* @parm IN DEVINST | dnStart |
|
|
* Starting point for the search.
|
|
*
|
|
* @parm IN DEVINST | dnSeek |
|
|
* The device instance we are looking for.
|
|
*
|
|
* @parm IN PULONG | pRecurse |
|
|
* To limit the number of recursions.
|
|
*
|
|
* @returns BOOL
|
|
* True on success.
|
|
*
|
|
*****************************************************************************/
|
|
CONFIGRET INTERNAL
|
|
SearchDevTree
|
|
(
|
|
DEVINST dnStart,
|
|
DEVINST dnSeek,
|
|
PUINT pRecurse
|
|
)
|
|
{
|
|
#define MAX_RECURSION ( 4 )
|
|
|
|
CONFIGRET cr;
|
|
|
|
EnterProcI(SearchDevTree, (_"xxx", dnStart, dnSeek, pRecurse));
|
|
|
|
cr = CR_SUCCESS;
|
|
for( *pRecurse = 0x0; *pRecurse < MAX_RECURSION && cr == CR_SUCCESS; (*pRecurse)++)
|
|
{
|
|
cr = CM_Get_Parent(&dnSeek, dnSeek, 0 );
|
|
if( dnStart == dnSeek )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if( dnStart != dnSeek )
|
|
{
|
|
cr = CR_NO_SUCH_DEVNODE;
|
|
}
|
|
#undef MAX_RECURSION
|
|
|
|
#if 0 // Using Recursion
|
|
if( *pRecurse > MAX_RECURSION )
|
|
{
|
|
return CR_NO_SUCH_DEVNODE;
|
|
}
|
|
|
|
if( dnSeek == dnStart )
|
|
{
|
|
return CR_SUCCESS;
|
|
}
|
|
|
|
do
|
|
{
|
|
DEVINST dnNode;
|
|
|
|
cr = CM_Get_Child(&dnNode, dnStart, 0 );
|
|
if( cr == CR_SUCCESS )
|
|
{
|
|
CAssertF(CR_SUCCESS == 0x0 );
|
|
if( CR_SUCCESS == SearchDevTree(dnNode, dnSeek, pRecurse) )
|
|
{
|
|
return cr;
|
|
}
|
|
}
|
|
cr = CM_Get_Sibling(&dnStart, dnStart, 0);
|
|
|
|
}while( cr == CR_SUCCESS );
|
|
#endif // No recursion
|
|
|
|
return cr;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func PBUSDEVICEINFO | pbdiFromphdi |
|
|
*
|
|
* Locates Gameport/Serialport information given a device instance of
|
|
* one of its children.
|
|
* Returns NULL if the device instance is not a child of
|
|
* any known gameports/serialports
|
|
*
|
|
* Internal Routine, parameters already validated.
|
|
*
|
|
* The DLL critical must be held across the call; once the
|
|
* critical section is released, the returned pointer becomes
|
|
* invalid.
|
|
*
|
|
* @parm IN PHIDDEVICEINFO | phdi |
|
|
*
|
|
* Address of a HIDDEVICEINFO structure
|
|
*
|
|
* @returns
|
|
*
|
|
* Pointer to the <t BUSDEVICEINFO> that describes
|
|
* the parent bus.
|
|
*
|
|
*****************************************************************************/
|
|
PBUSDEVICEINFO INTERNAL
|
|
pbdiFromphdi
|
|
(
|
|
IN PHIDDEVICEINFO phdi
|
|
)
|
|
{
|
|
PBUSDEVICEINFO pbdi_Found;
|
|
PBUSDEVICE pBusDevice;
|
|
int iBusType;
|
|
|
|
EnterProcI(pbdiFromphdi, (_"x", phdi));
|
|
|
|
AssertF(InCrit());
|
|
AssertF(phdi != NULL );
|
|
|
|
pbdi_Found = NULL;
|
|
for( iBusType = 0x0, pBusDevice = g_pBusDevice;
|
|
iBusType < cA(g_pBusDevice) && pbdi_Found == NULL;
|
|
iBusType++, pBusDevice++ )
|
|
{
|
|
HDEVINFO hdev;
|
|
/*
|
|
* Now talk to SetupApi to get info about the device.
|
|
*/
|
|
hdev = SetupDiCreateDeviceInfoList(NULL, NULL);
|
|
|
|
if(hdev != INVALID_HANDLE_VALUE )
|
|
{
|
|
SP_DEVINFO_DATA dinf_hid;
|
|
|
|
ZeroX(dinf_hid);
|
|
|
|
dinf_hid.cbSize = cbX(SP_DEVINFO_DATA);
|
|
|
|
/* Get SP_DEVINFO_DATA for the HID device */
|
|
if( pBusDevice->pbdl != NULL &&
|
|
phdi!= NULL &&
|
|
SetupDiOpenDeviceInfo(hdev, phdi->ptszId, NULL, 0, &dinf_hid))
|
|
{
|
|
int igdi;
|
|
PBUSDEVICEINFO pbdi;
|
|
SP_DEVINFO_DATA dinf_bus;
|
|
|
|
ZeroX(dinf_bus);
|
|
|
|
dinf_bus.cbSize = cbX(SP_DEVINFO_DATA);
|
|
|
|
/*
|
|
* Loop through all known gameports/serialports and look for a gameport/serialport
|
|
* that is a parent of the HID device
|
|
*/
|
|
|
|
for(igdi = 0, pbdi = pBusDevice->pbdl->rgbdi;
|
|
igdi < pBusDevice->pbdl->cgbi && pbdi_Found == NULL ;
|
|
igdi++, pbdi++)
|
|
{
|
|
if(SetupDiOpenDeviceInfo(hdev, pbdi->ptszId, NULL, 0, &dinf_bus))
|
|
{
|
|
ULONG Recurse = 0x0;
|
|
if( CR_SUCCESS == SearchDevTree(dinf_bus.DevInst, dinf_hid.DevInst, &Recurse) )
|
|
{
|
|
pbdi_Found = pbdi;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
SetupDiDestroyDeviceInfoList(hdev);
|
|
}
|
|
}
|
|
ExitProcX((UINT_PTR)pbdi_Found);
|
|
return pbdi_Found;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func PBUSDEVICEINFO | pbdiFromGUID |
|
|
*
|
|
* Locates Gameport/Serialport information given a device instance of
|
|
* one of its children.
|
|
* Returns NULL if the device instance is not a child of
|
|
* any known gameports/serialports
|
|
*
|
|
* Internal Routine, parameters already 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 BUSDEVICEINFO> that describes
|
|
* the parent bus.
|
|
*
|
|
*****************************************************************************/
|
|
PBUSDEVICEINFO EXTERNAL
|
|
pbdiFromGUID
|
|
(
|
|
IN PCGUID pguid
|
|
)
|
|
{
|
|
PBUSDEVICEINFO pbdi_Found;
|
|
PBUSDEVICE pBusDevice;
|
|
int iBusType;
|
|
|
|
EnterProcI(pbdiFromGUID, (_"G", &pguid));
|
|
|
|
AssertF(InCrit());
|
|
|
|
pbdi_Found = NULL;
|
|
for( iBusType = 0x0, pBusDevice = g_pBusDevice;
|
|
iBusType < cA(g_pBusDevice) && pbdi_Found == NULL;
|
|
iBusType++, pBusDevice++ )
|
|
{
|
|
/*
|
|
* Loop through all known gameports/serialports and look for a gameport/serialport
|
|
* that is a parent of the HID device
|
|
*/
|
|
PBUSDEVICEINFO pbdi;
|
|
int igdi;
|
|
for(igdi = 0, pbdi = pBusDevice->pbdl->rgbdi;
|
|
igdi < pBusDevice->pbdl->cgbi && pbdi_Found == NULL ;
|
|
igdi++, pbdi++)
|
|
{
|
|
if( IsEqualGUID(pguid, &pbdi->guid) )
|
|
{
|
|
pbdi_Found = pbdi;
|
|
}
|
|
}
|
|
}
|
|
ExitProcX((UINT_PTR)pbdi_Found);
|
|
return pbdi_Found;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func PHIDDEVICEINFO | phdiFrompbdi |
|
|
*
|
|
* Locates a HID device attached to a given Gameport/Serialport
|
|
* Returns NULL if no devices are currently attached to a known port.
|
|
*
|
|
* Internal Routine, parameters already validated.
|
|
*
|
|
* The DLL critical must be held across the call; once the
|
|
* critical section is released, the returned pointer becomes
|
|
* invalid.
|
|
*
|
|
* @parm IN PBUSDEVICEINFO | pbdi |
|
|
*
|
|
* Address of the <t BUSDEVICEINFO> structure that
|
|
* describes the gameport/serialport.
|
|
*
|
|
* @returns
|
|
*
|
|
* Pointer to one of the <t HIDDEVICEINFO> that describes
|
|
* the device. ( Gamport may have multiple devices attached ).
|
|
*
|
|
*****************************************************************************/
|
|
|
|
PHIDDEVICEINFO INTERNAL
|
|
phdiFrompbdi
|
|
(
|
|
IN PBUSDEVICEINFO pbdi
|
|
)
|
|
{
|
|
PHIDDEVICEINFO phdi_Found;
|
|
HDEVINFO hdev;
|
|
|
|
EnterProcI(phdiFrompbdi, (_"x", pbdi));
|
|
|
|
AssertF(InCrit());
|
|
AssertF(pbdi != NULL );
|
|
|
|
/* Enumurate the HID devices */
|
|
DIHid_BuildHidList(TRUE);
|
|
|
|
phdi_Found = NULL;
|
|
/*
|
|
* Now talk to SetupApi to get info about the device.
|
|
*/
|
|
hdev = SetupDiCreateDeviceInfoList(NULL, NULL);
|
|
|
|
if(hdev != INVALID_HANDLE_VALUE)
|
|
{
|
|
SP_DEVINFO_DATA dinf_bus;
|
|
|
|
dinf_bus.cbSize = cbX(SP_DEVINFO_DATA);
|
|
|
|
if( pbdi != NULL && SetupDiOpenDeviceInfo(hdev, pbdi->ptszId, NULL, 0, &dinf_bus))
|
|
{
|
|
int ihdi;
|
|
PHIDDEVICEINFO phdi;
|
|
SP_DEVINFO_DATA dinf_hid;
|
|
dinf_hid.cbSize = cbX(SP_DEVINFO_DATA);
|
|
|
|
if( g_phdl )
|
|
{
|
|
for(ihdi = 0, phdi = g_phdl->rghdi ;
|
|
ihdi < g_phdl->chdi && phdi_Found == NULL ;
|
|
ihdi++, phdi++)
|
|
{
|
|
if(SetupDiOpenDeviceInfo(hdev, phdi->ptszId, NULL, 0, &dinf_hid))
|
|
{
|
|
ULONG Recurse = 0x0;
|
|
if(CR_SUCCESS == SearchDevTree(dinf_bus.DevInst, dinf_hid.DevInst, &Recurse) )
|
|
{
|
|
phdi_Found = phdi;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
SetupDiDestroyDeviceInfoList(hdev);
|
|
}
|
|
|
|
ExitProcX((UINT_PTR)phdi_Found);
|
|
return phdi_Found;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc EXTERNAL
|
|
*
|
|
* @func PPORTDEVICEINFO | pbdiFromJoyId |
|
|
*
|
|
* Locates Gameport/Serialport information given a device id of
|
|
* a joystick .
|
|
*
|
|
* Returns NULL if the device instance is not a child of
|
|
* any known gameports/serialports
|
|
*
|
|
* Internal Routine, parameters already validated.
|
|
*
|
|
* The DLL critical must be held across the call; once the
|
|
* critical section is released, the returned pointer becomes
|
|
* invalid.
|
|
*
|
|
* @parm IN int | idJoy |
|
|
*
|
|
* The Joystick ID of the child device that will be associated
|
|
* to a known gameport/serialport.
|
|
*
|
|
* @returns
|
|
*
|
|
* Pointer to the <t BUSDEVICEINFO> that describes
|
|
* the device.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
PBUSDEVICEINFO EXTERNAL
|
|
pbdiFromJoyId
|
|
(
|
|
IN int idJoy
|
|
)
|
|
{
|
|
GUID guid;
|
|
HRESULT hres;
|
|
PBUSDEVICEINFO pbdi;
|
|
|
|
EnterProcI(pbdiFromJoyId, (_"x", idJoy));
|
|
AssertF(InCrit());
|
|
|
|
pbdi = NULL;
|
|
|
|
/* Find the GUID that corresponds to the Joystick ID */
|
|
hres = hResIdJoypInstanceGUID_WDM(idJoy, &guid);
|
|
|
|
#ifndef WINNT
|
|
if( FAILED(hres) ) {
|
|
hres = hResIdJoypInstanceGUID_95(idJoy, &guid);
|
|
}
|
|
#endif
|
|
|
|
if( SUCCEEDED(hres) )
|
|
{
|
|
PHIDDEVICEINFO phdi;
|
|
phdi = phdiFindHIDInstanceGUID(&guid);
|
|
|
|
if( phdi != NULL )
|
|
{
|
|
pbdi = pbdiFromphdi(phdi);
|
|
}
|
|
}
|
|
|
|
ExitProcX((UINT_PTR)pbdi);
|
|
return pbdi;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func BOOL | DIBusDevice_Expose |
|
|
*
|
|
* Attaches a gameport/serialport device to the Gameport/SerialPort Bus
|
|
*
|
|
* @parm IN PBUSDEVICEINFO | pbdi |
|
|
* Address of a BUSDEVICEINFO structure.
|
|
*
|
|
* @parm IN OUT PBUS_REGDATA | pRegData |
|
|
* Gameport/Serialport specific data. The Handle to the opened device
|
|
* is returned in this structure
|
|
*
|
|
*
|
|
* @returns
|
|
* BOOL. True indicates success.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
HRESULT EXTERNAL
|
|
DIBusDevice_Expose
|
|
(
|
|
IN PBUSDEVICEINFO pbdi,
|
|
IN OUT PBUS_REGDATA pRegData
|
|
)
|
|
{
|
|
HRESULT hres;
|
|
BOOL frc;
|
|
PHIDDEVICEINFO phdi;
|
|
|
|
EnterProcI(DIBusDevice_Expose, (_ "pp", pbdi, pRegData));
|
|
|
|
AssertF(DllInCrit() );
|
|
AssertF(pbdi!= NULL );
|
|
|
|
phdi = phdiFrompbdi(pbdi);
|
|
|
|
if( pRegData && pRegData->dwSize != cbX(*pRegData) )
|
|
{
|
|
hres = E_INVALIDARG;
|
|
} else if( phdi != NULL )
|
|
{
|
|
hres = E_ACCESSDENIED;
|
|
} else
|
|
{
|
|
HANDLE hf;
|
|
BUS_REGDATA RegDataTest;
|
|
|
|
/* There is a weird condition where the HID device does not appear on a previous
|
|
* Add, (drivers not loaded, user cancelled loading of some files, etc
|
|
* In such cases we need to tell GameEnum to remove the device before proceeding
|
|
* any further
|
|
*/
|
|
if( pbdi->fAttached || pRegData->hHardware != NULL )
|
|
{
|
|
DIBusDevice_Remove(pbdi);
|
|
}
|
|
AssertF(pbdi->fAttached == FALSE);
|
|
|
|
// Change for Windows bug 575181 -- make sure we can write to the registry
|
|
// before we expose the device; otherwise we might not be able to remove it!
|
|
ZeroMemory(&RegDataTest, cbX(RegDataTest));
|
|
RegDataTest.dwSize = cbX(RegDataTest);
|
|
if (FAILED(DIBusDevice_SetRegData(pbdi->hk, &RegDataTest)))
|
|
{
|
|
// We couldn't write to the registy; return E_ACCESSDENIED
|
|
hres = E_ACCESSDENIED;
|
|
}
|
|
else
|
|
{
|
|
// Open a File handle to the gameport/serialport device so we can send it IOCTLS
|
|
hf = CreateFile(pbdi->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 )
|
|
{
|
|
DWORD cbRc;
|
|
GAMEENUM_PORT_DESC Desc;
|
|
Desc.Size = cbX(Desc) ;
|
|
|
|
Sleep(50); //need sleep a while to wait for the device is ready to accept commands.
|
|
|
|
/* Get the gameport bus properties */
|
|
frc = DeviceIoControl (hf,
|
|
pbdi->pBusDevice->ioctl_DESC,
|
|
&Desc, cbX(Desc),
|
|
&Desc, cbX(Desc),
|
|
&cbRc, NULL);
|
|
if( frc && cbRc == cbX(Desc) )
|
|
{
|
|
PGAMEENUM_EXPOSE_HARDWARE pExpose;
|
|
DWORD cbExpose;
|
|
cbExpose = cbX(*pExpose) + cbX(pRegData->wszHardwareId);
|
|
|
|
hres = AllocCbPpv( cbExpose, & pExpose);
|
|
|
|
if( SUCCEEDED(hres ) )
|
|
{
|
|
typedef struct _OEMDATA
|
|
{
|
|
ULONG uVID_uPID;
|
|
ULONG joy_hws_dwFlags;
|
|
ULONG dwFlags1;
|
|
ULONG Reserved;
|
|
} OEMDATA, *POEMDATA;
|
|
|
|
POEMDATA pOemData = (POEMDATA)(&pExpose->OemData);
|
|
CAssertF(2*sizeof(*pOemData) == sizeof(pExpose->OemData))
|
|
|
|
pOemData->uVID_uPID = MAKELONG(pRegData->uVID, pRegData->uPID);
|
|
pOemData->joy_hws_dwFlags = pRegData->hws.dwFlags;
|
|
pOemData->dwFlags1 = pRegData->dwFlags1;
|
|
|
|
/*
|
|
* Make sure only known analog devices cause the
|
|
* compatible hardware ID to be exposed.
|
|
* This is done so that no in-box drivers will match for
|
|
* an unsupported digital joystick so users will be
|
|
* prompted to use an unsigned IHV driver rather than
|
|
* silently loading the generic analog joystick driver.
|
|
*/
|
|
if( ( pRegData->dwFlags1 & JOYTYPE_ANALOGCOMPAT )
|
|
|| ( ( pRegData->uVID == MSFT_SYSTEM_VID )
|
|
&& ( ( pRegData->uPID & 0xff00 ) == MSFT_SYSTEM_PID ) ) )
|
|
{
|
|
pExpose->Flags = GAMEENUM_FLAG_COMPATIDCTRL;
|
|
}
|
|
else
|
|
{
|
|
pExpose->Flags = GAMEENUM_FLAG_COMPATIDCTRL | GAMEENUM_FLAG_NOCOMPATID ;
|
|
}
|
|
|
|
pExpose->Size = cbX(*pExpose) ;
|
|
pExpose->PortHandle = Desc.PortHandle;
|
|
pExpose->NumberJoysticks = pRegData->nJoysticks;
|
|
|
|
pRegData->nAxes = 2;
|
|
|
|
if( pExpose->NumberJoysticks != 2 )
|
|
{
|
|
AssertF( pExpose->NumberJoysticks == 1);
|
|
if( pRegData->hws.dwFlags & JOY_HWS_HASZ )
|
|
{
|
|
pRegData->nAxes++;
|
|
}
|
|
if( pRegData->hws.dwFlags & JOY_HWS_HASR )
|
|
{
|
|
pRegData->nAxes++;
|
|
}
|
|
pExpose->NumberButtons = (USHORT)pRegData->hws.dwNumButtons;
|
|
}
|
|
else
|
|
{
|
|
pOemData++;
|
|
pOemData->uVID_uPID = MAKELONG(pRegData->uVID, pRegData->uPID);
|
|
pOemData->joy_hws_dwFlags = JOY_HWS_XISJ2X | JOY_HWS_YISJ2Y;
|
|
pExpose->NumberButtons = 2;
|
|
}
|
|
|
|
pExpose->NumberAxis = pRegData->nAxes;
|
|
|
|
/*
|
|
* The SideWinder driver uses the OEMData field in a
|
|
* sibling expose to pass internal data (this ptrs) from
|
|
* one instance to another. Since these fields are
|
|
* supposed to be for the OEMData we have a Flags1 field
|
|
* to allow the data to be zeroed for a DInput expose for
|
|
* drivers that don't want the normal data.
|
|
*/
|
|
|
|
if ( pRegData->dwFlags1 & JOYTYPE_ZEROGAMEENUMOEMDATA )
|
|
{
|
|
ZeroBuf(pExpose->OemData, sizeof(pExpose->OemData) );
|
|
}
|
|
|
|
CopyMemory(pExpose->HardwareIDs, pRegData->wszHardwareId, cbX(pRegData->wszHardwareId) );
|
|
|
|
Sleep(50);
|
|
|
|
if( frc = DeviceIoControl (hf,
|
|
pbdi->pBusDevice->ioctl_EXPOSE,
|
|
pExpose, cbExpose,
|
|
pExpose, cbExpose,
|
|
&cbRc, NULL )
|
|
&& cbRc == cbExpose )
|
|
{
|
|
PVOID hHardwareOld = pRegData->hHardware;
|
|
|
|
pbdi->fAttached = TRUE;
|
|
pRegData->hHardware = pExpose->HardwareHandle;
|
|
DIBusDevice_SetRegData(pbdi->hk, pRegData);
|
|
|
|
/*
|
|
* If we have dealt with this device before then the hHardwareOld
|
|
* will be non null, and we have sufficient reason to believe that the
|
|
* expose will succeed.
|
|
*
|
|
* This test needs to be removed to fix manbug: 39554.
|
|
* For new created device, we need wait for a while to let phdi be ready.
|
|
*
|
|
*/
|
|
//if(hHardwareOld)
|
|
{
|
|
int i;
|
|
|
|
for(i = 0; (i < 20) && (phdiFrompbdi(pbdi) == NULL); i++ )
|
|
{
|
|
Sleep(50);
|
|
}
|
|
}
|
|
|
|
} else // DeviceIOControl (EXPOSE) Failed
|
|
{
|
|
hres = E_FAIL;
|
|
SquirtSqflPtszV(sqfl | sqflError,
|
|
TEXT("%S: IOCTL_PORTENUM_EXPOSE_HARDWARE failed ")
|
|
TEXT("Error = %d"),
|
|
s_szProc, GetLastError());
|
|
}
|
|
FreePpv(&pExpose);
|
|
} else // Alloc failed
|
|
{
|
|
SquirtSqflPtszV(sqfl | sqflError,
|
|
TEXT("%S: AllocCbPpv failed "),
|
|
s_szProc);
|
|
}
|
|
} else // IOCTL FAILED
|
|
{
|
|
hres = E_FAIL;
|
|
SquirtSqflPtszV(sqfl | sqflError,
|
|
TEXT("%S: IOCTL_PORTENUM_PORT_DESC failed ")
|
|
TEXT("Error = %d"),
|
|
s_szProc, GetLastError());
|
|
}
|
|
|
|
CloseHandle(hf);
|
|
} else
|
|
{
|
|
hres = E_FAIL;
|
|
SquirtSqflPtszV(sqfl | sqflError,
|
|
TEXT("%S: CreateFile(%s) failed ")
|
|
TEXT("Error = %d"),
|
|
s_szProc, pbdi->pdidd->DevicePath, GetLastError());
|
|
}
|
|
}
|
|
}
|
|
ExitBenignProcX(hres);
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func BOOL | DIBusDevice_Remove |
|
|
*
|
|
* Removes the FDO for a gameport/serialport device.
|
|
*
|
|
* @parm IN HANDLE | hf |
|
|
* Handle to the GamePort/SerialPort Bus device file object
|
|
*
|
|
* @parm IN PPORT_REGDATA | pRegData |
|
|
* Structure that contains registry data. What we need from here is the
|
|
* handle to the hardware.
|
|
*
|
|
* @returns
|
|
* BOOL. True for success.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
HRESULT INTERNAL
|
|
DIBusDevice_Remove
|
|
(
|
|
IN PBUSDEVICEINFO pbdi
|
|
)
|
|
{
|
|
HRESULT hres;
|
|
BUS_REGDATA RegData;
|
|
|
|
EnterProcI(DIBus_Remove, (_ "p", pbdi));
|
|
|
|
hres = DIBusDevice_GetRegData(pbdi->hk, &RegData);
|
|
|
|
//
|
|
// Delete our registry goo, so this device
|
|
// will not show up on subsequent reboots
|
|
//
|
|
DIBusDevice_SetRegData(pbdi->hk, NULL);
|
|
|
|
if( SUCCEEDED(hres) )
|
|
{
|
|
HANDLE hf;
|
|
BOOL frc;
|
|
|
|
// Open a File handle to the gameport/serialport device so we can send it IOCTLS
|
|
hf = CreateFile(pbdi->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 )
|
|
{
|
|
|
|
DWORD cbRc;
|
|
GAMEENUM_REMOVE_HARDWARE Remove;
|
|
|
|
Remove.Size = cbX(Remove);
|
|
Remove.HardwareHandle = RegData.hHardware;
|
|
|
|
frc = DeviceIoControl (hf,
|
|
pbdi->pBusDevice->ioctl_REMOVE,
|
|
&Remove, cbX(Remove),
|
|
&Remove, cbX(Remove),
|
|
&cbRc, NULL) ;
|
|
if( frc && cbRc == cbX(Remove) )
|
|
{
|
|
pbdi->fAttached = FALSE;
|
|
} else // DeviceIoControl ( REMOVE_HARDWARE ) Failed
|
|
{
|
|
hres = E_FAIL;
|
|
SquirtSqflPtszV(sqfl | sqflError,
|
|
TEXT("%S: DeviceIOControl(REMOVE_HARDWARE) failed ")
|
|
TEXT("Error = %d"),
|
|
s_szProc, GetLastError());
|
|
}
|
|
CloseHandle(hf);
|
|
}
|
|
}
|
|
|
|
ExitBenignOleProc();
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func HRESULT | DIPort_SetRegData |
|
|
*
|
|
* Sets up registry data under the $hk$/Config subkey for the gameport
|
|
* device.
|
|
*
|
|
* @parm IN HKEY | hk |
|
|
* A handle to the parent key where the registry data will be written.
|
|
*
|
|
* @parm IN PGAMEPORT_REGDATA | pRegData |
|
|
* Pointer to a structure containing data to be written to the registry.
|
|
*
|
|
* @returns
|
|
* BOOL. True for success
|
|
*
|
|
*****************************************************************************/
|
|
HRESULT INTERNAL
|
|
DIBusDevice_SetRegData
|
|
(
|
|
IN HKEY hk,
|
|
IN PBUS_REGDATA pRegData
|
|
)
|
|
{
|
|
LONG lrc;
|
|
HRESULT hres = S_OK;
|
|
|
|
EnterProcI(DIPort_SetRegData, (_ "xpx", hk, pRegData ));
|
|
|
|
if( pRegData != NULL )
|
|
{
|
|
|
|
if( ( lrc = RegSetValueEx(hk, TEXT("Config"), 0, REG_BINARY,
|
|
(PV) (pRegData), cbX(*pRegData)) ) == ERROR_SUCCESS )
|
|
{
|
|
hres = S_OK;
|
|
} else // RegSetValueEx FAILED
|
|
{
|
|
hres = E_FAIL;
|
|
SquirtSqflPtszV(sqfl | sqflError,
|
|
TEXT("%S: RegSetValueEx() failed ")
|
|
TEXT("Error = %d"),
|
|
s_szProc, lrc);
|
|
}
|
|
} else
|
|
{
|
|
lrc = RegDeleteValue(hk, TEXT("Config"));
|
|
}
|
|
|
|
ExitOleProc();
|
|
return (hres);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func HRESULT | DIPort_GetRegData |
|
|
*
|
|
* Gets registry data from the $hk$/Config subkey for the gameport
|
|
* device.
|
|
*
|
|
* @parm IN HKEY | hk |
|
|
* A handle to the parent key where the registry.
|
|
*
|
|
* @parm IN PGAMEPORT_REGDATA | pRegData |
|
|
* Address of a pointer to the structure where the registry data
|
|
* will be read into.
|
|
*
|
|
* @returns
|
|
* HRESULT
|
|
*****************************************************************************/
|
|
HRESULT INTERNAL
|
|
DIBusDevice_GetRegData
|
|
(
|
|
IN HKEY hk,
|
|
OUT PBUS_REGDATA pRegData
|
|
)
|
|
{
|
|
LONG lRc;
|
|
DWORD cb;
|
|
HRESULT hres;
|
|
|
|
EnterProcI(DIPort_GetRegData, (_ "xpx", hk, pRegData ));
|
|
|
|
cb = cbX(*pRegData);
|
|
|
|
lRc = RegQueryValueEx( hk, TEXT("Config"), 0, 0 , (PV)(pRegData), &cb );
|
|
|
|
if( lRc == ERROR_SUCCESS && pRegData->dwSize == cbX(*pRegData ) )
|
|
{
|
|
hres = S_OK;
|
|
} else
|
|
{
|
|
DIBusDevice_SetRegData(hk, NULL );
|
|
ZeroX(*pRegData);
|
|
hres = E_FAIL;
|
|
|
|
SquirtSqflPtszV(sqfl | sqflBenign,
|
|
TEXT("%S: RegQueryValueEx(Config) failed ")
|
|
TEXT("Error = %d, ( pRegData->cbSize(%d) == cbX(*pRegData)(%d)) "),
|
|
s_szProc, lRc, pRegData->dwSize, cbX(*pRegData) );
|
|
}
|
|
|
|
ExitBenignOleProc();
|
|
return ( hres ) ;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func void | DIBus_BuildListEntry |
|
|
*
|
|
* Builds a single entry in the list of GAMEPORT/SERIALPORT 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
|
|
DIBusDevice_BuildListEntry
|
|
(
|
|
HDEVINFO hdev,
|
|
PSP_DEVICE_INTERFACE_DATA pdid,
|
|
PBUSDEVICE pBusDevice
|
|
)
|
|
{
|
|
BOOL fRc = TRUE;
|
|
//HKEY hkDev;
|
|
HKEY hkDin;
|
|
PBUSDEVICEINFO pbdi;
|
|
PBUSDEVICELIST pbdl;
|
|
PSP_DEVICE_INTERFACE_DETAIL_DATA pdidd;
|
|
BOOL fAlreadyExist;
|
|
|
|
HRESULT hres;
|
|
|
|
EnterProcI(DIBus_BuildListEntry, (_ "xp", hdev, pdid));
|
|
|
|
pbdl = pBusDevice->pbdl;
|
|
|
|
fAlreadyExist = FALSE;
|
|
|
|
/* GetDevicePath is expecting a NULL */
|
|
pdidd = NULL;
|
|
|
|
if( DIPort_GetDevicePath(hdev, pdid, &pdidd, NULL) )
|
|
{
|
|
int ibdi;
|
|
//Check whether the device has been in the list
|
|
for( ibdi = 0; ibdi < pbdl->cgbi; ibdi++)
|
|
{
|
|
if( pbdl->rgbdi[ibdi].pdidd )
|
|
{
|
|
if( lstrcmp( pdidd->DevicePath, pbdl->rgbdi[ibdi].pdidd->DevicePath ) == 0 )
|
|
{
|
|
//already in the list
|
|
fAlreadyExist = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
FreePpv(&pdidd);
|
|
}
|
|
|
|
if( fAlreadyExist == TRUE )
|
|
{
|
|
fRc = TRUE;
|
|
} else
|
|
{
|
|
/*
|
|
* Make sure there is room for this device in the list.
|
|
* Grow by doubling.
|
|
*/
|
|
if( pbdl->cgbi >= pbdl->cgbiAlloc)
|
|
{
|
|
/*
|
|
* Assert that we never ask for zero bytes here
|
|
*/
|
|
AssertF( cbGdlCbdi( pbdl->cgbiAlloc * 2) );
|
|
hres = ReallocCbPpv( cbGdlCbdi( pbdl->cgbiAlloc * 2), &pBusDevice->pbdl );
|
|
if( FAILED(hres) )
|
|
{
|
|
SquirtSqflPtszV(sqfl | sqflError,
|
|
TEXT("%S: Realloc failed"), s_szProc);
|
|
fRc = FALSE;
|
|
goto done;
|
|
}
|
|
/*
|
|
* Prefix warns (Win:170673) that pBusDevice->pbdl could be zero
|
|
* if we asked for zero bytes above but the size requested cannot
|
|
* be zero so this has been asserted. Note the fix for (W:45084)
|
|
* was incorrect so it has been removed.
|
|
*/
|
|
pbdl = pBusDevice->pbdl;
|
|
pbdl->cgbiAlloc *= 2;
|
|
}
|
|
|
|
AssertF( pbdl->cgbi < pbdl->cgbiAlloc);
|
|
pbdi = &pbdl->rgbdi[pbdl->cgbi];
|
|
pbdi->pBusDevice = pBusDevice;
|
|
pbdi->hk = 0;
|
|
pbdi->idJoy = JOY_BOGUSID;
|
|
|
|
/*
|
|
* Open the registry key for the device so we can obtain
|
|
* auxiliary information.
|
|
*/
|
|
{
|
|
//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))
|
|
{
|
|
//Create the Gameports reg key
|
|
HKEY hkGameports;
|
|
hres = hresMumbleKeyEx(hkDin,
|
|
TEXT("Gameports"),
|
|
DI_KEY_ALL_ACCESS,
|
|
REG_OPTION_NON_VOLATILE,
|
|
&hkGameports);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
//Create a reg key corresponding to the instance number.
|
|
//Since we do this for every gameport enumerated, the number in the list
|
|
//indicates the instance number.
|
|
TCHAR tszInst[3];
|
|
wsprintf(tszInst, TEXT("%u"), pbdl->cgbi);
|
|
|
|
hres = hresMumbleKeyEx(hkGameports,
|
|
tszInst,
|
|
DI_KEY_ALL_ACCESS,
|
|
REG_OPTION_NON_VOLATILE,
|
|
&pbdi->hk);
|
|
|
|
if(SUCCEEDED(hres))
|
|
{
|
|
SP_DEVINFO_DATA dinf;
|
|
dinf.cbSize = cbX(SP_DEVINFO_DATA);
|
|
|
|
/*
|
|
* Get the instance GUID and the path to
|
|
* the GAMEPORT/SERIALPORT device so we can talk to it.
|
|
*/
|
|
if(DIPort_GetDevicePath(hdev, pdid, &pbdi->pdidd, &dinf) &&
|
|
DIPort_GetDeviceInstanceId(hdev, &dinf, &pbdi->ptszId) &&
|
|
DIPort_GetInstanceGUID(pbdi->hk, &pbdi->guid) )
|
|
{
|
|
HANDLE hf;
|
|
hf = CreateFile(pbdi->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 )
|
|
{
|
|
|
|
BUS_REGDATA RegData;
|
|
ZeroX(RegData);
|
|
|
|
CloseHandle(hf);
|
|
|
|
// Bump up the counter
|
|
fRc = TRUE;
|
|
pbdl->cgbi++;
|
|
|
|
hres = DIBus_InitId(pBusDevice->pbdl);
|
|
|
|
if( SUCCEEDED(hres) )
|
|
{
|
|
hres = DIBusDevice_GetRegData(pbdi->hk, &RegData);
|
|
}
|
|
|
|
if( SUCCEEDED(hres) )
|
|
{
|
|
/* There is a pathological case which can cause endless bluescreen's
|
|
* If the HID driver causes a bluescreen, and we keep reattaching
|
|
* it, we are sunk !!
|
|
* To guard against this possiblity we reattach a device on reboot
|
|
* only if we are sure that it succeeded the first time around
|
|
*/
|
|
if( RegData.fAttachOnReboot == FALSE )
|
|
{
|
|
DIBusDevice_Remove(pbdi);
|
|
|
|
SquirtSqflPtszV(sqfl | sqflError,
|
|
TEXT("%S: DIPortDevice_Expose FAILED, ")
|
|
TEXT("Driver did not load property the first time around "),
|
|
s_szProc);
|
|
} else if( pbdi->fAttached == FALSE )
|
|
{
|
|
hres = DIBusDevice_Expose( pbdi, &RegData );
|
|
if( SUCCEEDED( hres ) || hres == E_ACCESSDENIED )
|
|
{
|
|
pbdi->fAttached = TRUE;
|
|
} else
|
|
{
|
|
SquirtSqflPtszV(sqfl | sqflError,
|
|
TEXT("%S: DIPortDevice_Expose FAILED ")
|
|
TEXT("hres = %d"),
|
|
s_szProc, hres);
|
|
}
|
|
}
|
|
}
|
|
|
|
} else
|
|
{
|
|
fRc = FALSE;
|
|
|
|
SquirtSqflPtszV(sqfl | sqflError,
|
|
TEXT("%S: CreateFile(%s) failed ")
|
|
TEXT("Error = %d"),
|
|
s_szProc, pbdi->pdidd->DevicePath, GetLastError());
|
|
}
|
|
|
|
} else
|
|
{
|
|
SquirtSqflPtszV(sqfl | sqflError,
|
|
TEXT("%S: Unable to get device path"),
|
|
s_szProc);
|
|
pbdi->hk = 0x0;
|
|
fRc = FALSE;
|
|
}
|
|
|
|
/*
|
|
* If we failed, then free the goo we already got.
|
|
*/
|
|
if(!fRc)
|
|
{
|
|
if( pbdi->hk )
|
|
RegCloseKey(pbdi->hk);
|
|
pbdi->hk = 0;
|
|
FreePpv(&pbdi->pdidd);
|
|
FreePpv(&pbdi->ptszId);
|
|
fRc = FALSE;
|
|
}
|
|
|
|
} else
|
|
{
|
|
SquirtSqflPtszV(sqfl | sqflError,
|
|
TEXT("%S: RegCreateKeyEx failed on Instance, error "),
|
|
s_szProc);
|
|
fRc = FALSE;
|
|
}
|
|
|
|
RegCloseKey(hkGameports);
|
|
|
|
} else // RegCreateKeyEx FAILED
|
|
{
|
|
SquirtSqflPtszV(sqfl | sqflError,
|
|
TEXT("%S: RegCreateKeyEx failed on Gameports, error "),
|
|
s_szProc);
|
|
fRc = FALSE;
|
|
}
|
|
|
|
RegCloseKey(hkDin);
|
|
}
|
|
else
|
|
{
|
|
SquirtSqflPtszV(sqfl | sqflError,
|
|
TEXT("%S: RegOpenKeyEx failed on DirectInput"),
|
|
s_szProc);
|
|
fRc = FALSE;
|
|
}
|
|
|
|
}
|
|
}
|
|
done:;
|
|
ExitProcF(fRc);
|
|
return fRc;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc EXTERNAL
|
|
*
|
|
* @func void | DIPort_EmptyList |
|
|
*
|
|
* Empty the list of GAMEPORT/SERIALPORT devices.
|
|
*
|
|
* This function must be called under the DLL critical section.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void INTERNAL
|
|
DIBus_EmptyList
|
|
(
|
|
PBUSDEVICELIST *ppbdl
|
|
)
|
|
{
|
|
PBUSDEVICELIST pbdl = *ppbdl;
|
|
|
|
AssertF(InCrit());
|
|
|
|
if( pbdl )
|
|
{
|
|
int igdi;
|
|
for(igdi = 0; igdi < pbdl->cgbi; igdi++)
|
|
{
|
|
FreePpv(&pbdl->rgbdi[igdi].pdidd);
|
|
FreePpv(&pbdl->rgbdi[igdi].ptszId);
|
|
if( pbdl->rgbdi[igdi].hk)
|
|
{
|
|
RegCloseKey( pbdl->rgbdi[igdi].hk);
|
|
}
|
|
}
|
|
/*
|
|
* We invalidated all the pointers, so make sure
|
|
* nobody looks at them.
|
|
*/
|
|
pbdl->cgbi = 0;
|
|
FreePpv(&pbdl);
|
|
*ppbdl = NULL;
|
|
}
|
|
}
|
|
|
|
void EXTERNAL
|
|
DIBus_FreeMemory()
|
|
{
|
|
int iBusType;
|
|
PBUSDEVICE pBusDevice;
|
|
|
|
for( iBusType = 0x0, pBusDevice = g_pBusDevice;
|
|
iBusType < cA(g_pBusDevice);
|
|
iBusType++, pBusDevice++ )
|
|
{
|
|
DIBus_EmptyList(&pBusDevice->pbdl);
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc EXTERNAL
|
|
*
|
|
* @func void | DIPort_InitId |
|
|
*
|
|
* Initializes Joystick IDs for JoyConfig and legacy APIs
|
|
* Store the joystick IDs the registry under the %%DirectX/JOYID key.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#undef PORTID_BOGUS
|
|
#define PORTID_BOGUS ( 0xffffffff )
|
|
|
|
HRESULT EXTERNAL
|
|
DIBus_InitId(PBUSDEVICELIST pbdl)
|
|
{
|
|
HRESULT hres = FALSE;
|
|
LONG lRc;
|
|
DWORD cb;
|
|
int igdi;
|
|
BOOL fNeedId;
|
|
BOOL rfPortId[cgbiMax]; /* Bool Array for to determine which IDs are in use */
|
|
PBUSDEVICEINFO pbdi;
|
|
|
|
EnterProcI(DIBus_InitId, (_ ""));
|
|
|
|
fNeedId = FALSE;
|
|
|
|
AssertF(DllInCrit());
|
|
|
|
ZeroX(rfPortId );
|
|
|
|
|
|
if( pbdl != NULL )
|
|
{
|
|
/* Iterate over to find used IDs */
|
|
for( igdi = 0, pbdi = pbdl->rgbdi ;
|
|
igdi < pbdl->cgbi ;
|
|
igdi++, pbdi++ )
|
|
{
|
|
pbdi->idPort = PORTID_BOGUS; // Default
|
|
|
|
cb = cbX(pbdi->idPort);
|
|
if( ( lRc = RegQueryValueEx(pbdi->hk, TEXT("ID"),
|
|
0, 0, (PV)&pbdi->idPort, &cb) == ERROR_SUCCESS ) )
|
|
{
|
|
if( rfPortId[pbdi->idPort] // Collision in GameId
|
|
|| pbdi->idPort > cgbiMax ) // Wrror
|
|
{
|
|
pbdi->idPort = PORTID_BOGUS;
|
|
fNeedId = TRUE;
|
|
} else // Valid idPort
|
|
{
|
|
rfPortId[pbdi->idPort] = TRUE;
|
|
|
|
}
|
|
} else // RegQueryValue("ID") does not exist
|
|
{
|
|
fNeedId = TRUE;
|
|
}
|
|
}
|
|
|
|
if( fNeedId )
|
|
{
|
|
/*
|
|
* We have Examined all GamePort/SerialPort Ids found used IDs
|
|
* and determined some device needs an Id
|
|
*/
|
|
/* Iterate to assign unused Id's */
|
|
for( igdi = 0, pbdi = pbdl->rgbdi;
|
|
igdi < pbdl->cgbi ;
|
|
igdi++, pbdi++ )
|
|
{
|
|
if( pbdi->idPort == PORTID_BOGUS )
|
|
{
|
|
/* Get an Unused ID */
|
|
for( pbdi->idPort = 0x0;
|
|
pbdi->idPort < cgbiMax;
|
|
pbdi->idPort++ )
|
|
{
|
|
if( rfPortId[pbdi->idPort] == FALSE )
|
|
break;
|
|
}
|
|
rfPortId[pbdi->idPort] = TRUE;
|
|
|
|
if( lRc = RegSetValueEx(pbdi->hk, TEXT("ID"), 0, REG_BINARY,
|
|
(PV)&pbdi->idPort, cbX(pbdi->idPort)) == ERROR_SUCCESS )
|
|
{
|
|
|
|
} else
|
|
{
|
|
SquirtSqflPtszV(sqfl | sqflError,
|
|
TEXT("%S: RegSetValueEx(JOYID) FAILED ")
|
|
TEXT("Error = %d"),
|
|
s_szProc, lRc);
|
|
hres = FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ExitOleProc();
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func void | DIBus_CheckList |
|
|
*
|
|
* Check the list of HID devices and free any that cannot be opened
|
|
*
|
|
* This function must be called under the DLL critical section.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void INTERNAL
|
|
DIBus_CheckList(PBUSDEVICELIST pbdl)
|
|
{
|
|
HANDLE hf;
|
|
|
|
AssertF(InCrit());
|
|
|
|
/*
|
|
* Free all the information of the device that cannot be opened
|
|
*/
|
|
if(pbdl)
|
|
{
|
|
int ibdi;
|
|
|
|
PBUSDEVICEINFO pbdi;
|
|
for(ibdi = 0, pbdl->cgbi = 0; ibdi < pbdl->cgbiAlloc; ibdi++)
|
|
{
|
|
pbdi = &pbdl->rgbdi[ibdi];
|
|
if( pbdi && pbdi->pdidd )
|
|
{
|
|
/*
|
|
* Open the device
|
|
*/
|
|
hf = CreateFile(pbdi->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)
|
|
{
|
|
FreePpv(&pbdi->pdidd);
|
|
FreePpv(&pbdi->ptszId);
|
|
if(pbdi->hk)
|
|
{
|
|
RegCloseKey(pbdi->hk);
|
|
}
|
|
ZeroX( pbdi );
|
|
} else
|
|
{
|
|
pbdl->cgbi++;
|
|
CloseHandle(hf);
|
|
}
|
|
}
|
|
}
|
|
|
|
//re-order the existing devices, put them at the front of the hid list.
|
|
for(ibdi = 0; ibdi < pbdl->cgbi; ibdi++)
|
|
{
|
|
if( !pbdl->rgbdi[ibdi].pdidd )
|
|
{
|
|
int ibdi2;
|
|
|
|
//find the existing device from the biggest index in the hid list.
|
|
for( ibdi2 = pbdl->cgbiAlloc; ibdi2 >= ibdi+1; ibdi2-- )
|
|
{
|
|
if( pbdl->rgbdi[ibdi2].pdidd )
|
|
{
|
|
memcpy( &pbdl->rgbdi[ibdi], &pbdl->rgbdi[ibdi2], sizeof(BUSDEVICEINFO) );
|
|
ZeroX( pbdl->rgbdi[ibdi2] );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc EXTERNAL
|
|
*
|
|
* @func void | DIBus_BuildList |
|
|
*
|
|
* Builds the list of GAMEPORT/SERIALPORT devices.
|
|
*
|
|
* @parm BOOL | fForce |
|
|
*
|
|
* If nonzero, we force a rebuild of the GAMEPORT/SERIALPORT list.
|
|
* Otherwise, we rebuild only if the list hasn't
|
|
* been rebuilt recently.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#define MSREBUILDRATE 20000 /* Twenty seconds */
|
|
|
|
ULONG EXTERNAL
|
|
DIBus_BuildList( IN BOOL fForce )
|
|
{
|
|
HRESULT hres;
|
|
PBUSDEVICE pBusDevice;
|
|
ULONG cDevices;
|
|
int iBusType;
|
|
DWORD dwTickCount;
|
|
|
|
EnterProcI(DIBus_BuildList, (_ "u", fForce));
|
|
|
|
DllEnterCrit();
|
|
|
|
/*
|
|
* Decide whether or not to rebuild once (don't want to half rebuild)
|
|
*/
|
|
dwTickCount = GetTickCount();
|
|
|
|
// Force implies a complete rebuild of the list.
|
|
if(fForce)
|
|
{
|
|
DIBus_FreeMemory();
|
|
}
|
|
|
|
DIHid_BuildHidList(fForce);
|
|
|
|
hres = S_OK;
|
|
for( cDevices = iBusType = 0x0, pBusDevice = g_pBusDevice;
|
|
iBusType < cA(g_pBusDevice);
|
|
iBusType++, pBusDevice++ )
|
|
{
|
|
PBUSDEVICELIST pbdl;
|
|
pbdl = pBusDevice->pbdl;
|
|
|
|
if( HidD_GetHidGuid && /* HID support */
|
|
( fForce || /* Forcing rebuild, or */
|
|
pBusDevice->tmLastRebuild == 0 || /* Never built before, or */
|
|
dwTickCount - pBusDevice->tmLastRebuild > MSREBUILDRATE )
|
|
)
|
|
{
|
|
HDEVINFO hdev;
|
|
|
|
/* delete devices that disappeared since we last looked */
|
|
DIBus_CheckList(pbdl);
|
|
|
|
hdev = SetupDiGetClassDevs((LPGUID)pBusDevice->pcGuid, 0, 0,
|
|
DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
|
|
|
|
if(hdev != INVALID_HANDLE_VALUE)
|
|
{
|
|
/*
|
|
* 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 pbdl, then re-use it. Else, create
|
|
* a new one. Alloc up to the minimum starting point.
|
|
*/
|
|
|
|
if( pBusDevice->pbdl == NULL )
|
|
{
|
|
/*
|
|
* Prefix warns that we leak this memory (mb:34688) but
|
|
* we keep a reference in our global list so we can free
|
|
* the memory when we're done with it.
|
|
*/
|
|
hres = AllocCbPpv(cbGdlCbdi(cgbiInit), &pBusDevice->pbdl );
|
|
|
|
if(SUCCEEDED(hres))
|
|
{
|
|
pbdl = pBusDevice->pbdl;
|
|
pbdl->cgbi = 0;
|
|
pbdl->cgbiAlloc = cgbiInit;
|
|
}
|
|
}
|
|
|
|
if( SUCCEEDED(hres) )
|
|
{
|
|
int idev;
|
|
|
|
/*
|
|
* To avoid infinite looping on
|
|
* internal *boo-boo's*, break on any
|
|
* error once we have tried more than
|
|
* cgbiMax devices, since that's the most
|
|
* GAMEPORT/SERIALPORT will ever give us.
|
|
*/
|
|
for(idev = 0; idev < cgbiMax; idev++)
|
|
{
|
|
SP_DEVICE_INTERFACE_DATA did;
|
|
|
|
AssertF( pbdl->cgbi <= pbdl->cgbiAlloc);
|
|
|
|
/*
|
|
* Note, pnp.c doesn't initialize this so we have to
|
|
*/
|
|
did.cbSize = cbX(did);
|
|
if(SetupDiEnumDeviceInterfaces(hdev, 0, (LPGUID)pBusDevice->pcGuid,
|
|
idev, &did))
|
|
{
|
|
if(DIBusDevice_BuildListEntry(hdev, &did, &g_pBusDevice[iBusType] ))
|
|
{
|
|
//pbdl->cgbi++;
|
|
} else
|
|
{
|
|
/* Skip erroneous items */
|
|
SquirtSqflPtszV(sqfl | sqflError,
|
|
TEXT("DIBus_BuildListEntry ")
|
|
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);
|
|
pBusDevice->tmLastRebuild = GetTickCount();
|
|
}
|
|
}
|
|
|
|
if(pbdl) { cDevices += pbdl->cgbi; }
|
|
}
|
|
|
|
/* New gameport devices may be exposed. Pick them up too */
|
|
DIHid_BuildHidList(FALSE);
|
|
|
|
DllLeaveCrit();
|
|
ExitProc();
|
|
return (cDevices);
|
|
}
|
|
|
|
PBUSDEVICELIST EXTERNAL
|
|
pbdlFromGUID
|
|
(
|
|
IN PCGUID pcGuid
|
|
)
|
|
{
|
|
PBUSDEVICELIST pbdl_Found = NULL;
|
|
PBUSDEVICE pBusDevice;
|
|
int iBusType;
|
|
|
|
for( iBusType = 0x0, pBusDevice = g_pBusDevice;
|
|
iBusType < cA(g_pBusDevice);
|
|
iBusType++, pBusDevice++ )
|
|
{
|
|
if( IsEqualGUID(pBusDevice->pcGuid, pcGuid) )
|
|
{
|
|
pbdl_Found = pBusDevice->pbdl;
|
|
break;
|
|
}
|
|
}
|
|
return pbdl_Found;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func HRESULT | DIBusDevice_ExposeEx |
|
|
*
|
|
* Attache a HID device to all available ports.
|
|
*
|
|
* @parm IN HANDLE | hf |
|
|
* Handle the the Gameport/SerialPort File object
|
|
*
|
|
* @parm IN OUT PBUS_REGDATA | pRegData |
|
|
* Gameport/Serialport specific data. The Handle to the opened device
|
|
* is returned in this structure
|
|
*
|
|
*
|
|
* @returns
|
|
* BOOL. True indicates success.
|
|
*
|
|
*****************************************************************************/
|
|
HRESULT EXTERNAL
|
|
DIBusDevice_ExposeEx
|
|
(
|
|
IN PBUSDEVICELIST pbdl,
|
|
IN PBUS_REGDATA pRegData
|
|
)
|
|
{
|
|
HRESULT hres = DIERR_DEVICENOTREG;
|
|
|
|
EnterProcI(DIBusDevice_ExposeEx, (_ "xx", pbdl, pRegData));
|
|
|
|
/*
|
|
* The return code a little strange for this function
|
|
* If the Expose succeeds for any gameport then
|
|
* we will return that error code.
|
|
* If the expose fails for all gameports,
|
|
* then we will return the amalgam of
|
|
* all the error codes.
|
|
*/
|
|
if( pbdl->cgbi != 0x0 )
|
|
{
|
|
HRESULT hres1 = DIERR_DEVICENOTREG;
|
|
int ibdi;
|
|
hres = S_OK;
|
|
|
|
for( ibdi = 0x0; ibdi < pbdl->cgbi; ibdi++)
|
|
{
|
|
HRESULT hres0;
|
|
PBUSDEVICEINFO pbdi;
|
|
|
|
hres0 = DIERR_DEVICENOTREG;
|
|
pbdi = &(pbdl->rgbdi[ibdi]);
|
|
|
|
if( pbdi->fAttached == FALSE )
|
|
{
|
|
pbdi->fDeleteIfNotConnected = TRUE;
|
|
hres0 = DIBusDevice_Expose(pbdi, pRegData);
|
|
if( FAILED(hres0) )
|
|
{
|
|
hres |= hres0;
|
|
} else
|
|
{
|
|
hres1 = hres0;
|
|
}
|
|
} else {
|
|
hres = DIERR_DEVICEFULL;
|
|
}
|
|
}
|
|
|
|
if(SUCCEEDED(hres1))
|
|
{
|
|
hres = hres1;
|
|
}
|
|
}
|
|
|
|
ExitOleProc();
|
|
return hres;
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func HRESULT | DIBusDevice_GetTypeInfo |
|
|
*
|
|
* Gets typeinfo for bus device.
|
|
*
|
|
* @parm IN PCGUID | pcguid |
|
|
* GUID that identifies the gameport
|
|
*
|
|
* @parm OUT LPDIJOTYPEINFO | pjti |
|
|
* Typeinfo stuct filled in by this function
|
|
*
|
|
* @parm IN DWORD | fl |
|
|
* Flags that specify what fields to fill out.
|
|
*
|
|
* @returns
|
|
* HRESULT.
|
|
*
|
|
*****************************************************************************/
|
|
HRESULT EXTERNAL
|
|
DIBusDevice_GetTypeInfo
|
|
(
|
|
PCGUID pcguid,
|
|
LPDIJOYTYPEINFO pjti,
|
|
DWORD fl
|
|
)
|
|
{
|
|
HRESULT hres;
|
|
PBUSDEVICEINFO pbdi;
|
|
EnterProcI(DIBusDevice_GetTypeInfo, (_ "Gp", pcguid, pjti));
|
|
|
|
hres = E_FAIL;
|
|
DllEnterCrit();
|
|
|
|
if( NULL != ( pbdi = pbdiFromGUID(pcguid) ) )
|
|
{
|
|
DIPROPSTRING dips;
|
|
|
|
if(fl & DITC_REGHWSETTINGS)
|
|
{
|
|
pjti->hws.dwFlags = pbdi->pBusDevice->dwJOY_HWS_ISPORTBUS | JOY_HWS_AUTOLOAD ;
|
|
pjti->hws.dwNumButtons = MAKELONG( pbdi->idPort, 0x0 );
|
|
}
|
|
|
|
if( fl & DITC_CLSIDCONFIG )
|
|
{
|
|
pjti->clsidConfig = pbdi->guid;
|
|
}
|
|
|
|
if(fl & DITC_DISPLAYNAME)
|
|
{
|
|
if(FAILED( hres = DIPort_GetRegistryProperty(pbdi->ptszId, SPDRP_FRIENDLYNAME, &dips.diph) ) )
|
|
{
|
|
if( FAILED( hres = DIPort_GetRegistryProperty(pbdi->ptszId, SPDRP_DEVICEDESC, &dips.diph) ) )
|
|
{
|
|
SquirtSqflPtszV(sqfl | sqflError,
|
|
TEXT("%S: No device name | friendly name for gameport %d "),
|
|
s_szProc, pbdi->idPort);
|
|
}
|
|
}
|
|
if( SUCCEEDED(hres) )
|
|
{
|
|
/*
|
|
* Prefix warns (Wi:228282) that dips.wsz could be
|
|
* uninitialized however one of the above GetRegistryProperty
|
|
* functions has succeeded leaving a worst case of a NULL
|
|
* terminator having been copied there.
|
|
*/
|
|
lstrcpyW(pjti->wszDisplayName, dips.wsz);
|
|
}
|
|
}
|
|
|
|
#ifndef WINNT
|
|
if(fl & DITC_CALLOUT)
|
|
{
|
|
ZeroX(pjti->wszCallout);
|
|
}
|
|
#endif
|
|
|
|
if(fl & DITC_HARDWAREID)
|
|
{
|
|
ZeroX(pjti->wszHardwareId);
|
|
}
|
|
|
|
if( fl & DITC_FLAGS1 )
|
|
{
|
|
pjti->dwFlags1 = 0x0;
|
|
}
|
|
|
|
hres = S_OK;
|
|
} else
|
|
{
|
|
hres = E_FAIL;
|
|
SquirtSqflPtszV(sqfl | sqflError,
|
|
TEXT("%S: GUID not a port GUID "),
|
|
s_szProc);
|
|
}
|
|
|
|
DllLeaveCrit();
|
|
ExitProcX(hres);
|
|
|
|
return hres;
|
|
|
|
}
|
|
|
|
|
|
HRESULT EXTERNAL DIPort_SnapTypes(LPWSTR *ppwszz)
|
|
{
|
|
LONG cDevices;
|
|
HRESULT hres = E_FAIL;
|
|
|
|
cDevices = DIBus_BuildList(FALSE);
|
|
|
|
if( cDevices)
|
|
{
|
|
DllEnterCrit();
|
|
hres = AllocCbPpv(cbCwch( cDevices * MAX_JOYSTRING) , ppwszz);
|
|
if( SUCCEEDED(hres) )
|
|
{
|
|
int iBusType, igdi;
|
|
PBUSDEVICE pBusDevice;
|
|
LPWSTR pwsz = *ppwszz;
|
|
|
|
for(iBusType = 0x0, pBusDevice = g_pBusDevice;
|
|
iBusType < 1;
|
|
iBusType++, pBusDevice++ )
|
|
{
|
|
PBUSDEVICEINFO pbdi;
|
|
for(igdi = 0, pbdi = pBusDevice->pbdl->rgbdi;
|
|
igdi < pBusDevice->pbdl->cgbi;
|
|
igdi++, pbdi++)
|
|
{
|
|
TCHAR tszGuid[MAX_JOYSTRING];
|
|
NameFromGUID(tszGuid, &pbdi->guid);
|
|
|
|
#ifdef UNICODE
|
|
lstrcpyW(pwsz, &tszGuid[ctchNamePrefix]);
|
|
pwsz += lstrlenW(pwsz) + 1;
|
|
#else
|
|
TToU(pwsz, cA(pwsz), &tszGuid[ctchNamePrefix]);
|
|
pwsz += lstrlenW(pwsz) + 1;
|
|
#endif
|
|
}
|
|
}
|
|
*pwsz = L'\0'; /* Make it ZZ */
|
|
}
|
|
DllLeaveCrit();
|
|
}
|
|
return hres;
|
|
}
|