/***************************************************************************** * * PidEff.c * Copyright (c) 1999 Microsoft Corporation. All Rights Reserved. * * Abstract: * * Download PID Effect Block. * *****************************************************************************/ #include "pidpr.h" #define sqfl ( sqflEff ) #pragma BEGIN_CONST_DATA /* * The structure c_rgUsgEffects, aids in translating elements in the DIEFFECT * structure to PID usages */ static PIDUSAGE c_rgUsgEffect[] = { MAKE_PIDUSAGE(DURATION, FIELD_OFFSET(DIEFFECT,dwDuration) ), MAKE_PIDUSAGE(SAMPLE_PERIOD, FIELD_OFFSET(DIEFFECT,dwSamplePeriod) ), MAKE_PIDUSAGE(GAIN, FIELD_OFFSET(DIEFFECT,dwGain) ), MAKE_PIDUSAGE(TRIGGER_BUTTON, FIELD_OFFSET(DIEFFECT,dwTriggerButton) ), MAKE_PIDUSAGE(TRIGGER_REPEAT_INTERVAL,FIELD_OFFSET(DIEFFECT,dwTriggerRepeatInterval) ), #if DIRECTINPUT_VERSION >= 0x600 MAKE_PIDUSAGE(START_DELAY ,FIELD_OFFSET(DIEFFECT,dwStartDelay)), #endif }; /* * g_Effect provides context to the c_rgUsgEffect struct */ PIDREPORT g_Effect = { HidP_Output, // Effect Blocks can only be output reports HID_USAGE_PAGE_PID, // Usage Page HID_USAGE_PID_SET_EFFECT_REPORT, // Collection cbX(DIEFFECT), // Size of incoming data cA(c_rgUsgEffect), // number of elements in c_rgUsgEffect c_rgUsgEffect // how elements of DIEFFECT are translated to PID }; /* * Effect block index to PID usage */ static PIDUSAGE c_rgUsgBlockIndex[] = { MAKE_PIDUSAGE(EFFECT_BLOCK_INDEX, 0x0 ), }; /* * For some PID transactions block index is output report */ PIDREPORT g_BlockIndex = { HidP_Output, // Report Type HID_USAGE_PAGE_PID, // Usage Page 0x0, // Any collection cbX(DWORD), // size of incoming data cA(c_rgUsgBlockIndex), // translation table for effect block index to PID usages c_rgUsgBlockIndex }; /* * In the PID state report, block index is an input report */ PIDREPORT g_BlockIndexIN = { HidP_Input, HID_USAGE_PAGE_PID, HID_USAGE_PID_STATE_REPORT, cbX(DWORD), cA(c_rgUsgBlockIndex), c_rgUsgBlockIndex }; //CAssertF(MAX_ORDINALS == cA(c_rgUsgOrdinals)); PIDREPORT g_TypeSpBlockOffset = { HidP_Output, // For PID ordinals output reports HID_USAGE_PAGE_PID, // Usage Page HID_USAGE_PID_TYPE_SPECIFIC_BLOCK_OFFSET, cA(c_rgUsgOrdinals)*cbX(DWORD), // sizeof incoming data cA(c_rgUsgOrdinals), // number of elements c_rgUsgOrdinals // translation table }; #pragma END_CONST_DATA PIDREPORT g_Direction = { HidP_Output, // For PID ordinals output reports HID_USAGE_PAGE_PID, // Usage Page HID_USAGE_PID_DIRECTION, 0x0, 0x0, NULL }; /***************************************************************************** * * hresFinddwUsageFromdwFlags * * Given the flags for a DEVICEOBJECTINSTANCE, find the usage and usage page * On init we enum the device and cache the * DeviceObjects marked as actuators and Effect Triggers. * *****************************************************************************/ HRESULT hresFinddwUsageFromdwFlags ( IDirectInputEffectDriver *ped, DWORD dwFlags, DWORD *pdwUsage ) { HRESULT hres = S_OK; CPidDrv *this = (CPidDrv *)ped; EnterProcI( PID_hresFinddwUsageFromdwFlags, (_"xxx", ped, dwFlags, pdwUsage )); // Init FF attributes hres = PID_InitFFAttributes(ped); if( SUCCEEDED(hres) ) { /* Better be a FF object ( actuator / Trigger ) */ if( dwFlags & DIDFT_FFACTUATOR || dwFlags & DIDFT_FFEFFECTTRIGGER ) { hres = S_OK; } else { SquirtSqflPtszV(sqfl | sqflError, TEXT("%s:FAIL dwFlags(0x%x) not FFACTUATOR | FFEFFECTTRIGGER "), s_tszProc, dwFlags ); hres = E_UNEXPECTED; } if( SUCCEEDED(hres) ) { UINT cFFObj; hres = E_NOTIMPL; /* Loop through the all the objects we found during enum */ for(cFFObj = 0x0; cFFObj < this->cFFObj; cFFObj++ ) { PDIUSAGEANDINST pdiUI = this->rgFFUsageInst + cFFObj; if( pdiUI->dwType == dwFlags ) { *pdwUsage = pdiUI->dwUsage; hres = S_OK; break; } } } } if( FAILED(hres) ) { SquirtSqflPtszV(sqfl | sqflError, TEXT("%s:FAIL No mapping for dwFlags(0x%x) "), s_tszProc, dwFlags ); } ExitOleProc(); return hres; } /***************************************************************************** * * PID_NewEffectIndex * * Gets a new effect index. * * For host managed devices, we assign an unused effect ID. * For device managed, we get the effectID from the device * *****************************************************************************/ STDMETHODIMP PID_NewEffectIndex ( IDirectInputEffectDriver *ped, LPDIEFFECT peff, DWORD dwEffectId, PDWORD pdwEffect ) { CPidDrv *this = (CPidDrv *)ped; HRESULT hres; USHORT dwEffect; EnterProcI(PID_NewEffectIndex, (_"xx", this, pdwEffect)); AssertF(*pdwEffect == 0); // Default assumption is that the device is full. hres = DIERR_DEVICEFULL; if( this->uDeviceManaged & PID_DEVICEMANAGED ) { PVOID pReport; UINT cbReport; USHORT LinkCollection = 0x0; USHORT TLinkCollection = 0x0; UINT nUsages = 0x1; USAGE Usage; USAGE Collection = HID_USAGE_PID_CREATE_NEW_EFFECT; USAGE UsagePage; HIDP_REPORT_TYPE HidP_Type = HidP_Feature; cbReport = this->cbReport[HidP_Type]; pReport = this->pReport[HidP_Type]; ZeroBuf(pReport, cbReport); // Usage and Usage page determine type of new effect Usage = DIGETUSAGE(dwEffectId); UsagePage = DIGETUSAGEPAGE(dwEffectId); hres = PID_GetLinkCollectionIndex(ped, UsagePage, Collection, 0x0, &LinkCollection ); if( SUCCEEDED(hres) ) { Collection = HID_USAGE_PID_EFFECT_TYPE; hres = PID_GetLinkCollectionIndex(ped, UsagePage, Collection, LinkCollection, &TLinkCollection ); } if( SUCCEEDED(hres) ) { hres = HidP_SetUsages ( HidP_Type, UsagePage, TLinkCollection, &Usage, &nUsages, this->ppd, pReport, cbReport); } if( SUCCEEDED(hres) && PIDMAKEUSAGEDWORD(ET_CUSTOM) == dwEffectId ) { DICUSTOMFORCE DiParam; LONG lValue; int nBytes; AssertF(peff->cbTypeSpecificParams <= cbX(DiParam) ); memcpy(&DiParam, peff->lpvTypeSpecificParams, cbX(DiParam)); //how many bytes do we need per sample? nBytes = ( this->customCaps[ 0].BitSize + this->customCaps[ 1].BitSize + this->customCaps[ 2].BitSize)/8; lValue = DiParam.cSamples * nBytes; hres = HidP_SetScaledUsageValue ( HidP_Type, HID_USAGE_PAGE_GENERIC, LinkCollection, HID_USAGE_GENERIC_BYTE_COUNT, lValue, this->ppd, pReport, cbReport ); } // Send the report if( SUCCEEDED(hres) ) { hres = PID_SendReport(ped, pReport, cbReport, HidP_Type, TRUE, 0, 1); } // Get back the effect ID if( SUCCEEDED(hres) ) { PIDREPORT BlockIndex = g_BlockIndex; USHORT LinkCollection; BlockIndex.Collection = HID_USAGE_PID_BLOCK_LOAD_REPORT; BlockIndex.HidP_Type = HidP_Feature; hres = PID_GetLinkCollectionIndex (ped, BlockIndex.UsagePage, BlockIndex.Collection, 0x0, &LinkCollection); if( SUCCEEDED(hres) ) { PUCHAR pReport = this->pReport[BlockIndex.HidP_Type]; UINT cbReport = this->cbReport[BlockIndex.HidP_Type]; PID_GetReport(ped, &BlockIndex, LinkCollection, pReport, cbReport ); // Get the EffectIndex hres = PID_ParseReport ( ped, &BlockIndex, LinkCollection, pdwEffect, cbX(*pdwEffect), pReport, cbReport ); if( SUCCEEDED(hres ) ) { NTSTATUS ntStat; USAGE rgUsageList[MAX_BUTTONS]; UINT cUsageList = MAX_BUTTONS; PID_GetLinkCollectionIndex(ped, HID_USAGE_PAGE_PID, HID_USAGE_PID_BLOCK_LOAD_STATUS, LinkCollection, &LinkCollection ); ntStat = HidP_GetUsages ( BlockIndex.HidP_Type, HID_USAGE_PAGE_PID, LinkCollection, rgUsageList, &cUsageList, this->ppd, pReport, cbReport); if(SUCCEEDED(ntStat) ) { if (cUsageList != 0) { if( rgUsageList[0] == HID_USAGE_PID_BLOCK_LOAD_FULL ) { hres = DIERR_DEVICEFULL; } else if(rgUsageList[0] == HID_USAGE_PID_BLOCK_LOAD_ERROR ) { hres = DIERR_PID_BLOCKLOADERROR; } else { AssertF(rgUsageList[0] == HID_USAGE_PID_BLOCK_LOAD_SUCCESS); } } else { //because of issues w/ some chipsets (see Whistler bugs 231235, 304863), //cUsageList can be 0. //so warn the user. RPF(TEXT("Unable to get the effect load status -- may be a USB chipset issue!")); RPF(TEXT("The effect may not play correctly!")); } } } if(SUCCEEDED(hres)) { NTSTATUS ntStat; UsagePage = HID_USAGE_PAGE_PID; Usage = HID_USAGE_PID_RAMPOOL_AVAILABLE; ntStat = HidP_GetScaledUsageValue ( HidP_Feature, UsagePage, LinkCollection, Usage, &this->dwUsedMem, this->ppd, pReport, cbReport ); if(FAILED(ntStat) ) { // Reset the amount of used memory this->dwUsedMem = 0x0 ; SquirtSqflPtszV(sqfl | sqflError, TEXT("%s: FAIL HidP_GetScaledUsageValue:0x%x for(%x, %x,%x:%s)"), s_tszProc, ntStat, LinkCollection, UsagePage, Usage, PIDUSAGETXT(UsagePage,Usage) ); } } } } if( SUCCEEDED(hres) ) { PEFFECTSTATE pEffectState = PeffectStateFromBlockIndex(this,*pdwEffect); // Serialize access to for new effect WaitForSingleObject(g_hmtxShared, INFINITE); AssertF(! (pEffectState->lEfState & PID_EFFECT_BUSY )); pEffectState->lEfState |= PID_EFFECT_BUSY; hres = S_OK; ReleaseMutex(g_hmtxShared); } } else { // Serialize access to common memory block WaitForSingleObject(g_hmtxShared, INFINITE); for(dwEffect = 1; dwEffect <= this->cMaxEffects; dwEffect++) { PEFFECTSTATE pEffectState = PeffectStateFromBlockIndex(this,dwEffect); if( ! ( pEffectState->lEfState & PID_EFFECT_BUSY ) ) { pEffectState->lEfState |= PID_EFFECT_BUSY; *pdwEffect = dwEffect; ZeroBuf(pEffectState->PidMem, cbX(pEffectState->PidMem[0]) * this->cMaxParameters ); hres = S_OK; break; } } ReleaseMutex(g_hmtxShared); } if( SUCCEEDED(hres) ) { ((PUNITSTATE)(g_pshmem + this->iUnitStateOffset))->cEfDownloaded++; } else { SquirtSqflPtszV(sqfl | sqflError, TEXT("%s:FAIL Could not create new effects, already have %d "), s_tszProc, ((PUNITSTATE)(g_pshmem + this->iUnitStateOffset))->cEfDownloaded ); } ExitOleProc(); return hres; } /***************************************************************************** * * PID_ValidateEffectIndex * * Validates an effect index. * *****************************************************************************/ STDMETHODIMP PID_ValidateEffectIndex ( IDirectInputEffectDriver *ped, DWORD dwEffect ) { CPidDrv *this = (CPidDrv *)ped; HRESULT hres; PEFFECTSTATE pEffectState = PeffectStateFromBlockIndex(this, dwEffect); EnterProc(PID_ValidateEffectIndex, (_"xx", this, dwEffect)); if( pEffectState->lEfState & PID_EFFECT_BUSY ) { hres = S_OK; } else { hres = E_HANDLE; } ExitOleProc(); return hres; } /***************************************************************************** * * PID_DestroyEffect * * Remove an effect from the device. * * If the effect is playing, the driver should stop it * before unloading it. * * dwId * * The external joystick number being addressed. * * dwEffect * * The effect to be destroyed. * * Returns: * * S_OK on success. * * Any other DIERR_* error code may be returned. * * Private driver-specific error codes in the range * DIERR_DRIVERFIRST through DIERR_DRIVERLAST * may be returned. * * * Makes an effect Index available for reuse. Deallocates parameter block * memory. * *****************************************************************************/ STDMETHODIMP PID_DestroyEffect ( IDirectInputEffectDriver *ped, DWORD dwId, DWORD dwEffect ) { CPidDrv *this = (CPidDrv *)ped; HRESULT hres=S_OK; EnterProc(PID_DestroyEffectIndex, (_"xx", this, dwEffect)); DllEnterCrit(); // Stop the Effect hres = PID_EffectOperation ( ped, dwId, dwEffect, PID_DIES_STOP, 0x0, TRUE, 0, 1 ); if(SUCCEEDED(hres) && ( this->uDeviceManaged & PID_DEVICEMANAGED ) ) { // Device Managed memory needs to be freed explicitly. USHORT cbReport; PUCHAR pReport; PIDREPORT BlockIndex = g_BlockIndex; USHORT LinkCollection; cbReport = this->cbReport[BlockIndex.HidP_Type]; pReport = this->pReport[BlockIndex.HidP_Type]; ZeroBuf(pReport, cbReport); BlockIndex.Collection = HID_USAGE_PID_BLOCK_FREE_REPORT; BlockIndex.HidP_Type = HidP_Output; PID_GetLinkCollectionIndex (ped, BlockIndex.UsagePage, BlockIndex.Collection, 0x0, &LinkCollection); hres = PID_PackValue ( ped, &BlockIndex, LinkCollection, &dwEffect, cbX(dwEffect), pReport, cbReport ); if(SUCCEEDED(hres) ) { hres = PID_SendReport(ped, pReport, cbReport, BlockIndex.HidP_Type, TRUE, 0, 1); } } if( SUCCEEDED(hres) ) { PEFFECTSTATE pEffectState = PeffectStateFromBlockIndex(this,dwEffect); UINT nAlloc, uParam; WaitForSingleObject(g_hmtxShared, INFINITE); pEffectState->lEfState = PID_EFFECT_RESET; for( uParam = 0x0; uParam < this->cMaxParameters; uParam++ ) { PPIDMEM pMem = &pEffectState->PidMem[uParam] ; if( PIDMEM_SIZE(pMem) ) { PPIDMEM pTmp; PUNITSTATE pUnitState = (PUNITSTATE)(g_pshmem + this->iUnitStateOffset); for(nAlloc = 0x0, pTmp = &(pUnitState->Guard[0]); nAlloc < pUnitState->nAlloc; nAlloc++, pTmp = (PPIDMEM)((PUCHAR)pUnitState + pTmp->iNext)) { if( (PPIDMEM)(pTmp->iNext) == (PPIDMEM)((PUCHAR)pMem - (PUCHAR)pUnitState )) { pTmp->iNext = pMem->iNext; pUnitState->nAlloc--; pUnitState->cbAlloc -= PIDMEM_SIZE(pMem); pMem->iNext = 0; pMem->uOfSz = 0x0; break; } } } } ReleaseMutex(g_hmtxShared); } if( SUCCEEDED(hres) ) { ((PUNITSTATE)(g_pshmem + this->iUnitStateOffset))->cEfDownloaded--; } DllLeaveCrit(); ExitOleProc(); return hres; } /***************************************************************************** * * PID_SanitizeEffect * * Sanitize the parameters in the DIEFFECT structure. * Clip values of magnitude, time, etc .. * Convert the axes array to usage, usage page from the DINPUT obj instances. * Convert and scale angles. * *****************************************************************************/ HRESULT PID_SanitizeEffect ( IDirectInputEffectDriver *ped, LPDIEFFECT lpeff, DWORD dwFlags ) { CPidDrv *this = (CPidDrv *)ped; HRESULT hres = S_OK; UINT nAxis; EnterProc( PID_SanitizeEffect, (_"xxx", ped, lpeff, dwFlags)); if( ( dwFlags & DIEP_TRIGGERBUTTON ) && lpeff->dwTriggerButton != -1 ) { DWORD dwUsage; hres = hresFinddwUsageFromdwFlags(ped, lpeff->dwTriggerButton, &dwUsage); if( SUCCEEDED(hres) ) { USAGE Usage = DIGETUSAGE(dwUsage); USAGE UsagePage = DIGETUSAGEPAGE(dwUsage); lpeff->dwTriggerButton = Usage; } else { lpeff->dwTriggerButton = 0x0; } } else { lpeff->dwTriggerButton = 0x0; } for(nAxis = 0x0; nAxis < lpeff->cAxes; nAxis++ ) { DWORD dwUsage; hres = hresFinddwUsageFromdwFlags(ped, lpeff->rgdwAxes[nAxis], &dwUsage); if(SUCCEEDED(hres) ) { lpeff->rgdwAxes[nAxis] = dwUsage; } //if we have only 1 axis and direction of 0 or 360, make sure the direction matches the axis! //if direction is not 0, we do not know what the app wants, so let it be. if ((lpeff->cAxes == 1) && (lpeff->rglDirection[nAxis] % 360*DI_DEGREES == 0)) { #ifndef HID_USAGE_SIMULATION_STEERING #define HID_USAGE_SIMULATION_STEERING ((USAGE) 0xC8) #endif #ifndef HID_USAGE_SIMULATION_ACCELERATOR #define HID_USAGE_SIMULATION_ACCELERATOR ((USAGE) 0xC4) #endif #ifndef HID_USAGE_SIMULATION_BRAKE #define HID_USAGE_SIMULATION_BRAKE ((USAGE) 0xC5) #endif //if it is X-axis or steering on the wheel, set direction to 90 degrees if ((DIGETUSAGE(lpeff->rgdwAxes[nAxis]) == HID_USAGE_GENERIC_X) || (DIGETUSAGE(lpeff->rgdwAxes[nAxis]) == HID_USAGE_SIMULATION_STEERING)) { lpeff->rglDirection[nAxis] = 90*DI_DEGREES; } //if it is Y-axis or accelerator or brake, set direction to 0 else if ((DIGETUSAGE(lpeff->rgdwAxes[nAxis]) == HID_USAGE_GENERIC_Y) || (DIGETUSAGE(lpeff->rgdwAxes[nAxis]) == HID_USAGE_SIMULATION_ACCELERATOR) || (DIGETUSAGE(lpeff->rgdwAxes[nAxis]) == HID_USAGE_SIMULATION_BRAKE)) { lpeff->rglDirection[nAxis] = 0x0; } } else //we have more than 1 axes or direction is non-0 for 1-axis effect; leave the direction along { lpeff->rglDirection[nAxis] %= 360*DI_DEGREES; if(lpeff->rglDirection[nAxis] < 0) { lpeff->rglDirection[nAxis] += 360*DI_DEGREES; } } } // Clip the values to min / max lpeff->dwGain = Clip(lpeff->dwGain, DI_FFNOMINALMAX); // Scale to units that device expects PID_ApplyScalingFactors(ped, &g_Effect, &this->DiSEffectScale, this->DiSEffectScale.dwSize, &this->DiSEffectOffset, this->DiSEffectOffset.dwSize, lpeff, lpeff->dwSize ); ExitOleProc(); return hres; } /***************************************************************************** * * CPidDrv_DownloadEffect * * Send an effect to the device. * * dwId * * The external joystick number being addressed. * * dwEffectId * * Internal identifier for the effect, taken from * the DIEFFECTATTRIBUTES structure for the effect * as stored in the registry. * * pdwEffect * * On entry, contains the handle of the effect being * downloaded. If the value is zero, then a new effect * is downloaded. If the value is nonzero, then an * existing effect is modified. * * On exit, contains the new effect handle. * * On failure, set to zero if the effect is lost, * or left alone if the effect is still valid with * its old parameters. * * Note that zero is never a valid effect handle. * * peff * * The new parameters for the effect. The axis and button * values have been converted to object identifiers * as follows: * * - One type specifier: * * DIDFT_RELAXIS, * DIDFT_ABSAXIS, * DIDFT_PSHBUTTON, * DIDFT_TGLBUTTON, * DIDFT_POV. * * - One instance specifier: * * DIDFT_MAKEINSTANCE(n). * * Other bits are reserved and should be ignored. * * For example, the value 0x0200104 corresponds to * the type specifier DIDFT_PSHBUTTON and * the instance specifier DIDFT_MAKEINSTANCE(1), * which together indicate that the effect should * be associated with button 1. Axes, buttons, and POVs * are each numbered starting from zero. * * dwFlags * * Zero or more DIEP_* flags specifying which * portions of the effect information has changed from * the effect already on the device. * * This information is passed to drivers to allow for * optimization of effect modification. If an effect * is being modified, a driver may be able to update * the effect in situ and transmit to the device * only the information that has changed. * * Drivers are not, however, required to implement this * optimization. All fields in the DIEFFECT structure * pointed to by the peff parameter are valid, and * a driver may choose simply to update all parameters of * the effect at each download. * * Returns: * * S_OK on success. * * DI_TRUNCATED if the parameters of the effect were * successfully downloaded, but some of them were * beyond the capabilities of the device and were truncated. * * DI_EFFECTRESTARTED if the parameters of the effect * were successfully downloaded, but in order to change * the parameters, the effect needed to be restarted. * * DI_TRUNCATEDANDRESTARTED if both DI_TRUNCATED and * DI_EFFECTRESTARTED apply. * * Any other DIERR_* error code may be returned. * * Private driver-specific error codes in the range * DIERR_DRIVERFIRST through DIERR_DRIVERLAST * may be returned. * *****************************************************************************/ STDMETHODIMP PID_DownloadEffect ( IDirectInputEffectDriver *ped, DWORD dwId, DWORD dwEffectId, LPDWORD pdwEffect, LPCDIEFFECT peff, DWORD dwFlags ) { CPidDrv *this = (CPidDrv *)ped; HRESULT hres = S_OK; DIEFFECT eff; DWORD rgdwAxes[MAX_AXES]; LONG rglDirection[MAX_AXES]; UINT uParameter = 0x0 ; UINT totalBlocks = 0x0; BOOL bBlocking = FALSE; EnterProcI( PID_DownloadEffectBlock, (_"xxxxxx", ped, dwId, dwEffectId, pdwEffect, peff, dwFlags)); AssertF(peff->cAxes <= MAX_AXES); DllEnterCrit(); // If new effect is being downloaded if( *pdwEffect == 0x0 ) { // Verify that dwEffectId is supported DWORD dwJunk; PIDSUPPORT pidSupport; pidSupport.dwPidUsage = dwEffectId; pidSupport.HidP_Type = HidP_Output; pidSupport.Type = HID_BUTTON; hres = PID_Support ( ped, 0x1, &pidSupport, &dwJunk ); if(FAILED(hres)) { SquirtSqflPtszV(sqfl | sqflError, TEXT("%s:FAIL dwEffectId(0x%x) not supported"), s_tszProc, dwEffectId ); } } if( SUCCEEDED(hres) ) { // Make a local copy of the effect structure // And sanitize the effect struct eff = *peff; memcpy(rgdwAxes, peff->rgdwAxes,eff.cAxes*cbX(*(eff.rgdwAxes))); memcpy(rglDirection, peff->rglDirection, eff.cAxes*cbX(*(eff.rglDirection))); eff.rgdwAxes = rgdwAxes; eff.rglDirection = rglDirection; hres = PID_SanitizeEffect(ped, &eff, dwFlags); } // Allocate new effect index or Validate Existing index if( SUCCEEDED(hres) ) { if( *pdwEffect != 0x0 ) { hres = PID_ValidateEffectIndex(ped, *pdwEffect); } else { if (! (dwFlags & DIEP_NODOWNLOAD)) { hres = PID_NewEffectIndex(ped, &eff, dwEffectId, pdwEffect); //block the first time around bBlocking = TRUE; } } } if (dwFlags & DIEP_NODOWNLOAD) { goto done; } //if the DIEP_NORESTART flag is passed, we have no block because this may fail //if the device can't update the parameters on the fly if (dwFlags & DIEP_NORESTART) { bBlocking = TRUE; } if( SUCCEEDED(hres) ) { //count up how many total blocks we will have in this download //check wether we're sending the effect block if (dwFlags & ( DIEP_DURATION | DIEP_SAMPLEPERIOD | DIEP_GAIN | DIEP_TRIGGERBUTTON | DIEP_TRIGGERREPEATINTERVAL | DIEP_AXES | DIEP_DIRECTION | DIEP_STARTDELAY ) ) { totalBlocks ++; } //check whether we're sending the type-specific params if (dwFlags & DIEP_TYPESPECIFICPARAMS) { //this is slightly different, in that conditions can have 1 type-specific block PER AXIS, //i.e. currently up to 2 //so if we have a DICONDITION, we check how many type-specific blocks we've got if ((dwEffectId == PIDMAKEUSAGEDWORD(ET_SPRING)) || (dwEffectId == PIDMAKEUSAGEDWORD(ET_DAMPER)) || (dwEffectId == PIDMAKEUSAGEDWORD(ET_INERTIA)) || (dwEffectId == PIDMAKEUSAGEDWORD(ET_FRICTION))) { totalBlocks +=(eff.cbTypeSpecificParams)/sizeof(DICONDITION); //DICONDITIONS also can't have envelopes dwFlags &= ~DIEP_ENVELOPE; } else { totalBlocks++; } } //check whether we're sending the envelope if ((dwFlags & DIEP_ENVELOPE) && (eff.lpEnvelope != NULL)) { totalBlocks++; } //check whether we need to send the start reprot if (dwFlags & DIEP_START) { totalBlocks++; } //make sure that we haven't got more than the maximum AssertF(totalBlocks <= MAX_BLOCKS); // Do the parameter block if( SUCCEEDED(hres) && ( dwFlags & ( DIEP_TYPESPECIFICPARAMS | DIEP_ENVELOPE) ) ) { hres = PID_DoParameterBlocks ( ped, dwId, dwEffectId, *pdwEffect, &eff, dwFlags, &uParameter, bBlocking, totalBlocks ); } // Now do the effect report if( SUCCEEDED(hres) && ( dwFlags & ( DIEP_DURATION | DIEP_SAMPLEPERIOD | DIEP_GAIN | DIEP_TRIGGERBUTTON | DIEP_TRIGGERREPEATINTERVAL | DIEP_AXES | DIEP_DIRECTION | DIEP_STARTDELAY ) ) ) { USHORT cbReport; PUCHAR pReport; AssertF(g_Effect.HidP_Type == HidP_Output); cbReport = this->cbReport[g_Effect.HidP_Type]; pReport = this->pReport[g_Effect.HidP_Type]; // Set the Effect Structure if( SUCCEEDED(hres) ) { USHORT LinkCollection; PID_GetLinkCollectionIndex(ped, g_Effect.UsagePage, g_Effect.Collection, 0x0, &LinkCollection ); ZeroBuf(pReport, cbReport); // Do the common elements of the effect structure hres = PID_PackValue ( ped, &g_Effect, LinkCollection, &eff, eff.dwSize, pReport, cbReport ); // Set the Effect Block Index if( SUCCEEDED(hres) ) { hres = PID_PackValue ( ped, &g_BlockIndex, LinkCollection, pdwEffect, cbX(*pdwEffect), pReport, cbReport ); } // Set Direction and axis attributes if( SUCCEEDED(hres) ) { USHORT DirectionCollection; PID_GetLinkCollectionIndex(ped, g_Direction.UsagePage, g_Direction.Collection, 0x0, &DirectionCollection ); PID_ApplyScalingFactors(ped, &g_Direction, &this->DiSEffectAngleScale, cbX(this->DiSEffectAngleScale), &this->DiSEffectAngleOffset, cbX(this->DiSEffectAngleOffset), eff.rglDirection, eff.cAxes*cbX(LONG) ); hres = PID_PackValue ( ped, &g_Direction, DirectionCollection, eff.rglDirection, eff.cAxes * cbX(LONG), pReport, cbReport ); if(SUCCEEDED(hres) && ! ( eff.dwFlags & DIEFF_CARTESIAN ) ) { // Direction Enable USHORT Usage; USHORT UsagePage; UINT nUsages = 0x1; NTSTATUS ntStat; // Direction Enable is in the set effect collection UsagePage = g_Effect.UsagePage; Usage = HID_USAGE_PID_DIRECTION_ENABLE; ntStat = HidP_SetUsages ( HidP_Output, UsagePage, LinkCollection, &Usage, &nUsages, this->ppd, pReport, cbReport); if( FAILED(ntStat) ) { SquirtSqflPtszV(sqfl | sqflError, TEXT("%s: FAIL HidP_SetUsages:0x%x for(%x,%x,%x:%s)"), s_tszProc, ntStat, LinkCollection, UsagePage, Usage, PIDUSAGETXT(UsagePage,Usage) ); } else { SquirtSqflPtszV(sqfl | sqflVerbose, TEXT("%s: HidP_SetUsages:0x%x for(%x,%x,%x:%s)"), s_tszProc, ntStat, LinkCollection,UsagePage, Usage, PIDUSAGETXT(UsagePage,Usage) ); } } else //if( dwFlags & DIEP_AXES ) { UINT nAxis; USHORT LinkCollection_AE=0x0; if(SUCCEEDED(PID_GetLinkCollectionIndex(ped, HID_USAGE_PAGE_PID, HID_USAGE_PID_AXES_ENABLE, 0x0, &LinkCollection_AE))) { // ISSUE-2001/03/29-timgill Need to support axes within pointer collections // PID spec indicates a pointer collection, // Do we want to support axes enables within a pointer // collection ? // See if there is a pointer collection } for(nAxis = 0x0; nAxis < eff.cAxes; nAxis++ ) { UINT nUsages = 0x1; USHORT Usage = DIGETUSAGE(eff.rgdwAxes[nAxis]); USHORT UsagePage = DIGETUSAGEPAGE(eff.rgdwAxes[nAxis]); NTSTATUS ntStat; //ISSUE-2001/03/29-timgill For now we assume any collection ntStat = HidP_SetUsages ( HidP_Output, UsagePage, 0x0, &Usage, &nUsages, this->ppd, pReport, cbReport); if( FAILED(ntStat) ) { SquirtSqflPtszV(sqfl | sqflError, TEXT("%s: FAIL HidP_SetUsages:0x%x for(%x,%x,%x:%s)"), s_tszProc, ntStat, 0x0, UsagePage, Usage, PIDUSAGETXT(UsagePage,Usage) ); hres = ntStat; break; } else { SquirtSqflPtszV(sqfl | sqflVerbose, TEXT("%s: HidP_SetUsages:0x%x for(%x,%x,%x:%s)"), s_tszProc, ntStat, 0x0, UsagePage, Usage, PIDUSAGETXT(UsagePage, Usage) ); } } } } if( SUCCEEDED(hres) && !( this->uDeviceManaged & PID_DEVICEMANAGED ) ) { // Need parameter block offsets UINT indx; USHORT LinkCollection; LONG rglValue[MAX_ORDINALS]; PID_GetLinkCollectionIndex(ped, g_Effect.UsagePage, g_TypeSpBlockOffset.Collection, 0x0, &LinkCollection ); for(indx = 0x0; indx < this->cMaxParameters; indx++ ) { hres = PID_GetParameterOffset(ped, *pdwEffect, indx, 0x0, &rglValue[indx]); if(FAILED(hres)) { break; } } if(SUCCEEDED(hres)) { hres = PID_PackValue ( ped, &g_TypeSpBlockOffset, LinkCollection, rglValue, this->cMaxParameters*cbX(LONG), pReport, cbReport ); } } // Set the Effect Type if( SUCCEEDED(hres) ) { USAGE UsagePage = DIGETUSAGEPAGE(dwEffectId); USAGE Usage = DIGETUSAGE(dwEffectId); UINT nUsages = 0x1; USHORT LinkCollection_ET; NTSTATUS ntStat; PID_GetLinkCollectionIndex(ped, g_Effect.UsagePage, HID_USAGE_PID_EFFECT_TYPE, 0x0, &LinkCollection_ET); ntStat = HidP_SetUsages ( HidP_Output, UsagePage, LinkCollection_ET, &Usage, &nUsages, this->ppd, pReport, cbReport); if( FAILED(ntStat) ) { SquirtSqflPtszV(sqfl | sqflError, TEXT("%s: FAIL HidP_SetUsages:0x%x for(%x,%x,%x:%s)"), s_tszProc, ntStat, LinkCollection_ET, UsagePage, Usage, PIDUSAGETXT(UsagePage,Usage) ); hres = ntStat; } else { SquirtSqflPtszV(sqfl | sqflVerbose, TEXT("%s: HidP_SetUsages:0x%x for(%x,%x,%x:%s)"), s_tszProc, ntStat, LinkCollection_ET, UsagePage, Usage, PIDUSAGETXT(UsagePage,Usage) ); } } if( SUCCEEDED(hres) ) { hres = PID_SendReport(ped, pReport, cbReport, g_Effect.HidP_Type, bBlocking, uParameter, totalBlocks); uParameter ++; } } } } if( FAILED(hres) ) { PID_DestroyEffect(ped, dwId, *pdwEffect); } if( SUCCEEDED(hres) && (dwFlags & DIEP_START) ) { hres = PID_EffectOperation ( ped, dwId, *pdwEffect, PID_DIES_START, 0x1, bBlocking, uParameter, totalBlocks ); if (SUCCEEDED(hres)) { //set the status to DIEGES_PLAYING. //we do this because of the following: if an app calls Start(), and then immediately //calls GetEffectStatus(), it might happen that our second thread (pidrd.c) //would not have time to update the status of the effect to DIEGES_PLAYING //(see Whistler bug 287035). //GetEffectStatus() returns (pEffectState->lEfState & DIEGES_PLAYING). //in the blocking case, we know that the call to WriteFile() has succeeded, and that //all the data has been written (see PID_SendReportBl() in pidhid.c) -- //so we might as well set the status. //in the non-blocking case, the data can be buffered anyway -- so we might as well set the status. PEFFECTSTATE pEffectState = PeffectStateFromBlockIndex(this, *pdwEffect); pEffectState->lEfState |= DIEGES_PLAYING; } } done:; DllLeaveCrit(); ExitOleProc(); return hres; }