981 lines
29 KiB
C
981 lines
29 KiB
C
/*****************************************************************************
|
|
*
|
|
* PidInit.c
|
|
* Copyright (c) 1999 Microsoft Corporation. All Rights Reserved.
|
|
* Abstract:
|
|
*
|
|
* Initialization code .
|
|
*
|
|
*****************************************************************************/
|
|
#include "pidpr.h"
|
|
|
|
#define sqfl ( sqflInit )
|
|
|
|
#define NudgeWorkerThread(thid) \
|
|
PostThreadMessage(thid, WM_NULL, 0x0, (LPARAM)NULL)
|
|
|
|
#pragma BEGIN_CONST_DATA
|
|
|
|
|
|
static PIDUSAGE c_rgUsgPool[] =
|
|
{
|
|
MAKE_PIDUSAGE(SIMULTANEOUS_EFFECTS_MAX, FIELD_OFFSET(REPORTPOOL,uSimulEfMax)),
|
|
MAKE_PIDUSAGE(RAM_POOL_SIZE, FIELD_OFFSET(REPORTPOOL,uRamPoolSz)),
|
|
MAKE_PIDUSAGE(ROM_POOL_SIZE, FIELD_OFFSET(REPORTPOOL,uRomPoolSz)),
|
|
MAKE_PIDUSAGE(ROM_EFFECT_BLOCK_COUNT, FIELD_OFFSET(REPORTPOOL,uRomETCount)),
|
|
MAKE_PIDUSAGE(POOL_ALIGNMENT, FIELD_OFFSET(REPORTPOOL,uPoolAlign)),
|
|
};
|
|
|
|
static PIDUSAGE c_rgUsgPoolSz[] =
|
|
{
|
|
MAKE_PIDUSAGE(SET_CONSTANT_FORCE_REPORT, FIELD_OFFSET(SZPOOL, uSzConstant)),
|
|
MAKE_PIDUSAGE(SET_ENVELOPE_REPORT, FIELD_OFFSET(SZPOOL, uSzEnvelope)),
|
|
MAKE_PIDUSAGE(SET_CONDITION_REPORT, FIELD_OFFSET(SZPOOL, uSzCondition)),
|
|
MAKE_PIDUSAGE(SET_CUSTOM_FORCE_REPORT, FIELD_OFFSET(SZPOOL, uSzCustom)),
|
|
MAKE_PIDUSAGE(SET_PERIODIC_REPORT, FIELD_OFFSET(SZPOOL, uSzPeriodic)),
|
|
MAKE_PIDUSAGE(SET_RAMP_FORCE_REPORT, FIELD_OFFSET(SZPOOL, uSzRamp)),
|
|
MAKE_PIDUSAGE(SET_EFFECT_REPORT, FIELD_OFFSET(SZPOOL, uSzEffect)),
|
|
MAKE_PIDUSAGE(CUSTOM_FORCE_DATA_REPORT, FIELD_OFFSET(SZPOOL, uSzCustomData)),
|
|
};
|
|
|
|
|
|
static PIDREPORT PoolSz =
|
|
{
|
|
HidP_Feature,
|
|
HID_USAGE_PAGE_PID,
|
|
HID_USAGE_PID_PARAMETER_BLOCK_SIZE,
|
|
cbX(SZPOOL),
|
|
cA(c_rgUsgPoolSz),
|
|
c_rgUsgPoolSz
|
|
};
|
|
|
|
|
|
PIDREPORT g_PoolReport =
|
|
{
|
|
HidP_Feature,
|
|
HID_USAGE_PAGE_PID,
|
|
HID_USAGE_PID_POOL_REPORT,
|
|
cbX(REPORTPOOL),
|
|
cA(c_rgUsgPool),
|
|
c_rgUsgPool
|
|
};
|
|
|
|
static PIDSUPPORT g_PoolSupport[] =
|
|
{
|
|
{PID_DEVICEMANAGED, PIDMAKEUSAGEDWORD(DEVICE_MANAGED_POOL), HID_BUTTON, HidP_Feature},
|
|
{PID_SHAREDPARAM, PIDMAKEUSAGEDWORD(SHARED_PARAMETER_BLOCKS), HID_BUTTON, HidP_Feature},
|
|
};
|
|
|
|
|
|
#pragma END_CONST_DATA
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* PID_InitSharedMem
|
|
*
|
|
* Inits our Shared Memory
|
|
*
|
|
*****************************************************************************/
|
|
|
|
HRESULT INTERNAL
|
|
PID_InitSharedMem
|
|
(
|
|
IDirectInputEffectDriver *ped
|
|
)
|
|
{
|
|
HRESULT hres = S_OK;
|
|
CPidDrv *this = (CPidDrv *)ped;
|
|
|
|
EnterProcI( PID_InitSharedMem, (_"x", ped));
|
|
|
|
// Get hold of global memory to keep the EffectState
|
|
if( SUCCEEDED(hres) )
|
|
{
|
|
UINT unitID;
|
|
hres = DIERR_PID_NOTINITIALIZED;
|
|
WaitForSingleObject(g_hmtxShared, INFINITE);
|
|
for(unitID = 0; unitID < MAX_UNITS; unitID++)
|
|
{
|
|
|
|
GUID* pGuid = &g_pshmem->rgus[unitID].GuidInstance;
|
|
#ifdef DEBUG
|
|
TCHAR lpName[MAX_PATH];
|
|
NameFromGUID(lpName, pGuid);
|
|
|
|
SquirtSqflPtszV(sqfl | sqflVerbose,
|
|
TEXT("%s:UnitId(%d): GUID %s"),
|
|
s_tszProc, unitID, lpName );
|
|
#endif
|
|
|
|
if( IsEqualGUID(pGuid, &this->GuidInstance) )
|
|
{
|
|
this->iUnitStateOffset = (&g_pshmem->rgus[unitID] - (PUNITSTATE)g_pshmem);
|
|
hres = S_OK;
|
|
} else if( IsEqualGUID(pGuid, &GUID_NULL ) )
|
|
{
|
|
PUNITSTATE pUnitState;
|
|
this->iUnitStateOffset = (&g_pshmem->rgus[unitID] - (PUNITSTATE)g_pshmem);
|
|
pUnitState = (PUNITSTATE)(g_pshmem + this->iUnitStateOffset);
|
|
pUnitState->GuidInstance = this->GuidInstance;
|
|
pUnitState->nAlloc = 0x0;
|
|
ZeroBuf(pUnitState->State,GLOBAL_EFFECT_MEMSZ );
|
|
hres = S_OK;
|
|
}
|
|
if( SUCCEEDED(hres) )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(SUCCEEDED(hres) )
|
|
{
|
|
PUNITSTATE pUnitState = (PUNITSTATE)(g_pshmem + this->iUnitStateOffset);
|
|
PPIDMEM pGuard = pUnitState->Guard;
|
|
INT_PTR iGuard1 = (PUCHAR)&pUnitState->Guard[0] - (PUCHAR)pUnitState, iGuard2 = (PUCHAR)&pUnitState->Guard[1] - (PUCHAR)pUnitState;
|
|
|
|
pGuard->uOfSz = PIDMEM_OFSZ(0x0, 0x0 );
|
|
pGuard->iNext = iGuard2;
|
|
|
|
pGuard++;
|
|
|
|
pGuard->uOfSz = PIDMEM_OFSZ(this->ReportPool.uRamPoolSz, 0x0);
|
|
pGuard->iNext = iGuard1;
|
|
|
|
pUnitState->nAlloc = 0x2;
|
|
|
|
pUnitState->cEfDownloaded = (USHORT)this->ReportPool.uRomETCount;
|
|
}
|
|
|
|
|
|
ReleaseMutex(g_hmtxShared);
|
|
|
|
if( FAILED(hres) )
|
|
{
|
|
SquirtSqflPtszV(sqfl | sqflError,
|
|
TEXT("%s:FAIL Could not find free unitID"),
|
|
s_tszProc );
|
|
|
|
}
|
|
}
|
|
ExitOleProc();
|
|
return hres;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* PID_InitScaling
|
|
*
|
|
* Inits Scaling Coefficients
|
|
*
|
|
*****************************************************************************/
|
|
PID_InitScaling
|
|
(
|
|
IDirectInputEffectDriver *ped
|
|
)
|
|
{
|
|
HRESULT hres = S_OK;
|
|
CPidDrv *this = (CPidDrv *)ped;
|
|
USHORT LinkCollection;
|
|
UINT indx;
|
|
EnterProc( PID_InitScaling, (_"x", ped));
|
|
|
|
|
|
// Scaling Exponents and Offsets
|
|
this->DiSEffectScale.dwSize = this->DiSEffectOffset.dwSize = sizeof(DIEFFECT); /* sizeof(DIEFFECT) */
|
|
//this->DiSEffect.dwFlags /* DiEffect* */
|
|
this->DiSEffectScale.dwDuration = DI_SECONDS ;/* Microseconds */
|
|
this->DiSEffectScale.dwSamplePeriod = DI_SECONDS ;/* Microseconds */
|
|
this->DiSEffectScale.dwGain = DI_FFNOMINALMAX;
|
|
this->DiSEffectScale.dwTriggerButton = 0x0; /* or DIEB_NOTRIGGER */
|
|
this->DiSEffectScale.dwTriggerRepeatInterval = DI_SECONDS; /* Microseconds */
|
|
//this->DiSEffect.cAxes; /* Number of axes */
|
|
//this->DiSEffect.rgdwAxes; /* Array of axes */
|
|
//this->DiSEffect.rglDirection; /* Array of directions */
|
|
//this->DiSEffect.lpEnvelope; /* Optional */
|
|
//this->DiSEffect.cbTypeSpecificParams; /* Size of params */
|
|
//this->DiSEffect.lpvTypeSpecificParams; /* Pointer to params */
|
|
#if DIRECTINPUT_VERSION >= 0x600
|
|
this->DiSEffectScale.dwStartDelay = DI_SECONDS; // Start delay
|
|
#endif
|
|
|
|
|
|
if(SUCCEEDED(PID_GetLinkCollectionIndex(ped, g_Effect.UsagePage, g_Effect.Collection, 0x0, &LinkCollection )))
|
|
{
|
|
PID_ComputeScalingFactors(ped, &g_Effect, LinkCollection, &this->DiSEffectScale, this->DiSEffectScale.dwSize, &this->DiSEffectOffset, this->DiSEffectOffset.dwSize);
|
|
}
|
|
|
|
this->DiSEnvScale.dwSize = this->DiSEnvOffset.dwSize = sizeof(DIENVELOPE); /* sizeof(DIENVELOPE) */
|
|
this->DiSEnvScale.dwAttackLevel = DI_FFNOMINALMAX;
|
|
this->DiSEnvScale.dwAttackTime = DI_SECONDS; /* Microseconds */
|
|
this->DiSEnvScale.dwFadeLevel = DI_FFNOMINALMAX;
|
|
this->DiSEnvScale.dwFadeTime = DI_SECONDS; /* Microseconds */
|
|
|
|
|
|
if(SUCCEEDED(PID_GetLinkCollectionIndex(ped, g_Envelope.UsagePage, g_Envelope.Collection, 0x0, &LinkCollection)))
|
|
{
|
|
PID_ComputeScalingFactors(ped, &g_Envelope, LinkCollection, &this->DiSEnvScale, this->DiSEnvScale.dwSize, &this->DiSEnvOffset, this->DiSEnvOffset.dwSize);
|
|
}
|
|
|
|
this->DiSPeriodicScale.dwMagnitude = DI_FFNOMINALMAX;
|
|
this->DiSPeriodicScale.lOffset = DI_FFNOMINALMAX;
|
|
this->DiSPeriodicScale.dwPhase = 360 * DI_DEGREES;
|
|
this->DiSPeriodicScale.dwPeriod = DI_SECONDS;
|
|
|
|
if(SUCCEEDED(PID_GetLinkCollectionIndex(ped, g_Periodic.UsagePage, g_Periodic.Collection, 0x0, &LinkCollection)))
|
|
{
|
|
PID_ComputeScalingFactors(ped, &g_Periodic, LinkCollection, &this->DiSPeriodicScale, cbX(this->DiSPeriodicScale), &this->DiSPeriodicOffset, cbX(this->DiSPeriodicOffset));
|
|
}
|
|
|
|
this->DiSRampScale.lStart =
|
|
this->DiSRampScale.lEnd = DI_FFNOMINALMAX;
|
|
|
|
|
|
if(SUCCEEDED(PID_GetLinkCollectionIndex(ped, g_Ramp.UsagePage, g_Ramp.Collection, 0x0, &LinkCollection)))
|
|
{
|
|
PID_ComputeScalingFactors(ped, &g_Ramp, LinkCollection, &this->DiSRampScale, cbX(this->DiSRampScale), &this->DiSRampOffset, cbX(this->DiSRampOffset));
|
|
}
|
|
|
|
this->DiSCondScale.lOffset =
|
|
this->DiSCondScale.lPositiveCoefficient =
|
|
this->DiSCondScale.lNegativeCoefficient =
|
|
this->DiSCondScale.dwPositiveSaturation =
|
|
this->DiSCondScale.dwNegativeSaturation =
|
|
this->DiSCondScale.lDeadBand = DI_FFNOMINALMAX;
|
|
|
|
if(SUCCEEDED(PID_GetLinkCollectionIndex(ped, g_Condition.UsagePage,g_Condition.Collection,0x0, &LinkCollection)))
|
|
{
|
|
PID_ComputeScalingFactors(ped, &g_Condition, LinkCollection, &this->DiSCondScale, cbX(this->DiSCondScale), &this->DiSCondOffset, cbX(this->DiSCondOffset));
|
|
}
|
|
|
|
this->DiSConstScale.lMagnitude = DI_FFNOMINALMAX;
|
|
|
|
if(SUCCEEDED(PID_GetLinkCollectionIndex(ped, g_Constant.UsagePage, g_Constant.Collection, 0x0, &LinkCollection)))
|
|
{
|
|
PID_ComputeScalingFactors(ped, &g_Constant, LinkCollection, &this->DiSConstScale,cbX(this->DiSConstScale), &this->DiSConstOffset,cbX(this->DiSConstOffset));
|
|
}
|
|
|
|
this->DiSCustomScale.dwSamplePeriod = DI_SECONDS;
|
|
|
|
if(SUCCEEDED(PID_GetLinkCollectionIndex(ped, g_Custom.UsagePage, g_Custom.Collection, 0x0, &LinkCollection)))
|
|
{
|
|
PID_ComputeScalingFactors(ped, &g_Custom, LinkCollection, &this->DiSCustomScale, cbX(this->DiSCustomScale), &this->DiSCustomOffset, cbX(this->DiSCustomOffset));
|
|
}
|
|
|
|
|
|
|
|
// Direction could be ordinals
|
|
g_Direction.cbXData = cA(c_rgUsgOrdinals)*cbX(DWORD);
|
|
g_Direction.cAPidUsage = cA(c_rgUsgOrdinals);
|
|
g_Direction.rgPidUsage = c_rgUsgOrdinals;
|
|
|
|
if(SUCCEEDED(PID_GetLinkCollectionIndex(ped, g_Direction.UsagePage, g_Direction.Collection, 0x0, &LinkCollection)))
|
|
{
|
|
HRESULT hres1;
|
|
|
|
for(indx = 0x0; indx < MAX_ORDINALS; indx++)
|
|
{
|
|
this->DiSEffectAngleScale[indx] = 360 * DI_DEGREES;
|
|
}
|
|
|
|
hres1 = PID_ComputeScalingFactors(ped, &g_Direction, LinkCollection, &this->DiSEffectAngleScale[0], cbX(this->DiSEffectAngleScale), &this->DiSEffectAngleOffset[0], cbX(this->DiSEffectAngleOffset));
|
|
|
|
// Direction could be angles
|
|
if(hres1 == E_NOTIMPL )
|
|
{
|
|
g_Direction.cbXData = cA(c_rgUsgDirection)*cbX(DWORD);
|
|
g_Direction.cAPidUsage = cA(c_rgUsgDirection);
|
|
g_Direction.rgPidUsage = c_rgUsgDirection;
|
|
|
|
// Reset the nominal values
|
|
for(indx = 0x0; indx < MAX_ORDINALS; indx++)
|
|
{
|
|
this->DiSEffectAngleScale[indx] = 360 * DI_DEGREES;
|
|
}
|
|
|
|
hres1 = PID_ComputeScalingFactors(ped, &g_Direction, LinkCollection, &this->DiSEffectAngleScale[0], cbX(this->DiSEffectAngleScale), &this->DiSEffectAngleOffset[0], cbX(this->DiSEffectAngleOffset));
|
|
|
|
if( hres1 == E_NOTIMPL )
|
|
{
|
|
// Could be direction Vectors
|
|
// Not sure how vectors are implemented in PID
|
|
|
|
SquirtSqflPtszV(sqfl | sqflError,
|
|
TEXT("%s:FAIL Cannot understand the direction collection\n")
|
|
TEXT("\t\t Supported usages are {Rx, Ry, Rz} or {Ordinals} \n"),
|
|
s_tszProc );
|
|
}
|
|
}
|
|
}
|
|
|
|
for(indx = 0x0; indx < MAX_ORDINALS; indx++)
|
|
{
|
|
this->DiSCustomSample[indx] = DI_FFNOMINALMAX;
|
|
}
|
|
|
|
if(SUCCEEDED(PID_GetLinkCollectionIndex(ped, g_CustomSample.UsagePage, g_CustomSample.Collection, 0x0, &LinkCollection)) )
|
|
{
|
|
//get the custom data for each axis
|
|
USHORT cAValCaps = 0x1;
|
|
USAGE UsagePage;
|
|
USAGE Usage[3] = {HID_USAGE_GENERIC_X, HID_USAGE_GENERIC_Y, HID_USAGE_GENERIC_Z};
|
|
NTSTATUS ntSt[3];
|
|
int nAxis = 0;
|
|
|
|
UsagePage = HID_USAGE_PAGE_GENERIC;
|
|
|
|
for (nAxis = 0; nAxis < 3; nAxis ++)
|
|
{
|
|
cAValCaps = 0x1;
|
|
ntSt[nAxis] = HidP_GetSpecificValueCaps
|
|
(
|
|
g_CustomSample.HidP_Type,
|
|
UsagePage,
|
|
LinkCollection,
|
|
Usage[nAxis],
|
|
&this->customCaps[nAxis],
|
|
&cAValCaps,
|
|
this->ppd
|
|
);
|
|
|
|
if (FAILED(ntSt[nAxis]))
|
|
{
|
|
this->customCaps[nAxis].BitSize = 0;
|
|
this->customCaps[nAxis].LogicalMin = this->customCaps[nAxis].LogicalMax = 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if ((FAILED(ntSt[0])) && (FAILED(ntSt[1])) && (FAILED(ntSt[2])))
|
|
{
|
|
SquirtSqflPtszV(sqfl | sqflError,
|
|
TEXT("%s:FAIL Cannot understand the download force sample collection\n")
|
|
TEXT("\t\t Supported usages are {X, Y, Z} \n"),
|
|
s_tszProc );
|
|
|
|
}
|
|
|
|
|
|
//get how many bytes of custom data can send at a time
|
|
if (SUCCEEDED(PID_GetLinkCollectionIndex(ped, g_CustomData.UsagePage, g_CustomData.Collection, 0x0, &LinkCollection )))
|
|
{
|
|
|
|
USAGE UsageData = HID_USAGE_PID_CUSTOM_FORCE_DATA;
|
|
NTSTATUS ntst;
|
|
cAValCaps = 0x1;
|
|
UsagePage = HID_USAGE_PAGE_PID;
|
|
|
|
ntst = HidP_GetSpecificValueCaps
|
|
(
|
|
g_CustomData.HidP_Type,
|
|
UsagePage,
|
|
LinkCollection,
|
|
UsageData,
|
|
&this->customDataCaps,
|
|
&cAValCaps,
|
|
this->ppd
|
|
);
|
|
|
|
if (FAILED(ntst))
|
|
{
|
|
this->customDataCaps.BitSize = 0;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
ExitOleProc();
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* DIEnumProc
|
|
*
|
|
* Enum and cache FF device objects
|
|
*
|
|
*****************************************************************************/
|
|
|
|
BOOL CALLBACK
|
|
DIEnumProc(LPCDIDEVICEOBJECTINSTANCE pinst, LPVOID pv)
|
|
{
|
|
BOOL frc = DIENUM_CONTINUE;
|
|
HRESULT hres = S_OK;
|
|
CPidDrv* this = (CPidDrv*) pv;
|
|
|
|
EnterProc( DIEnumProc, (_"xx", pinst, pv ));
|
|
|
|
if( (pinst->dwFlags & DIDOI_FFACTUATOR )
|
|
||(pinst->dwFlags & DIDOI_FFEFFECTTRIGGER ))
|
|
{
|
|
AssertF(this->cFFObj <= this->cFFObjMax);
|
|
if( this->cFFObj == this->cFFObjMax )
|
|
{
|
|
/* Grow by doubling */
|
|
this->cFFObjMax = max(PIDALLOC_INIT, 2*this->cFFObjMax);
|
|
hres = ReallocCbPpv(this->cFFObjMax * cbX(DIUSAGEANDINST), &this->rgFFUsageInst);
|
|
}
|
|
|
|
if( SUCCEEDED(hres) )
|
|
{
|
|
PDIUSAGEANDINST pdiUI = this->rgFFUsageInst + this->cFFObj;
|
|
pdiUI->dwUsage = DIMAKEUSAGEDWORD(pinst->wUsagePage, pinst->wUsage);
|
|
pdiUI->dwType = pinst->dwType ;
|
|
}
|
|
|
|
this->cFFObj++;
|
|
}
|
|
if( FAILED(hres) )
|
|
{
|
|
frc = DIENUM_STOP;
|
|
}
|
|
|
|
ExitProcF(frc);
|
|
return frc;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
PID_InitFFAttributes
|
|
(
|
|
IDirectInputEffectDriver *ped
|
|
)
|
|
{
|
|
HRESULT hres = S_OK;
|
|
CPidDrv *this = (CPidDrv *)ped;
|
|
|
|
EnterProcI( PID_Init, (_"x", ped));
|
|
|
|
// We cannot call this function at init, because DInput call us before the device
|
|
// has been completely initialized.
|
|
if( this->cFFObj )
|
|
{
|
|
hres = S_FALSE; // Already initialized
|
|
} else
|
|
{
|
|
// We need to get the Usage & UsagePage
|
|
// for device objects marked as
|
|
// FF Triggers and FF Actuators
|
|
|
|
//If we are called with DInput version not larger than 7, load dinput.dll w/ IID_DirectInput7
|
|
//else load dinput8.dll.
|
|
if (this->dwDirectInputVersion <= 0x0700)
|
|
{
|
|
HINSTANCE hinst = LoadLibrary(TEXT("dinput.dll"));
|
|
if (hinst)
|
|
{
|
|
typedef HRESULT ( WINAPI * DIRECTINPUTCREATEEX) ( HINSTANCE hinst, DWORD dwVersion, REFIID riidltf, LPVOID *ppvOut, LPUNKNOWN punkOuter);
|
|
DIRECTINPUTCREATEEX _DirectInputCreateEx;
|
|
LPDIRECTINPUT lpDI;
|
|
_DirectInputCreateEx = (DIRECTINPUTCREATEEX)GetProcAddress(hinst, "DirectInputCreateEx");
|
|
if (_DirectInputCreateEx)
|
|
{
|
|
hres = _DirectInputCreateEx(g_hinst, this->dwDirectInputVersion, &IID_IDirectInput7, &lpDI, NULL );
|
|
if( SUCCEEDED(hres) )
|
|
{
|
|
LPDIRECTINPUTDEVICE pdid;
|
|
hres = IDirectInput_CreateDevice(lpDI, &this->GuidInstance, &pdid, NULL);
|
|
/* Create the device object */
|
|
if( SUCCEEDED(hres) )
|
|
{
|
|
hres = IDirectInputDevice2_EnumObjects
|
|
(
|
|
pdid,
|
|
DIEnumProc,
|
|
ped,
|
|
DIDFT_ALL //DIDFT_FFEFFECTTRIGGER | DIDFT_FFACTUATOR
|
|
);
|
|
|
|
IDirectInput_Release(pdid);
|
|
}
|
|
IDirectInput_Release(lpDI);
|
|
}
|
|
}
|
|
else //!DirectInputCreateEx
|
|
{
|
|
//Something is horribly wrong here if we can't find the Create fn!
|
|
//Return the same error code that CDIDev_CreateEffectDriver() returns if there was an error loading FF driver
|
|
hres = DIERR_UNSUPPORTED;
|
|
}
|
|
|
|
FreeLibrary(hinst);
|
|
}
|
|
else // !hinst
|
|
{
|
|
//Something is horribly wrong here if we came through Dinput but can't load it!
|
|
//Return the same error code that CDIDev_CreateEffectDriver() returns if there was an error loading FF driver
|
|
hres = DIERR_UNSUPPORTED;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
HINSTANCE hinst = LoadLibrary(TEXT("dinput8.dll"));
|
|
if (hinst)
|
|
{
|
|
typedef HRESULT ( WINAPI * DIRECTINPUT8CREATE) ( HINSTANCE hinst, DWORD dwVersion, REFIID riidltf, LPVOID *ppvOut, LPUNKNOWN punkOuter);
|
|
DIRECTINPUT8CREATE _DirectInput8Create;
|
|
LPDIRECTINPUT8 lpDI;
|
|
_DirectInput8Create = (DIRECTINPUT8CREATE)GetProcAddress(hinst, "DirectInput8Create");
|
|
if (_DirectInput8Create)
|
|
{
|
|
hres = _DirectInput8Create(g_hinst, this->dwDirectInputVersion, &IID_IDirectInput8, &lpDI, NULL );
|
|
if( SUCCEEDED(hres) )
|
|
{
|
|
LPDIRECTINPUTDEVICE8 pdid;
|
|
hres = IDirectInput8_CreateDevice(lpDI, &this->GuidInstance, &pdid, NULL);
|
|
/* Create the device object */
|
|
if( SUCCEEDED(hres) )
|
|
{
|
|
hres = IDirectInputDevice8_EnumObjects
|
|
(
|
|
pdid,
|
|
DIEnumProc,
|
|
ped,
|
|
DIDFT_ALL //DIDFT_FFEFFECTTRIGGER | DIDFT_FFACTUATOR
|
|
);
|
|
|
|
IDirectInput_Release(pdid);
|
|
}
|
|
IDirectInput_Release(lpDI);
|
|
}
|
|
}
|
|
else //!DirectInput8Create
|
|
{
|
|
//Something is horribly wrong here if we can't find the Create fn!
|
|
//Return the same error code that CDIDev_CreateEffectDriver() returns if there was an error loading FF driver
|
|
hres = DIERR_UNSUPPORTED;
|
|
}
|
|
|
|
FreeLibrary(hinst);
|
|
}
|
|
else // !hinst
|
|
{
|
|
//Something is horribly wrong here if we came through Dinput but can't load it!
|
|
//Return the same error code that CDIDev_CreateEffectDriver() returns if there was an error loading FF driver
|
|
hres = DIERR_UNSUPPORTED;
|
|
}
|
|
}
|
|
}
|
|
ExitOleProc();
|
|
return hres;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* PID_Init
|
|
*
|
|
* Inits PID device
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
PID_Init
|
|
(
|
|
IDirectInputEffectDriver *ped
|
|
)
|
|
{
|
|
HRESULT hres = S_OK;
|
|
CPidDrv *this = (CPidDrv *)ped;
|
|
USHORT LinkCollection;
|
|
|
|
EnterProcI( PID_Init, (_"x", ped));
|
|
|
|
PID_CreateUsgTxt();
|
|
|
|
AssertF( this->hdev == INVALID_HANDLE_VALUE );
|
|
|
|
if( SUCCEEDED(hres) )
|
|
{
|
|
this->hdev = CreateFile(this->tszDeviceInterface,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
0, /* no SECURITY_ATTRIBUTES */
|
|
OPEN_EXISTING,
|
|
0x0, /* attributes */
|
|
0); /* template */
|
|
|
|
if( this->hdev == INVALID_HANDLE_VALUE )
|
|
{
|
|
hres = E_HANDLE;
|
|
|
|
SquirtSqflPtszV(sqfl | sqflError,
|
|
TEXT("%s:FAIL CreateFile"),
|
|
s_tszProc );
|
|
|
|
}
|
|
}
|
|
|
|
if( SUCCEEDED(hres) )
|
|
{
|
|
// Get all the HID goo
|
|
if( HidD_GetAttributes(this->hdev, &this->attr) &&
|
|
HidD_GetPreparsedData(this->hdev, &this->ppd) &&
|
|
SUCCEEDED(HidP_GetCaps(this->ppd, &this->caps)) )
|
|
{
|
|
// Success
|
|
} else
|
|
{
|
|
SquirtSqflPtszV(sqfl | sqflError,
|
|
TEXT("%s: FAIL HID init "),
|
|
s_tszProc );
|
|
|
|
hres = DIERR_PID_NOTINITIALIZED;
|
|
}
|
|
}
|
|
|
|
if( SUCCEEDED(hres) )
|
|
{
|
|
// Get collection info
|
|
hres = AllocCbPpv(cbX(*this->pLinkCollection) * this->caps.NumberLinkCollectionNodes,
|
|
&this->pLinkCollection);
|
|
|
|
if( SUCCEEDED(hres) && (this->pLinkCollection != NULL) )
|
|
{
|
|
ULONG cALinkCollection=this->caps.NumberLinkCollectionNodes;
|
|
|
|
hres = HidP_GetLinkCollectionNodes
|
|
(
|
|
this->pLinkCollection,
|
|
&cALinkCollection,
|
|
this->ppd
|
|
);
|
|
}
|
|
}
|
|
|
|
if(SUCCEEDED(hres) )
|
|
{
|
|
UINT indx;
|
|
this->cbReport[HidP_Input] = this->caps.InputReportByteLength;
|
|
this->cbReport[HidP_Output] = this->caps.OutputReportByteLength;
|
|
this->cbReport[HidP_Feature] = this->caps.FeatureReportByteLength;
|
|
//write reports are output reports
|
|
for( indx = 0x0; indx < MAX_BLOCKS; indx++ )
|
|
{
|
|
this->cbWriteReport[indx] = this->caps.OutputReportByteLength;
|
|
}
|
|
|
|
for( indx = 0x0; indx < HidP_Max; indx++ )
|
|
{
|
|
hres = AllocCbPpv(this->cbReport[indx], &this->pReport[indx]);
|
|
if( FAILED(hres) )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
for( indx = 0x0; indx < MAX_BLOCKS; indx++ )
|
|
{
|
|
hres = AllocCbPpv(this->cbWriteReport[indx], &this->pWriteReport[indx]);
|
|
if( FAILED(hres) )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( SUCCEEDED(hres) )
|
|
{
|
|
hres = PID_InitRegistry(ped);
|
|
}
|
|
|
|
if(SUCCEEDED(PID_GetLinkCollectionIndex(ped,g_PoolReport.UsagePage,g_PoolReport.Collection,0x0,&LinkCollection)))
|
|
{
|
|
PUCHAR pReport = this->pReport[g_PoolReport.HidP_Type];
|
|
UINT cbReport = this->cbReport[g_PoolReport.HidP_Type];
|
|
|
|
PID_GetReport
|
|
(ped,
|
|
&g_PoolReport,
|
|
LinkCollection,
|
|
pReport,
|
|
cbReport
|
|
);
|
|
|
|
PID_ParseReport
|
|
(
|
|
ped,
|
|
&g_PoolReport,
|
|
LinkCollection,
|
|
&this->ReportPool,
|
|
cbX(this->ReportPool),
|
|
pReport,
|
|
cbReport
|
|
);
|
|
}
|
|
|
|
|
|
SquirtSqflPtszV(sqfl | sqflVerbose,
|
|
TEXT("%s:RamPoolSz:0x%x"),
|
|
s_tszProc, this->ReportPool.uRamPoolSz );
|
|
|
|
if(SUCCEEDED(PID_GetLinkCollectionIndex(ped,PoolSz.UsagePage,PoolSz.Collection,0x0,&LinkCollection)) )
|
|
{
|
|
PUCHAR pReport = this->pReport[PoolSz.HidP_Type];
|
|
UINT cbReport = this->cbReport[PoolSz.HidP_Type];
|
|
|
|
PID_GetReport
|
|
(ped,
|
|
&PoolSz,
|
|
LinkCollection,
|
|
pReport,
|
|
cbReport
|
|
);
|
|
|
|
PID_ParseReport
|
|
(
|
|
ped,
|
|
&PoolSz,
|
|
LinkCollection,
|
|
&this->SzPool,
|
|
cbX(this->SzPool),
|
|
pReport,
|
|
cbReport
|
|
);
|
|
}
|
|
|
|
|
|
PID_Support(ped, cA(g_PoolSupport), g_PoolSupport, &this->uDeviceManaged);
|
|
|
|
// Determine max number of parameter blocks per effect ?
|
|
if( SUCCEEDED(hres) )
|
|
{
|
|
USHORT LinkCollection;
|
|
hres = PID_GetLinkCollectionIndex(ped, HID_USAGE_PAGE_PID, HID_USAGE_PID_TYPE_SPECIFIC_BLOCK_OFFSET, 0x0, &LinkCollection );
|
|
|
|
if( SUCCEEDED(hres) )
|
|
{
|
|
USHORT cAValCaps;
|
|
cAValCaps = 0x0;
|
|
|
|
HidP_GetSpecificValueCaps
|
|
(
|
|
HidP_Output,
|
|
HID_USAGE_PAGE_ORDINALS,
|
|
LinkCollection,
|
|
0x0,
|
|
NULL,
|
|
&cAValCaps,
|
|
this->ppd
|
|
);
|
|
this->cMaxParameters = cAValCaps;
|
|
} else
|
|
{
|
|
this->cMaxParameters = 0x2;
|
|
hres = S_OK;
|
|
}
|
|
}
|
|
|
|
|
|
if( SUCCEEDED(hres))
|
|
{
|
|
hres = PID_InitSharedMem(ped);
|
|
}
|
|
|
|
if( SUCCEEDED(hres ) )
|
|
{
|
|
hres = PID_InitScaling(ped);
|
|
}
|
|
|
|
if( SUCCEEDED(hres) )
|
|
{
|
|
// Determine Max effects that can be downloaded to the device
|
|
HIDP_VALUE_CAPS ValCaps;
|
|
USHORT cAValCaps = 0x1;
|
|
USAGE UsagePage = DIGETUSAGEPAGE(g_BlockIndex.rgPidUsage[0].dwUsage);
|
|
USAGE Usage = DIGETUSAGE(g_BlockIndex.rgPidUsage[0].dwUsage);
|
|
hres = HidP_GetSpecificValueCaps
|
|
(
|
|
g_BlockIndex.HidP_Type,
|
|
UsagePage,
|
|
0x0,
|
|
Usage,
|
|
&ValCaps,
|
|
&cAValCaps,
|
|
this->ppd
|
|
);
|
|
|
|
if( SUCCEEDED(hres) || ( hres == HIDP_STATUS_BUFFER_TOO_SMALL ) )
|
|
{
|
|
hres = S_OK;
|
|
this->cMaxEffects = (USHORT) ( ValCaps.PhysicalMax - ValCaps.PhysicalMin );
|
|
} else
|
|
{
|
|
SquirtSqflPtszV(sqfl | sqflError,
|
|
TEXT("%s: FAIL HidP_GetValCaps for (%x %x:%s) "),
|
|
s_tszProc , UsagePage, Usage, PIDUSAGETXT(UsagePage,Usage) );
|
|
}
|
|
}
|
|
|
|
this->cMaxEffects = (USHORT)min(this->cMaxEffects,
|
|
GLOBAL_EFFECT_MEMSZ / ((FIELD_OFFSET(EFFECTSTATE,PidMem)) + this->cMaxParameters*cbX(PIDMEM)) );
|
|
|
|
|
|
if( this->ReportPool.uSimulEfMax == 0x0 )
|
|
{
|
|
this->ReportPool.uSimulEfMax = 0xff;
|
|
|
|
SquirtSqflPtszV(sqfl | sqflError,
|
|
TEXT("%s: FAIL HID dwSimulEfMax == 0x0 defaults to %d "),
|
|
s_tszProc, this->cMaxEffects );
|
|
}
|
|
|
|
|
|
if( SUCCEEDED(hres) )
|
|
{
|
|
TCHAR tsz[MAX_PATH];
|
|
|
|
AssertF(this->hThread == 0x0 );
|
|
AssertF(this->hWrite == 0x0);
|
|
AssertF(this->hWriteComplete == 0x0);
|
|
|
|
if( GetModuleFileName(g_hinst, tsz, cA(tsz))
|
|
&&LoadLibrary(tsz) == g_hinst)
|
|
{
|
|
InterlockedIncrement(&this->cThreadRef);
|
|
AssertF(this->cThreadRef == 0x1 );
|
|
AssertF(this->hThread == 0x0 );
|
|
|
|
this->hWrite = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
if (this->hWrite == 0x0)
|
|
{
|
|
goto event_thread_error;
|
|
}
|
|
this->hWriteComplete = CreateEvent(NULL, TRUE, TRUE, NULL);
|
|
if (this->hWriteComplete == 0x0)
|
|
{
|
|
goto event_thread_error;
|
|
}
|
|
this->hThread= CreateThread(0, 0, (LPTHREAD_START_ROUTINE)PID_ThreadProc, this,
|
|
0, &this->idThread);
|
|
|
|
if (this->hThread == 0x0)
|
|
{
|
|
event_thread_error:;
|
|
//close the event handles
|
|
if (this->hWrite != 0x0)
|
|
{
|
|
CloseHandle(this->hWrite);
|
|
this->hWrite = 0x0;
|
|
}
|
|
if (this->hWriteComplete != 0x0)
|
|
{
|
|
CloseHandle(this->hWriteComplete);
|
|
this->hWriteComplete = 0x0;
|
|
}
|
|
|
|
hres = DIERR_PID_NOTINITIALIZED;
|
|
FreeLibrary(g_hinst);
|
|
InterlockedDecrement(&this->cThreadRef);
|
|
}
|
|
}
|
|
}
|
|
|
|
ExitOleProc();
|
|
return hres;
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* PID_Finalize
|
|
*
|
|
* Destroys PID device specific memory
|
|
*
|
|
*****************************************************************************/
|
|
STDMETHODIMP
|
|
PID_Finalize
|
|
(
|
|
IDirectInputEffectDriver *ped
|
|
)
|
|
{
|
|
HRESULT hres = S_OK;
|
|
CPidDrv *this = (CPidDrv *)ped;
|
|
HANDLE hdev;
|
|
UINT indx;
|
|
|
|
EnterProc( PID_Finalize, (_"x", ped));
|
|
|
|
DllEnterCrit();
|
|
|
|
// Assasinate the thread
|
|
InterlockedDecrement(&this->cThreadRef);
|
|
|
|
AssertF(this->cThreadRef == 0x0 );
|
|
// Wait for the thread to die before we go about releasing
|
|
// memory
|
|
|
|
do
|
|
{
|
|
DWORD dwWait;
|
|
|
|
NudgeWorkerThread(this->idThread);
|
|
Sleep(0);
|
|
|
|
dwWait = WaitForSingleObject(this->hThread, 500 ) ;
|
|
|
|
if( WAIT_TIMEOUT == dwWait)
|
|
{
|
|
SquirtSqflPtszV(sqfl | sqflError,
|
|
TEXT("%s: Waiting for worker Thread %d to die"),
|
|
s_tszProc,this->idThread );
|
|
}
|
|
|
|
//if didn't timeout, and thread didn't die, then we can get into an infinite loop.
|
|
//so close the handle.
|
|
if ((WAIT_ABANDONED == dwWait) || (WAIT_FAILED == dwWait))
|
|
{
|
|
if( this->hdevOvrlp != INVALID_HANDLE_VALUE )
|
|
{
|
|
HANDLE hdevOvr;
|
|
hdevOvr = this->hdevOvrlp;
|
|
this->hdevOvrlp = INVALID_HANDLE_VALUE;
|
|
CancelIo_(hdevOvr);
|
|
Sleep(0);
|
|
CloseHandle(hdevOvr);
|
|
}
|
|
|
|
AssertF(this->hdevOvrlp == INVALID_HANDLE_VALUE);
|
|
|
|
}
|
|
|
|
}while( this->hdevOvrlp != INVALID_HANDLE_VALUE );
|
|
|
|
// Close the handle
|
|
if( this->hdev != INVALID_HANDLE_VALUE)
|
|
{
|
|
hdev = this->hdev;
|
|
this->hdev = INVALID_HANDLE_VALUE;
|
|
CloseHandle(hdev);
|
|
}
|
|
|
|
// Free PreParseData
|
|
if( this->ppd )
|
|
{
|
|
HidD_FreePreparsedData(this->ppd);
|
|
this->ppd = NULL;
|
|
}
|
|
|
|
// Free HIDP_VALUE_CAPS data
|
|
FreePpv(&this->rgFFUsageInst);
|
|
FreePpv(&this->pLinkCollection);
|
|
|
|
for(indx = 0x0; indx < HidP_Max; indx++ )
|
|
{
|
|
FreePpv(&this->pReport[indx]);
|
|
}
|
|
for(indx = 0x0; indx < MAX_BLOCKS; indx++ )
|
|
{
|
|
FreePpv(&this->pWriteReport[indx]);
|
|
}
|
|
|
|
DllLeaveCrit();
|
|
ExitOleProc();
|
|
return hres;
|
|
}
|
|
|