6361 lines
207 KiB
C
6361 lines
207 KiB
C
/*++
|
|
|
|
Copyright (c) 1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
hidphone.c
|
|
|
|
Abstract:
|
|
|
|
This module contains implements the phone tsp functions is called by
|
|
tapi in order to access the HID compliant USB phone device.
|
|
This module communicates with the phone device using the HID interface.
|
|
|
|
Author: Shivani Aggarwal
|
|
|
|
Comments:
|
|
Locking Mechanism:
|
|
There are two critical section objects being used inorder to protect
|
|
the phone structure from simultaneous access - csAllPhones and
|
|
csThisPhone. Every phone has one csThisPhone, the critical section
|
|
object, associated with it. csThisPhone ensures that the Phone Info
|
|
is accessed in a thread-safe manner. csAllPhones is a global Critical
|
|
Section Object that ensures that a thread acquires the csThisPhone
|
|
Critical Section Object in a thread safe manner. In other words, it
|
|
ensures that, the thread waits on csThisPhone while the Critical
|
|
Section Object is still valid.
|
|
The csAllPhones should always be acquired before csThisPhone.
|
|
A phone can be closed only after the thread has acquired both
|
|
csAllPhones and csThisPhone for the specific phone to be closed. Both
|
|
these objects are released only after the function is completed. For
|
|
all other functions, the csAllPhones critical section is released as
|
|
soon as the thread acquires csThisPhone object.
|
|
|
|
------------------------------------------------------------------------------*/
|
|
|
|
|
|
#include "hidphone.h" //** NOTE - hidphone.h must be defined before mylog.h
|
|
#include "mylog.h"
|
|
|
|
BOOL
|
|
WINAPI
|
|
DllMain(
|
|
HANDLE hDLL,
|
|
DWORD dwReason,
|
|
LPVOID lpReserved
|
|
)
|
|
{
|
|
switch (dwReason)
|
|
{
|
|
case DLL_PROCESS_ATTACH:
|
|
{
|
|
// inorder to enable logging for this tsp
|
|
LOGREGISTERDEBUGGER(_T("hidphone"));
|
|
|
|
LOG((PHONESP_TRACE, "DllMain - DLL_PROCESS_ATTACH"));
|
|
|
|
ghInst = hDLL;
|
|
|
|
// if the heap cannot be created, use the heap from the process
|
|
|
|
if (!(ghHeap = HeapCreate(
|
|
0, // NULL on failure,serialize access
|
|
0x1000, // initial heap size
|
|
0 // max heap size (0 == growable)
|
|
)))
|
|
{
|
|
LOG((PHONESP_ERROR, "DllMain - HeapCreate failed %d", GetLastError()));
|
|
|
|
ghHeap = GetProcessHeap();
|
|
|
|
if (ghHeap == NULL)
|
|
{
|
|
LOG((PHONESP_ERROR, "DllMain - GetProcessHeap failed %d", GetLastError()));
|
|
|
|
return GetLastError();
|
|
}
|
|
}
|
|
|
|
|
|
// Inorder to diasble notifications of thread attach and detach in
|
|
// multi-threaded apps it can be a very useful optimization
|
|
|
|
DisableThreadLibraryCalls( hDLL );
|
|
|
|
break;
|
|
}
|
|
|
|
case DLL_PROCESS_DETACH:
|
|
{
|
|
LOG((PHONESP_TRACE, "DllMain - DLL_PROCESS_DETACH"));
|
|
|
|
LOGDEREGISTERDEBUGGER();
|
|
|
|
// if ghHeap is NULL, then there is no heap to destroy
|
|
if ( ( ghHeap != NULL) && ( ghHeap != GetProcessHeap() ) )
|
|
{
|
|
HeapDestroy (ghHeap);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case DLL_THREAD_ATTACH:
|
|
break;
|
|
|
|
case DLL_THREAD_DETACH:
|
|
break;
|
|
|
|
} // switch
|
|
return TRUE;
|
|
}
|
|
/*************************DLLMAIN - END***************************************/
|
|
|
|
|
|
/******************************************************************************
|
|
IsTSPAlreadyInstalled:
|
|
|
|
Searchs registry for previous instance of HidPhone TSP.
|
|
|
|
Arguments:
|
|
none
|
|
|
|
Returns BOOL:
|
|
Returns true if TSP already installed
|
|
|
|
Comments:
|
|
|
|
******************************************************************************/
|
|
BOOL
|
|
IsTSPAlreadyInstalled()
|
|
{
|
|
DWORD i;
|
|
HKEY hKey;
|
|
LONG lStatus;
|
|
DWORD dwNumProviders = 0;
|
|
DWORD dwDataSize = sizeof(DWORD);
|
|
DWORD dwDataType = REG_DWORD;
|
|
LPTSTR pszProvidersKey = TAPI_REGKEY_PROVIDERS;
|
|
LPTSTR pszNumProvidersValue = TAPI_REGVAL_NUMPROVIDERS;
|
|
TCHAR szName[MAX_PATH];
|
|
TCHAR szPath[MAX_PATH];
|
|
|
|
// attempt to open key
|
|
lStatus = RegOpenKeyEx(
|
|
HKEY_LOCAL_MACHINE,
|
|
pszProvidersKey,
|
|
0,
|
|
KEY_READ,
|
|
&hKey
|
|
);
|
|
|
|
// validate status
|
|
if (lStatus != ERROR_SUCCESS)
|
|
{
|
|
LOG((PHONESP_ERROR, "IsTSPAlreadyInstalled - "
|
|
"error opening tapi providers key - %lx", lStatus));
|
|
|
|
// done
|
|
return FALSE;
|
|
}
|
|
|
|
// see if installed bit set
|
|
lStatus = RegQueryValueEx(
|
|
hKey,
|
|
pszNumProvidersValue,
|
|
0,
|
|
&dwDataType,
|
|
(LPBYTE) &dwNumProviders,
|
|
&dwDataSize
|
|
);
|
|
|
|
// validate status
|
|
if( lStatus != ERROR_SUCCESS )
|
|
{
|
|
LOG((PHONESP_ERROR, "IsTSPAlreadyInstalled - "
|
|
"error determining number of providers - %lx", lStatus));
|
|
|
|
// release handle
|
|
RegCloseKey(hKey);
|
|
|
|
// done
|
|
return FALSE;
|
|
}
|
|
|
|
// loop through each provider
|
|
for (i = 0; i < dwNumProviders; i++)
|
|
{
|
|
// construct path to provider name
|
|
wsprintf(szName, _T("ProviderFileName%d"), i);
|
|
|
|
// reinitialize size
|
|
dwDataSize = sizeof(szPath);
|
|
|
|
// query the next name
|
|
lStatus = RegQueryValueEx(
|
|
hKey,
|
|
szName,
|
|
0,
|
|
&dwDataType,
|
|
(unsigned char*)szPath,
|
|
&dwDataSize
|
|
);
|
|
|
|
// validate status
|
|
if (lStatus == ERROR_SUCCESS)
|
|
{
|
|
// upper case
|
|
_tcsupr(szPath);
|
|
|
|
// compare path string to hidphone provider
|
|
if (_tcsstr(szPath, HIDPHONE_TSPDLL) != NULL)
|
|
{
|
|
// release handle
|
|
RegCloseKey(hKey);
|
|
|
|
// done
|
|
return TRUE;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
LOG((PHONESP_ERROR, "IsTSPAlreadyInstalled - "
|
|
"error querying %s - %lx", szName, lStatus));
|
|
}
|
|
}
|
|
|
|
// release handle
|
|
RegCloseKey(hKey);
|
|
|
|
// done
|
|
return FALSE;
|
|
}
|
|
|
|
/******************************************************************************
|
|
ReenumDevices:
|
|
|
|
This function reenumerated hid devices after a pnp event. It will
|
|
create phone devices for new hid arrivals and remove phone devices
|
|
(provided they are closed) for hid removals. It will also notify
|
|
TAPI of these events.
|
|
|
|
Arguments:
|
|
none
|
|
|
|
Returns VOID:
|
|
|
|
Comments:
|
|
|
|
******************************************************************************/
|
|
|
|
VOID
|
|
ReenumDevices ()
|
|
{
|
|
PHID_DEVICE pHidDevices;
|
|
PHID_DEVICE pHidDevice;
|
|
PHID_DEVICE pNextHidDevice;
|
|
ULONG NumHidDevices;
|
|
DWORD dwNewCount;
|
|
DWORD dwRemovedCount;
|
|
DWORD dwPhone;
|
|
LONG lResult;
|
|
PPHONESP_PHONE_INFO pPhone;
|
|
|
|
LOG((PHONESP_TRACE, "ReenumDevices - enter"));
|
|
|
|
EnterCriticalSection(&csHidList);
|
|
|
|
// Find Telephony hid Devices
|
|
lResult = FindKnownHidDevices (&pHidDevices,
|
|
&NumHidDevices);
|
|
|
|
LOG((PHONESP_TRACE, "ReenumDevices - number of Hid Devices : %d ", NumHidDevices));
|
|
|
|
dwNewCount = 0;
|
|
dwRemovedCount = 0;
|
|
|
|
EnterCriticalSection(&csAllPhones);
|
|
|
|
for (pHidDevice = pHidDevices; pHidDevice != NULL; pHidDevice = pNextHidDevice)
|
|
{
|
|
//
|
|
// Get pointer to the next Hid device now, so we can remove the current
|
|
// device if necessary without messing up our search
|
|
//
|
|
pNextHidDevice = pHidDevice->Next;
|
|
|
|
if (pHidDevice->bRemoved)
|
|
{
|
|
//
|
|
// This device has been removed
|
|
//
|
|
|
|
dwRemovedCount++;
|
|
|
|
pPhone = GetPhoneFromHid(pHidDevice);
|
|
|
|
// Check whether the phone handle is still valid
|
|
if ( !IsBadReadPtr(pPhone,sizeof(PHONESP_PHONE_INFO) ))
|
|
{
|
|
|
|
EnterCriticalSection(&pPhone->csThisPhone);
|
|
|
|
//
|
|
// First lets get rid of the Hid device since it has already
|
|
// physically left the system
|
|
//
|
|
|
|
pPhone->pHidDevice = NULL;
|
|
CloseHidDevice(pHidDevice);
|
|
|
|
//
|
|
// Send a phone remove to TAPI
|
|
//
|
|
SendPhoneEvent(
|
|
pPhone,
|
|
PHONE_REMOVE,
|
|
pPhone->dwDeviceID,
|
|
0,
|
|
0
|
|
);
|
|
|
|
if (pPhone->bPhoneOpen)
|
|
{
|
|
//
|
|
// The phone is open, we can't remove it right away so
|
|
// mark it remove pending
|
|
//
|
|
|
|
pPhone->bRemovePending = TRUE;
|
|
|
|
LOG((PHONESP_TRACE, "ReenumDevices - phone remove pending [dwDeviceID %d] ", pPhone->dwDeviceID));
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// The phone is closed, we can remove it now
|
|
//
|
|
|
|
FreePhone(pPhone);
|
|
|
|
LOG((PHONESP_TRACE, "ReenumDevices - phone remove complete [dwDeviceID %d] ", pPhone->dwDeviceID));
|
|
}
|
|
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
}
|
|
else
|
|
{
|
|
LOG((PHONESP_ERROR, "ReenumDevices - GetPhoneFromHid returned an invalid phone pointer"));
|
|
}
|
|
}
|
|
else if (pHidDevice->bNew)
|
|
{
|
|
BOOL bFound = FALSE;
|
|
|
|
//
|
|
// This device is new
|
|
//
|
|
|
|
dwNewCount++;
|
|
|
|
pHidDevice->bNew = FALSE;
|
|
|
|
//
|
|
// We need to create a new phone device, find a spot
|
|
//
|
|
|
|
for (dwPhone = 0; dwPhone < gdwNumPhones; dwPhone++)
|
|
{
|
|
pPhone = (PPHONESP_PHONE_INFO) gpPhone[ dwPhone ];
|
|
|
|
EnterCriticalSection(&pPhone->csThisPhone);
|
|
|
|
if ( !pPhone->bAllocated && !pPhone->bCreatePending )
|
|
{
|
|
//
|
|
// We have an open slot for this phone
|
|
//
|
|
LOG((PHONESP_TRACE, "ReenumDevices - slot %d open", dwPhone));
|
|
|
|
bFound = TRUE;
|
|
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
break;
|
|
}
|
|
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
}
|
|
|
|
|
|
if (!bFound)
|
|
{
|
|
//
|
|
// We don't have a slot open, so we will have to realloc the
|
|
// array to create a new one
|
|
//
|
|
|
|
PPHONESP_PHONE_INFO *pNewPhones;
|
|
|
|
LOG((PHONESP_TRACE, "ReenumDevices - creating a new slot"));
|
|
|
|
if ( ! ( pNewPhones = MemAlloc((gdwNumPhones + 1) * sizeof(PPHONESP_PHONE_INFO)) ) )
|
|
{
|
|
LOG((PHONESP_ERROR,"ReenumDevices - out of memory "));
|
|
}
|
|
else
|
|
{
|
|
CopyMemory(
|
|
pNewPhones,
|
|
gpPhone,
|
|
sizeof(PPHONESP_PHONE_INFO) * gdwNumPhones
|
|
);
|
|
|
|
// Allocate memory for this phone
|
|
if ( pNewPhones[gdwNumPhones] = (PPHONESP_PHONE_INFO)MemAlloc(sizeof(PHONESP_PHONE_INFO)) )
|
|
{
|
|
LOG((PHONESP_TRACE, "ReenumDevices - initializing device: %d",gdwNumPhones+1));
|
|
|
|
ZeroMemory( pNewPhones[gdwNumPhones], sizeof(PHONESP_PHONE_INFO));
|
|
|
|
//
|
|
// Initialize the critical section object for this phone. only the
|
|
// thread that owns this object can access the structure for this phone
|
|
//
|
|
__try
|
|
{
|
|
InitializeCriticalSection( &pNewPhones[gdwNumPhones]->csThisPhone );
|
|
}
|
|
__except(1)
|
|
{
|
|
MemFree(pNewPhones[gdwNumPhones]);
|
|
MemFree(pNewPhones);
|
|
pNewPhones = NULL;
|
|
|
|
LOG((PHONESP_ERROR,"ReenumDevices - Initialize Critical Section"
|
|
" Failed for Phone %d", gdwNumPhones+1));
|
|
}
|
|
|
|
if ( pNewPhones != NULL )
|
|
{
|
|
//
|
|
// Success
|
|
//
|
|
|
|
LOG((PHONESP_TRACE, "ReenumDevices - slot %d created", gdwNumPhones));
|
|
|
|
dwPhone = gdwNumPhones;
|
|
pPhone = pNewPhones[dwPhone];
|
|
bFound = TRUE;
|
|
|
|
MemFree(gpPhone);
|
|
gpPhone = pNewPhones;
|
|
gdwNumPhones++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MemFree(pNewPhones);
|
|
|
|
LOG((PHONESP_ERROR,"ReenumDevices - out of memory "));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bFound)
|
|
{
|
|
//
|
|
// Now actually create the phone
|
|
//
|
|
|
|
EnterCriticalSection(&pPhone->csThisPhone);
|
|
|
|
lResult = CreatePhone( pPhone, pHidDevice, dwPhone );
|
|
|
|
if ( lResult != ERROR_SUCCESS )
|
|
{
|
|
LOG((PHONESP_ERROR,"ReenumDevices - CreatePhone"
|
|
" Failed for Phone %d: error: %d", dwPhone, lResult));
|
|
}
|
|
else
|
|
{
|
|
// Phone created successfully, send a PHONE_CREATE message
|
|
|
|
pPhone->bCreatePending = TRUE;
|
|
|
|
SendPhoneEvent(
|
|
pPhone,
|
|
PHONE_CREATE,
|
|
(DWORD_PTR)ghProvider,
|
|
dwPhone,
|
|
0
|
|
);
|
|
|
|
LOG((PHONESP_TRACE, "ReenumDevices - phone create pending [dwTempID %d] ", dwPhone));
|
|
}
|
|
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
}
|
|
else
|
|
{
|
|
LOG((PHONESP_ERROR, "ReenunDevices - unable to create new phone"));
|
|
}
|
|
}
|
|
}
|
|
|
|
LeaveCriticalSection(&csAllPhones);
|
|
|
|
LeaveCriticalSection(&csHidList);
|
|
|
|
LOG((PHONESP_TRACE, "ReenumDevices - new : %d ", dwNewCount));
|
|
LOG((PHONESP_TRACE, "ReenumDevices - removed : %d ", dwRemovedCount));
|
|
|
|
LOG((PHONESP_TRACE, "ReenumDevices - exit"));
|
|
}
|
|
|
|
/******************************************************************************
|
|
FreePhone:
|
|
|
|
This function frees all of a phones data structures
|
|
|
|
Arguments:
|
|
PPHONESP_PHONE_INFO pPhone
|
|
|
|
Returns VOID:
|
|
|
|
Comments:
|
|
|
|
******************************************************************************/
|
|
VOID
|
|
FreePhone (
|
|
PPHONESP_PHONE_INFO pPhone
|
|
)
|
|
{
|
|
DWORD dwButtonCnt;
|
|
|
|
LOG((PHONESP_TRACE, "FreePhone - enter"));
|
|
|
|
// Check whether the phone handle is still valid
|
|
if ( IsBadReadPtr(pPhone,sizeof(PHONESP_PHONE_INFO) ))
|
|
{
|
|
LOG((PHONESP_ERROR, "FreePhone - phone handle invalid"));
|
|
return;
|
|
}
|
|
|
|
if ( !pPhone->bAllocated )
|
|
{
|
|
LOG((PHONESP_ERROR, "FreePhone - phone not allocated"));
|
|
return;
|
|
}
|
|
|
|
for (dwButtonCnt = 0;
|
|
dwButtonCnt < pPhone->dwNumButtons; dwButtonCnt++)
|
|
{
|
|
if (pPhone->pButtonInfo[dwButtonCnt].szButtonText != NULL)
|
|
{
|
|
MemFree(pPhone->pButtonInfo[dwButtonCnt].szButtonText);
|
|
pPhone->pButtonInfo[dwButtonCnt].szButtonText = NULL;
|
|
}
|
|
}
|
|
|
|
if (pPhone->pButtonInfo != NULL)
|
|
{
|
|
MemFree(pPhone->pButtonInfo);
|
|
pPhone->pButtonInfo = NULL;
|
|
}
|
|
|
|
if (pPhone->wszPhoneInfo != NULL)
|
|
{
|
|
MemFree((LPVOID) pPhone->wszPhoneInfo);
|
|
pPhone->wszPhoneInfo = NULL;
|
|
}
|
|
|
|
if (pPhone->wszPhoneName != NULL)
|
|
{
|
|
MemFree((LPVOID) pPhone->wszPhoneName);
|
|
pPhone->wszPhoneName = NULL;
|
|
}
|
|
|
|
pPhone->bAllocated = FALSE;
|
|
|
|
LOG((PHONESP_TRACE, "FreePhone - exit"));
|
|
}
|
|
|
|
/******************************************************************************
|
|
UpdatePhoneFeatures:
|
|
|
|
This function reads feature values from the phone.
|
|
|
|
Arguments:
|
|
PPHONESP_PHONE_INFO pPhone
|
|
|
|
Returns VOID:
|
|
|
|
Comments:
|
|
|
|
******************************************************************************/
|
|
VOID UpdatePhoneFeatures(
|
|
PPHONESP_PHONE_INFO pPhone
|
|
)
|
|
{
|
|
LOG((PHONESP_TRACE, "UpdatePhoneFeatures - enter"));
|
|
|
|
if( pPhone->pHidDevice->Caps.NumberFeatureValueCaps ||
|
|
pPhone->pHidDevice->Caps.NumberFeatureButtonCaps )
|
|
{
|
|
USAGE UsagePage;
|
|
USAGE Usage;
|
|
|
|
if (GetFeature(pPhone->pHidDevice))
|
|
{
|
|
DWORD dwDataCnt;
|
|
PHID_DATA pHidData;
|
|
|
|
pHidData = pPhone->pHidDevice->FeatureData;
|
|
for ( dwDataCnt = 0, pHidData = pPhone->pHidDevice->FeatureData;
|
|
dwDataCnt < pPhone->pHidDevice->FeatureDataLength;
|
|
dwDataCnt++, pHidData++ )
|
|
{
|
|
UsagePage = pHidData->UsagePage;
|
|
|
|
if (UsagePage == HID_USAGE_PAGE_TELEPHONY)
|
|
{
|
|
if(pHidData->IsButtonData)
|
|
{
|
|
for ( Usage = (USAGE)pHidData->ButtonData.UsageMin;
|
|
Usage <= (USAGE)pHidData->ButtonData.UsageMax;
|
|
Usage++ )
|
|
{
|
|
DWORD i;
|
|
|
|
for (i = 0;
|
|
i < pHidData->ButtonData.MaxUsageLength;
|
|
i++)
|
|
{
|
|
if(Usage == pHidData->ButtonData.Usages[i])
|
|
{
|
|
LOG((PHONESP_TRACE,"Button for Usage "
|
|
"0x%04x ON",Usage));
|
|
|
|
InitUsage(pPhone, Usage, TRUE);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( i == pHidData->ButtonData.MaxUsageLength)
|
|
{
|
|
InitUsage(pPhone, Usage, FALSE);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
InitUsage(pPhone, pHidData->ValueData.Usage,
|
|
pHidData->ValueData.Value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOG((PHONESP_ERROR, "UpdatePhoneFeatures - GetFeature failed"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOG((PHONESP_TRACE, "UpdatePhoneFeatures - NO FEATURE"));
|
|
}
|
|
|
|
LOG((PHONESP_TRACE, "UpdatePhoneFeatures - exit"));
|
|
}
|
|
|
|
/******************************************************************************
|
|
CreatePhone:
|
|
|
|
This function creates all of a phones data structures
|
|
|
|
Arguments:
|
|
PPHONESP_PHONE_INFO pPhone
|
|
PHID_DEVICE pHidDevice
|
|
|
|
Returns LONG:
|
|
|
|
Comments:
|
|
|
|
******************************************************************************/
|
|
LONG
|
|
CreatePhone (
|
|
PPHONESP_PHONE_INFO pPhone,
|
|
PHID_DEVICE pHidDevice,
|
|
DWORD dwPhoneCnt
|
|
)
|
|
{
|
|
LONG lResult;
|
|
LPWSTR wszPhoneName, wszPhoneInfo;
|
|
WCHAR wszPhoneID[MAX_CHARS];
|
|
PHIDP_BUTTON_CAPS pButtonCaps;
|
|
PHIDP_VALUE_CAPS pValueCaps;
|
|
HRESULT hr;
|
|
|
|
LOG((PHONESP_TRACE, "CreatePhone - enter"));
|
|
|
|
// Check whether the phone handle is still valid
|
|
if ( IsBadReadPtr(pPhone,sizeof(PHONESP_PHONE_INFO) ))
|
|
{
|
|
LOG((PHONESP_ERROR, "CreatePhone - phone handle invalid"));
|
|
return PHONEERR_INVALPHONEHANDLE;
|
|
}
|
|
|
|
if ( IsBadReadPtr(pHidDevice,sizeof(PHID_DEVICE) ))
|
|
{
|
|
LOG((PHONESP_ERROR, "CreatePhone - hid device pointer invalid"));
|
|
return PHONEERR_OPERATIONFAILED;
|
|
}
|
|
|
|
if ( pPhone->bAllocated )
|
|
{
|
|
LOG((PHONESP_ERROR, "CreatePhone - phone already allocated"));
|
|
return PHONEERR_OPERATIONFAILED;
|
|
}
|
|
|
|
// Load Phone Info From String Table
|
|
wszPhoneInfo = PHONESP_LoadString(
|
|
IDS_PHONE_INFO,
|
|
&lResult
|
|
);
|
|
|
|
if ( lResult != ERROR_SUCCESS )
|
|
{
|
|
if ( lResult == ERROR_OUTOFMEMORY )
|
|
{
|
|
LOG((PHONESP_ERROR, "CreatePhone - "
|
|
"PHONESP_LoadString out of memory"));
|
|
|
|
return PHONEERR_NOMEM;
|
|
}
|
|
else
|
|
{
|
|
LOG((PHONESP_ERROR, "CreatePhone - "
|
|
"PHONESP_LoadString failed %d", lResult));
|
|
|
|
return lResult;
|
|
}
|
|
}
|
|
|
|
// Load Phone Name From String Table
|
|
wszPhoneName = PHONESP_LoadString(
|
|
IDS_PHONE_NAME,
|
|
&lResult
|
|
);
|
|
|
|
if ( lResult != ERROR_SUCCESS )
|
|
{
|
|
MemFree((LPVOID)wszPhoneInfo);
|
|
|
|
if ( lResult == ERROR_OUTOFMEMORY )
|
|
{
|
|
LOG((PHONESP_ERROR, "CreatePhone - "
|
|
"PHONESP_LoadString out of memory"));
|
|
|
|
return PHONEERR_NOMEM;
|
|
}
|
|
else
|
|
{
|
|
LOG((PHONESP_ERROR, "CreatePhone - "
|
|
"PHONESP_LoadString failed %d", lResult));
|
|
|
|
return lResult;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Associate phone with the hid and wave devices
|
|
//
|
|
|
|
pPhone->bAllocated = TRUE;
|
|
pPhone->pHidDevice = pHidDevice;
|
|
|
|
// Discover Render Wave ID
|
|
|
|
hr = DiscoverAssociatedWaveId(pHidDevice->dwDevInst,
|
|
TRUE,
|
|
&pPhone->dwRenderWaveId);
|
|
|
|
|
|
if (hr != S_OK)
|
|
{
|
|
pPhone->bRender = FALSE;
|
|
LOG((PHONESP_ERROR, "CreatePhone - DiscoverAssociatedWaveID:"
|
|
" Render Failed for Phone %d: %0x", dwPhoneCnt, hr));
|
|
}
|
|
else
|
|
{
|
|
pPhone->bRender = TRUE;
|
|
LOG((PHONESP_TRACE,"CreatePhone - DiscoverAssociatedWaveId for Render: %d",
|
|
pPhone->dwRenderWaveId));
|
|
}
|
|
|
|
// Discover Capture Wave ID
|
|
hr = DiscoverAssociatedWaveId(pHidDevice->dwDevInst,
|
|
FALSE,
|
|
&pPhone->dwCaptureWaveId);
|
|
|
|
if (hr != S_OK)
|
|
{
|
|
pPhone->bCapture = FALSE;
|
|
LOG((PHONESP_ERROR, "CreatePhone - DiscoverAssociatedWaveID:"
|
|
" Capture Failed for Phone %d: %0x", dwPhoneCnt, hr));
|
|
}
|
|
else
|
|
{
|
|
pPhone->bCapture = TRUE;
|
|
LOG((PHONESP_TRACE,"CreatePhone - DiscoverAssociatedWaveId for Capture: %d",
|
|
pPhone->dwCaptureWaveId));
|
|
}
|
|
|
|
pPhone->dwButtonModesMsgs = PHONESP_ALLBUTTONMODES;
|
|
pPhone->dwButtonStateMsgs = PHONESP_ALLBUTTONSTATES;
|
|
|
|
//
|
|
// Extract Usages and Initialize the phone structure
|
|
//
|
|
|
|
// Get the usages from the HID structure
|
|
|
|
// Parse input button caps structure
|
|
LOG((PHONESP_TRACE, "CreatePhone - INPUT BUTTON CAPS"));
|
|
pButtonCaps = pHidDevice->InputButtonCaps;
|
|
|
|
|
|
GetButtonUsages(
|
|
pPhone,
|
|
pButtonCaps,
|
|
pHidDevice->Caps.NumberInputButtonCaps,
|
|
INPUT_REPORT
|
|
);
|
|
|
|
|
|
// Parse output button caps structure
|
|
LOG((PHONESP_TRACE, "CreatePhone - OUTPUT BUTTON CAPS" ));
|
|
pButtonCaps = pHidDevice->OutputButtonCaps;
|
|
GetButtonUsages(
|
|
pPhone,
|
|
pButtonCaps,
|
|
pHidDevice->Caps.NumberOutputButtonCaps,
|
|
OUTPUT_REPORT
|
|
);
|
|
|
|
|
|
// Parse feature button caps structure
|
|
LOG((PHONESP_TRACE, "CreatePhone - FEATURE BUTTON CAPS" ));
|
|
pButtonCaps = pHidDevice->FeatureButtonCaps;
|
|
GetButtonUsages(
|
|
pPhone,
|
|
pButtonCaps,
|
|
pHidDevice->Caps.NumberFeatureButtonCaps,
|
|
FEATURE_REPORT
|
|
);
|
|
|
|
|
|
|
|
// Parse input value caps structure
|
|
LOG((PHONESP_TRACE, "CreatePhone - INPUT VALUE CAPS"));
|
|
pValueCaps = pHidDevice->InputValueCaps;
|
|
GetValueUsages(
|
|
pPhone,
|
|
pValueCaps,
|
|
pHidDevice->Caps.NumberInputValueCaps,
|
|
INPUT_REPORT
|
|
);
|
|
|
|
// Parse output value caps structure
|
|
LOG((PHONESP_TRACE, "CreatePhone - OUTPUT VALUE CAPS" ));
|
|
pValueCaps = pHidDevice->OutputValueCaps;
|
|
|
|
GetValueUsages(
|
|
pPhone,
|
|
pValueCaps,
|
|
pHidDevice->Caps.NumberOutputValueCaps,
|
|
OUTPUT_REPORT
|
|
);
|
|
|
|
// Parse feature value caps structure
|
|
LOG((PHONESP_TRACE, "CreatePhone - FEATURE VALUE CAPS" ));
|
|
|
|
pValueCaps = pHidDevice->FeatureValueCaps;
|
|
GetValueUsages(
|
|
pPhone,
|
|
pValueCaps,
|
|
pHidDevice->Caps.NumberFeatureValueCaps,
|
|
FEATURE_REPORT
|
|
);
|
|
|
|
//
|
|
// The Phone should have a handset with input and feature
|
|
// reports supported. If it does not the phone will not be supported
|
|
// by this TSP. If this part of the code is uncommented, then the nokia
|
|
// box will be the unsupported phone device since it does not contain
|
|
// a feature report for the handset
|
|
//
|
|
if ( !( pPhone->dwHandset & INPUT_REPORT ) )
|
|
|
|
{
|
|
LOG((PHONESP_ERROR,"CreatePhone - This Phone not Supported"));
|
|
|
|
MemFree((LPVOID) wszPhoneInfo);
|
|
MemFree((LPVOID) wszPhoneName);
|
|
|
|
FreePhone(pPhone);
|
|
|
|
return PHONEERR_OPERATIONFAILED;
|
|
}
|
|
|
|
//
|
|
// Store the Phone ID as a string Value
|
|
//
|
|
|
|
wsprintf(wszPhoneID, TEXT(": %d"), dwPhoneCnt);
|
|
|
|
//
|
|
// Allocate space for storing the Phone Info
|
|
//
|
|
pPhone->wszPhoneInfo = (LPWSTR) MemAlloc ( (lstrlen(wszPhoneInfo) +
|
|
lstrlen(wszPhoneID) + 1 ) *
|
|
sizeof(WCHAR) );
|
|
|
|
if( NULL == pPhone->wszPhoneInfo)
|
|
{
|
|
LOG((PHONESP_ERROR,"CreatePhone - unable to allocate wszPhoneInfo"));
|
|
|
|
MemFree((LPVOID) wszPhoneInfo);
|
|
MemFree((LPVOID) wszPhoneName);
|
|
|
|
FreePhone(pPhone);
|
|
|
|
return PHONEERR_NOMEM;
|
|
}
|
|
|
|
//
|
|
// Copy the Phone Info in the phone structure and append it with
|
|
// the Phone ID
|
|
//
|
|
lstrcpy(pPhone->wszPhoneInfo,wszPhoneInfo);
|
|
lstrcat(pPhone->wszPhoneInfo,wszPhoneID);
|
|
|
|
|
|
pPhone->wszPhoneName = (LPWSTR)MemAlloc ( ( lstrlen(wszPhoneName) +
|
|
lstrlen(wszPhoneID) +
|
|
1 ) * sizeof(WCHAR) );
|
|
|
|
if( NULL == pPhone->wszPhoneName)
|
|
{
|
|
LOG((PHONESP_ERROR,"CreatePhone - unable to allocate wszPhoneName"));
|
|
MemFree((LPVOID) wszPhoneInfo);
|
|
MemFree((LPVOID) wszPhoneName);
|
|
|
|
FreePhone(pPhone);
|
|
|
|
return PHONEERR_NOMEM;
|
|
}
|
|
|
|
//
|
|
// Copy the Phone Name in the phone structure and append it with
|
|
// the Phone ID
|
|
//
|
|
lstrcpy(pPhone->wszPhoneName,wszPhoneName);
|
|
lstrcat(pPhone->wszPhoneName,wszPhoneID);
|
|
|
|
//
|
|
// Create Buttons for the ones discovered by tracking the usages
|
|
//
|
|
if ( CreateButtonsAndAssignID(pPhone) != ERROR_SUCCESS)
|
|
{
|
|
LOG((PHONESP_ERROR,"CreatePhone - CreateButtonsAndAssignID failed"));
|
|
MemFree((LPVOID) wszPhoneInfo);
|
|
MemFree((LPVOID) wszPhoneName);
|
|
|
|
FreePhone(pPhone);
|
|
|
|
return PHONEERR_NOMEM;
|
|
}
|
|
|
|
//
|
|
// Get initial values for phone features (such as hookswitch state)
|
|
//
|
|
UpdatePhoneFeatures( pPhone );
|
|
|
|
//
|
|
// Close the file handle
|
|
//
|
|
if ( !CloseHidFile(pPhone->pHidDevice) )
|
|
{
|
|
LOG((PHONESP_ERROR, "CreatePhone - CloseHidFile failed"));
|
|
}
|
|
|
|
MemFree((LPVOID) wszPhoneInfo);
|
|
MemFree((LPVOID) wszPhoneName);
|
|
|
|
LOG((PHONESP_TRACE, "CreatePhone - exit"));
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
/******************************************************************************
|
|
NotifWndProc:
|
|
|
|
This function handles the pnp events for which this tsp has registered for
|
|
|
|
Arguments:
|
|
HWND hwnd
|
|
UINT uMsg
|
|
WPARAM wParam
|
|
LPARAM lParam
|
|
|
|
Returns LRESULT:
|
|
|
|
Comments:
|
|
|
|
******************************************************************************/
|
|
|
|
LRESULT CALLBACK NotifWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (uMsg)
|
|
{
|
|
case WM_DEVICECHANGE:
|
|
switch(wParam)
|
|
{
|
|
case DBT_DEVICEARRIVAL:
|
|
LOG((PHONESP_TRACE, "NotifWndProc - DBT_DEVICEARRIVAL"));
|
|
ReenumDevices();
|
|
break;
|
|
|
|
case DBT_DEVICEREMOVECOMPLETE:
|
|
LOG((PHONESP_TRACE, "NotifWndProc - DBT_DEVICEREMOVECOMPLETE"));
|
|
ReenumDevices();
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case WM_CREATE:
|
|
LOG((PHONESP_TRACE, "NotifWndProc - WM_CREATE"));
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
LOG((PHONESP_TRACE, "NotifWndProc - WM_DESTROY"));
|
|
break;
|
|
|
|
default:
|
|
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
/********************************NotifWndProc - end***************************/
|
|
|
|
|
|
/******************************************************************************
|
|
AsyncEventQueueServiceThread:
|
|
|
|
This routine services, in a serialized manner, the requests present in the
|
|
Async Queue. If no requests are currently outstanding, it waits for an
|
|
Event which happens when the queue has currently no requests and a new
|
|
request comes in.
|
|
|
|
Arguments:
|
|
LPVOID pParams: Any Information that needs to be passed to the thread
|
|
when startup. Currently no information is being passed.
|
|
|
|
Return Parameter: Void
|
|
|
|
******************************************************************************/
|
|
VOID
|
|
AsyncEventQueueServiceThread(
|
|
LPVOID pParams
|
|
)
|
|
{
|
|
WNDCLASS wc;
|
|
ATOM atom;
|
|
|
|
LOG((PHONESP_TRACE, "AsyncEventQueueServiceThread - enter"));
|
|
|
|
//
|
|
// Create a window to receive PNP device notifications
|
|
//
|
|
|
|
ZeroMemory(&wc, sizeof(wc));
|
|
wc.lpfnWndProc = NotifWndProc;
|
|
wc.lpszClassName = TEXT("HidPhoneNotifClass");
|
|
|
|
if (!(atom = RegisterClass(&wc)))
|
|
{
|
|
LOG((PHONESP_ERROR, "AsyncEventQueueServiceThread - can't register window class %08x", GetLastError()));
|
|
}
|
|
else
|
|
{
|
|
ghWndNotify = CreateWindow((LPCTSTR)atom, TEXT(""), 0,
|
|
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, NULL, NULL);
|
|
|
|
if (ghWndNotify == NULL)
|
|
{
|
|
LOG((PHONESP_ERROR, "AsyncEventQueueServiceThread - can't create notification window"));
|
|
}
|
|
else
|
|
{
|
|
DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;
|
|
|
|
LOG((PHONESP_TRACE, "AsyncEventQueueServiceThread - created notification window"));
|
|
|
|
//
|
|
// Register to receive PNP device notifications
|
|
//
|
|
|
|
ZeroMemory( &NotificationFilter, sizeof(NotificationFilter) );
|
|
NotificationFilter.dbcc_size =
|
|
sizeof(DEV_BROADCAST_DEVICEINTERFACE);
|
|
NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
|
|
NotificationFilter.dbcc_classguid = GUID_CLASS_INPUT;
|
|
|
|
if ((ghDevNotify = RegisterDeviceNotification( ghWndNotify,
|
|
&NotificationFilter,
|
|
DEVICE_NOTIFY_WINDOW_HANDLE
|
|
)) == NULL)
|
|
{
|
|
LOG((PHONESP_ERROR, "AsyncEventQueueServiceThread - can't register for input device notification"));
|
|
}
|
|
else
|
|
{
|
|
LOG((PHONESP_TRACE, "AsyncEventQueueServiceThread - registered for PNP device notifications"));
|
|
}
|
|
}
|
|
}
|
|
|
|
while (!gbProviderShutdown)
|
|
{
|
|
// Waiting for a new request to arrive since the queue is currently
|
|
// empty
|
|
|
|
DWORD dwResult;
|
|
MSG msg;
|
|
|
|
dwResult = MsgWaitForMultipleObjectsEx(
|
|
1, // wait for one event
|
|
&gAsyncQueue.hAsyncEventsPendingEvent, // array of events to wait for
|
|
INFINITE, // wait forever
|
|
QS_ALLINPUT, // get all window messages
|
|
0 // return when an event is signaled
|
|
);
|
|
|
|
if ( ( dwResult == WAIT_OBJECT_0 ) || ( dwResult == WAIT_OBJECT_0 + 1 ) )
|
|
{
|
|
LOG((PHONESP_TRACE, "AsyncEventQueueServiceThread - thread is signaled"));
|
|
|
|
while (1)
|
|
{
|
|
PPHONESP_ASYNC_REQ_INFO pAsyncReqInfo;
|
|
PPHONESP_PHONE_INFO pPhone;
|
|
|
|
EnterCriticalSection (&gAsyncQueue.AsyncEventQueueCritSec);
|
|
|
|
// No requests in the queue present - wait for a new request
|
|
if (gAsyncQueue.dwNumUsedQueueEntries == 0)
|
|
{
|
|
ResetEvent (gAsyncQueue.hAsyncEventsPendingEvent);
|
|
LeaveCriticalSection (&gAsyncQueue.AsyncEventQueueCritSec);
|
|
break;
|
|
}
|
|
|
|
pAsyncReqInfo = *gAsyncQueue.pAsyncRequestQueueOut;
|
|
|
|
// Increment the next-request-to-be-serviced counter
|
|
gAsyncQueue.pAsyncRequestQueueOut++;
|
|
|
|
|
|
//
|
|
// The queue is maintained a circular queue. If the bottom of the
|
|
// circular queue is reached, go back to the top and process the
|
|
// requests if any.
|
|
//
|
|
if (gAsyncQueue.pAsyncRequestQueueOut ==
|
|
(gAsyncQueue.pAsyncRequestQueue +
|
|
gAsyncQueue.dwNumTotalQueueEntries))
|
|
{
|
|
gAsyncQueue.pAsyncRequestQueueOut =
|
|
gAsyncQueue.pAsyncRequestQueue;
|
|
}
|
|
|
|
// Decrement the number of outstanding requests present in queue
|
|
gAsyncQueue.dwNumUsedQueueEntries--;
|
|
|
|
LeaveCriticalSection (&gAsyncQueue.AsyncEventQueueCritSec);
|
|
|
|
|
|
// If async function for the request exists - call the function
|
|
|
|
if (pAsyncReqInfo->pfnAsyncProc)
|
|
{
|
|
(*(pAsyncReqInfo->pfnAsyncProc))(
|
|
pAsyncReqInfo->pFuncInfo
|
|
);
|
|
}
|
|
|
|
pPhone = (PPHONESP_PHONE_INFO) pAsyncReqInfo->pFuncInfo->dwParam1;
|
|
|
|
// Decrement the counter of pending requests for this phone
|
|
|
|
if ( pPhone )
|
|
{
|
|
EnterCriticalSection(&pPhone->csThisPhone);
|
|
|
|
pPhone->dwNumPendingReqInQueue--;
|
|
|
|
// if there are no requests pending for this phone
|
|
// Set no requests pending event on this phone
|
|
if (pPhone->dwNumPendingReqInQueue == 0 )
|
|
{
|
|
SetEvent(pPhone->hNoPendingReqInQueueEvent);
|
|
}
|
|
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
}
|
|
|
|
// The memory allocated for the processed request is freed.
|
|
MemFree(pAsyncReqInfo->pFuncInfo);
|
|
MemFree(pAsyncReqInfo);
|
|
}
|
|
|
|
//
|
|
// We have processed all commands and unblocked everyone
|
|
// who is waiting for us. Now check for window messages.
|
|
//
|
|
|
|
while ( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) )
|
|
{
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
}
|
|
}
|
|
|
|
LOG((PHONESP_TRACE, "AsyncEventQueueServiceThread - shutdown"));
|
|
|
|
//
|
|
// Unregister for PNP device notifications and destroy window
|
|
//
|
|
|
|
if ( NULL != ghDevNotify )
|
|
{
|
|
if (!UnregisterDeviceNotification(ghDevNotify))
|
|
{
|
|
LOG((PHONESP_ERROR, "AsyncEventQueueServiceThread - "
|
|
"can't unregister device notification %d", GetLastError()));
|
|
|
|
}
|
|
|
|
ghDevNotify = NULL;
|
|
}
|
|
|
|
if ( NULL != ghWndNotify )
|
|
{
|
|
if (!DestroyWindow(ghWndNotify))
|
|
{
|
|
LOG((PHONESP_ERROR, "AsyncEventQueueServiceThread - "
|
|
"can't destroy notification window %d", GetLastError()));
|
|
}
|
|
|
|
ghWndNotify = NULL;
|
|
}
|
|
|
|
if (!UnregisterClass((LPCTSTR)atom, GetModuleHandle(NULL)))
|
|
{
|
|
LOG((PHONESP_ERROR, "AsyncEventQueueServiceThread - "
|
|
"can't unregister window class %d", GetLastError()));
|
|
}
|
|
|
|
LOG((PHONESP_TRACE, "AsyncEventQueueServiceThread - exit"));
|
|
|
|
// Since the Provider Shutdown is called .. we terminate the thread
|
|
ExitThread (0);
|
|
}
|
|
/*************************AsyncEventQueueServiceThread - end******************/
|
|
|
|
|
|
/******************************************************************************
|
|
ReadThread:
|
|
|
|
Arguments:
|
|
|
|
PVOID lpParameter - The parameter passed to the function when this
|
|
function is called. In this case - the parameter is
|
|
the pointer to the phone structure (PMYPHONE) that
|
|
has just been opened
|
|
|
|
Returns VOID
|
|
******************************************************************************/
|
|
VOID
|
|
ReadThread(
|
|
PVOID lpParameter
|
|
)
|
|
{
|
|
PPHONESP_PHONE_INFO pPhone;
|
|
PHID_DEVICE pHidDevice;
|
|
DWORD dwInputDataCnt;
|
|
PHID_DATA pHidData;
|
|
DWORD dwResult;
|
|
HANDLE hWaitHandles[2];
|
|
DWORD dwWaitResult;
|
|
|
|
LOG((PHONESP_TRACE, "ReadThread - enter"));
|
|
|
|
EnterCriticalSection(&csAllPhones);
|
|
|
|
pPhone = (PPHONESP_PHONE_INFO) lpParameter;
|
|
|
|
// Check whether the phone handle is still valid
|
|
if ( IsBadReadPtr(pPhone,sizeof(PHONESP_PHONE_INFO) ))
|
|
{
|
|
LOG((PHONESP_ERROR, "ReadThread - phone handle invalid"));
|
|
|
|
LeaveCriticalSection(&csAllPhones);
|
|
ExitThread(0);
|
|
}
|
|
|
|
EnterCriticalSection(&pPhone->csThisPhone);
|
|
LeaveCriticalSection(&csAllPhones);
|
|
|
|
// Check whether the phone handle is still in use
|
|
if ( !pPhone->bAllocated )
|
|
{
|
|
LOG((PHONESP_ERROR, "ReadThread - phone not allocated"));
|
|
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
ExitThread(0);
|
|
}
|
|
|
|
// verify whether the phone is open
|
|
if( !pPhone->bPhoneOpen )
|
|
{
|
|
LOG((PHONESP_ERROR, "ReadThread - Phone not open"));
|
|
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
ExitThread(0);
|
|
}
|
|
|
|
pHidDevice = pPhone->pHidDevice;
|
|
|
|
// Check whether hid device is present
|
|
if ( pHidDevice == NULL )
|
|
{
|
|
LOG((PHONESP_ERROR, "ReadThread - invalid hid device pointer"));
|
|
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
ExitThread(0);
|
|
}
|
|
|
|
hWaitHandles[0] = pPhone->hCloseEvent;
|
|
hWaitHandles[1] = pPhone->hInputReportEvent;
|
|
|
|
while (TRUE)
|
|
{
|
|
if (! ReadInputReport(pPhone))
|
|
{
|
|
LOG((PHONESP_ERROR, "ReadThread - ReadInputReport failed - exiting"));
|
|
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
ExitThread(0);
|
|
}
|
|
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
|
|
//
|
|
// Wait for the read to complete, or the phone to be closed
|
|
//
|
|
|
|
dwWaitResult = WaitForMultipleObjects( 2, hWaitHandles, FALSE, INFINITE );
|
|
|
|
LOG((PHONESP_TRACE, "ReadThread - activated"));
|
|
|
|
if ( dwWaitResult == WAIT_OBJECT_0 )
|
|
{
|
|
LOG((PHONESP_TRACE, "ReadThread - CloseEvent fired - exiting"));
|
|
|
|
//
|
|
// Cancel the pending IO operation
|
|
//
|
|
|
|
CancelIo( pHidDevice->HidDevice );
|
|
ExitThread(0);
|
|
}
|
|
|
|
EnterCriticalSection(&pPhone->csThisPhone);
|
|
|
|
// This function is implemented in report.c
|
|
// The report received from the device is unmarshalled here
|
|
if ( UnpackReport(
|
|
pHidDevice->InputReportBuffer,
|
|
pHidDevice->Caps.InputReportByteLength,
|
|
HidP_Input,
|
|
pHidDevice->InputData,
|
|
pHidDevice->InputDataLength,
|
|
pHidDevice->Ppd
|
|
) )
|
|
{
|
|
|
|
for (dwInputDataCnt = 0, pHidData = pHidDevice->InputData;
|
|
dwInputDataCnt < pHidDevice->InputDataLength;
|
|
pHidData++, dwInputDataCnt++)
|
|
{
|
|
|
|
// Since pHidData->IsDataSet in all the input HidData structures
|
|
// initialized to false before reading the input report .. if the
|
|
// pHidData->IsDataSet is set for the HidData structure, that
|
|
// HidData structure contains the new input report
|
|
// Also we are interested in only telephony usage page usages only
|
|
|
|
if ( pHidData->IsDataSet &&
|
|
( (pHidData->UsagePage == HID_USAGE_PAGE_TELEPHONY) ||
|
|
(pHidData->UsagePage == HID_USAGE_PAGE_CONSUMER) ) )
|
|
{
|
|
PPHONESP_FUNC_INFO pFuncInfo;
|
|
PPHONESP_ASYNC_REQ_INFO pAsyncReqInfo;
|
|
|
|
if( ! (pFuncInfo = (PPHONESP_FUNC_INFO)
|
|
MemAlloc(sizeof (PHONESP_FUNC_INFO)) ) )
|
|
{
|
|
LOG((PHONESP_ERROR, "ReadThread - "
|
|
"MemAlloc pFuncInfo - out of memory"));
|
|
|
|
continue;
|
|
}
|
|
|
|
ZeroMemory(pFuncInfo, sizeof(PHONESP_FUNC_INFO));
|
|
|
|
pFuncInfo->dwParam1 = (ULONG_PTR) pPhone;
|
|
|
|
if ( ! ( pAsyncReqInfo = (PPHONESP_ASYNC_REQ_INFO)
|
|
MemAlloc(sizeof(PHONESP_ASYNC_REQ_INFO))))
|
|
{
|
|
LOG((PHONESP_ERROR, "ReadThread - "
|
|
"MemAlloc pAsyncReqInfo - out of memory"));
|
|
|
|
MemFree(pFuncInfo);
|
|
|
|
continue;
|
|
}
|
|
|
|
pAsyncReqInfo->pfnAsyncProc = ShowData;
|
|
pAsyncReqInfo->pFuncInfo = pFuncInfo;
|
|
|
|
// if the usage is associated with a Button
|
|
if( pHidData->IsButtonData )
|
|
{
|
|
PUSAGE Usages;
|
|
|
|
// fill the structure to be put on the async queue
|
|
if ( ! ( Usages = (PUSAGE)
|
|
MemAlloc(sizeof(USAGE) *
|
|
pHidData->ButtonData.MaxUsageLength) ) )
|
|
{
|
|
LOG((PHONESP_ERROR, "ReadIOCompletionRoutine - "
|
|
"MemAlloc Usages - out of memory"));
|
|
|
|
MemFree(pFuncInfo);
|
|
MemFree(pAsyncReqInfo);
|
|
|
|
continue;
|
|
}
|
|
|
|
pFuncInfo->dwNumParams = 7;
|
|
pFuncInfo->dwParam2 = PHONESP_BUTTON;
|
|
pFuncInfo->dwParam3 = pHidData->UsagePage;
|
|
pFuncInfo->dwParam4 = pHidData->ButtonData.UsageMin;
|
|
pFuncInfo->dwParam5 = pHidData->ButtonData.UsageMax;
|
|
pFuncInfo->dwParam6 = pHidData->ButtonData.MaxUsageLength;
|
|
|
|
CopyMemory(Usages,
|
|
pHidData->ButtonData.Usages,
|
|
sizeof(USAGE) *
|
|
pHidData->ButtonData.MaxUsageLength
|
|
);
|
|
|
|
pFuncInfo->dwParam7 = (ULONG_PTR) Usages;
|
|
}
|
|
else
|
|
{
|
|
// the usage is associated with a Value
|
|
pFuncInfo->dwNumParams = 5;
|
|
pFuncInfo->dwParam2 = PHONESP_VALUE;
|
|
pFuncInfo->dwParam3 = pHidData->UsagePage;
|
|
pFuncInfo->dwParam4 = pHidData->ValueData.Usage;
|
|
pFuncInfo->dwParam5 = pHidData->ValueData.Value;
|
|
}
|
|
|
|
if ( AsyncRequestQueueIn(pAsyncReqInfo) )
|
|
{
|
|
// Reset the event for number of pending requests in
|
|
// queue for this phone and increment the counter
|
|
if (pPhone->dwNumPendingReqInQueue == 0)
|
|
{
|
|
ResetEvent(pPhone->hNoPendingReqInQueueEvent);
|
|
}
|
|
pPhone->dwNumPendingReqInQueue++;
|
|
}
|
|
else
|
|
{
|
|
if ( pFuncInfo->dwParam2 == PHONESP_BUTTON )
|
|
{
|
|
MemFree((LPVOID)pFuncInfo->dwParam7);
|
|
}
|
|
|
|
MemFree(pFuncInfo);
|
|
MemFree(pAsyncReqInfo);
|
|
|
|
LOG((PHONESP_ERROR,"ReadIOCompletionRoutine - "
|
|
"AsyncRequestQueueIn failed"));
|
|
|
|
continue;
|
|
}
|
|
|
|
//ShowData(pFuncInfo);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/******************** ReadThread - end****************************/
|
|
|
|
|
|
/******************************************************************************
|
|
ReadInputReport
|
|
|
|
This function reads the phone device asynchronously. When an input report
|
|
is received from the device, the Event specified in the lpOverlapped
|
|
structure which is part of the PHONESP_PHONE_INFO structure is set. This
|
|
event results in ReadIOcompletionRoutine being called
|
|
|
|
Arguments:
|
|
PPHONESP_PHONE_INFO pPhone - the pointer to the phone to be read
|
|
|
|
Return BOOL:
|
|
TRUE if the function succeeds
|
|
FALSE if the function fails
|
|
|
|
******************************************************************************/
|
|
BOOL
|
|
ReadInputReport (
|
|
PPHONESP_PHONE_INFO pPhone
|
|
)
|
|
{
|
|
DWORD i, dwResult;
|
|
PHID_DEVICE pHidDevice;
|
|
PHID_DATA pData;
|
|
BOOL bResult;
|
|
|
|
LOG((PHONESP_TRACE, "ReadInputReport - enter"));
|
|
|
|
pHidDevice = pPhone->pHidDevice;
|
|
|
|
// Check whether hid device is present
|
|
if ( pHidDevice == NULL )
|
|
{
|
|
LOG((PHONESP_ERROR, "ReadInputReport - invalid hid device pointer"));
|
|
return FALSE;
|
|
}
|
|
|
|
pData = pHidDevice->InputData;
|
|
|
|
//
|
|
// Set all the input hid data structures to False so we can identify the
|
|
// new reports from the device
|
|
for ( i = 0; i < pHidDevice->InputDataLength; i++, pData++)
|
|
{
|
|
pData->IsDataSet = FALSE;
|
|
}
|
|
|
|
bResult = ReadFile(
|
|
pHidDevice->HidDevice,
|
|
pHidDevice->InputReportBuffer,
|
|
pHidDevice->Caps.InputReportByteLength,
|
|
NULL,
|
|
pPhone->lpOverlapped
|
|
);
|
|
|
|
if ( !bResult )
|
|
{
|
|
// if the Readfile succeeds then GetLastError returns ERROR_IO_PENDING since
|
|
// this is an asynchronous read
|
|
|
|
dwResult = GetLastError();
|
|
|
|
if ( dwResult && ( dwResult != ERROR_IO_PENDING ) )
|
|
{
|
|
LOG((PHONESP_ERROR, "ReadInputReport - ReadFile Failed, error: %d",
|
|
GetLastError()));
|
|
|
|
if (dwResult == ERROR_DEVICE_NOT_CONNECTED)
|
|
{
|
|
//
|
|
// The hid device has most likely gone away. Lets close the file
|
|
// handle so we can get proper pnp notifications.
|
|
//
|
|
if ( CloseHidFile(pHidDevice) )
|
|
{
|
|
LOG((PHONESP_TRACE, "ReadInputReport - "
|
|
"closed hid device file handle"));
|
|
}
|
|
else
|
|
{
|
|
LOG((PHONESP_ERROR, "ReadInputReport - "
|
|
"CloseHidFile failed" ));
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
LOG((PHONESP_TRACE, "ReadInputReport - exit"));
|
|
return TRUE;
|
|
}
|
|
/************************ReadInputReport - end *******************************/
|
|
|
|
// --------------------------- TAPI_lineXxx funcs -----------------------------
|
|
//
|
|
|
|
|
|
// The TSPI_lineNegotiateTSPIVersion function returns the highest SPI version the
|
|
// service provider can operate under for this device, given the range of possible
|
|
// SPI versions.
|
|
|
|
|
|
LONG
|
|
TSPIAPI
|
|
TSPI_lineNegotiateTSPIVersion(
|
|
DWORD dwDeviceID,
|
|
DWORD dwLowVersion,
|
|
DWORD dwHighVersion,
|
|
LPDWORD lpdwTSPIVersion
|
|
)
|
|
{
|
|
LOG((PHONESP_TRACE, "TSPI_lineNegotiateTSPIVersion - enter"));
|
|
|
|
|
|
if (dwHighVersion >= HIGH_VERSION)
|
|
{
|
|
// If the high version of the app is greater than the high version
|
|
// supported by this TSP and the low version of the app is less than
|
|
// the High version of the TSP - The TSP high version will be negotiated
|
|
// else the tsp cannot support this app
|
|
if (dwLowVersion <= HIGH_VERSION)
|
|
{
|
|
*lpdwTSPIVersion = (DWORD) HIGH_VERSION;
|
|
}
|
|
else
|
|
{ // the app is too new for us
|
|
return LINEERR_INCOMPATIBLEAPIVERSION;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(dwHighVersion >= LOW_VERSION)
|
|
{
|
|
*lpdwTSPIVersion = dwHighVersion;
|
|
}
|
|
else
|
|
{
|
|
//we are too new for the app
|
|
return LINEERR_INCOMPATIBLEAPIVERSION;
|
|
}
|
|
}
|
|
LOG((PHONESP_TRACE, "TSPI_lineNegotiateTSPIVersion - exit"));
|
|
return 0;
|
|
}
|
|
|
|
|
|
//
|
|
// -------------------------- TSPI_phoneXxx funcs -----------------------------
|
|
//
|
|
|
|
/******************************************************************************
|
|
TSPI_phoneClose:
|
|
|
|
This function closes the specified open phone device after completing all
|
|
the asynchronous operations pending on the device
|
|
|
|
Arguments:
|
|
HDRVPHONE hdPhone - the handle to the phone to be closed
|
|
|
|
Returns LONG:
|
|
Zero if the function succeeds
|
|
Error code if an error occurs - Possible values are
|
|
PHONEERR_INVALPHONEHANDLE
|
|
|
|
******************************************************************************/
|
|
LONG
|
|
TSPIAPI
|
|
TSPI_phoneClose(
|
|
HDRVPHONE hdPhone
|
|
)
|
|
{
|
|
PPHONESP_PHONE_INFO pPhone;
|
|
LOG((PHONESP_TRACE, "TSPI_phoneClose - enter"));
|
|
|
|
// We need a critical section in order to ensure that the critical section
|
|
// of the phone is obtained while the phone handle is still valid.
|
|
EnterCriticalSection(&csAllPhones);
|
|
|
|
pPhone = (PPHONESP_PHONE_INFO) gpPhone[ (DWORD_PTR) hdPhone ];
|
|
|
|
// Check whether the phone handle is valid
|
|
if ( IsBadReadPtr( pPhone,sizeof(PHONESP_PHONE_INFO) ) )
|
|
{
|
|
LeaveCriticalSection(&csAllPhones);
|
|
LOG((PHONESP_ERROR, "TSPI_phoneClose - Phone handle invalid"));
|
|
return PHONEERR_INVALPHONEHANDLE;
|
|
}
|
|
|
|
EnterCriticalSection(&pPhone->csThisPhone);
|
|
LeaveCriticalSection(&csAllPhones);
|
|
|
|
// Check whether the phone handle is still in use
|
|
if ( !pPhone->bAllocated )
|
|
{
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
|
|
LOG((PHONESP_ERROR, "TSPI_phoneClose - phone not allocated"));
|
|
return PHONEERR_NODEVICE;
|
|
}
|
|
|
|
// Check if the phone to be closed is still open
|
|
if( pPhone->bPhoneOpen )
|
|
{
|
|
// Inorder to ensure that there no other activities happen on the phone
|
|
|
|
pPhone->bPhoneOpen = FALSE;
|
|
|
|
//
|
|
// wait for the read thread to exit
|
|
//
|
|
SetEvent(pPhone->hCloseEvent);
|
|
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
|
|
LOG((PHONESP_TRACE,"TSPI_phoneClose - waiting for read thread"));
|
|
|
|
WaitForSingleObject(pPhone->hReadThread, INFINITE);
|
|
|
|
LOG((PHONESP_TRACE,"TSPI_phoneClose - read thread complete"));
|
|
|
|
EnterCriticalSection(&pPhone->csThisPhone);
|
|
|
|
//
|
|
// if there are still pending requests on the phone in the queue, wait
|
|
// till all the pending asynchronous operations are completed
|
|
//
|
|
if (pPhone->dwNumPendingReqInQueue)
|
|
{
|
|
LOG((PHONESP_TRACE,"TSPI_phoneClose - requests pending"));
|
|
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
|
|
WaitForSingleObject(&pPhone->hNoPendingReqInQueueEvent, INFINITE);
|
|
|
|
EnterCriticalSection(&pPhone->csThisPhone);
|
|
|
|
LOG((PHONESP_TRACE,"TSPI_phoneClose - requests completed"));
|
|
}
|
|
|
|
CloseHandle(pPhone->hReadThread);
|
|
CloseHandle(pPhone->hCloseEvent);
|
|
CloseHandle(pPhone->hInputReportEvent);
|
|
|
|
MemFree(pPhone->lpOverlapped);
|
|
pPhone->htPhone = NULL;
|
|
|
|
//
|
|
// Close HID file handle
|
|
//
|
|
if ( !CloseHidFile(pPhone->pHidDevice) )
|
|
{
|
|
LOG((PHONESP_WARN, "TSPI_phoneClose - CloseHidFile failed"));
|
|
}
|
|
|
|
if (pPhone->bRemovePending)
|
|
{
|
|
//
|
|
// This phone is gone, lets get rid of it
|
|
//
|
|
|
|
pPhone->bRemovePending = FALSE;
|
|
|
|
FreePhone(pPhone);
|
|
|
|
LOG((PHONESP_TRACE, "TSPI_phoneClose - phone remove complete [dwDeviceID %d] ", pPhone->dwDeviceID));
|
|
}
|
|
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
}
|
|
else
|
|
{
|
|
LOG((PHONESP_ERROR,"TSPI_phoneClose - Phone Not Open"));
|
|
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
|
|
return PHONEERR_INVALPHONEHANDLE;
|
|
}
|
|
|
|
LOG((PHONESP_TRACE, "TSPI_phoneClose - exit"));
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
The TSPI_phoneDevSpecific:
|
|
|
|
This function is used as a general extension mechanism to enable a Telephony
|
|
API implementation to provide features not described in the other operations.
|
|
The meanings of these extensions are device specific.
|
|
|
|
|
|
Comments: To be implemented in Tier 2
|
|
******************************************************************************/
|
|
|
|
LONG
|
|
TSPIAPI
|
|
TSPI_phoneDevSpecific(
|
|
DRV_REQUESTID dwRequestID,
|
|
HDRVPHONE hdPhone,
|
|
LPVOID lpParams,
|
|
DWORD dwSize
|
|
)
|
|
{
|
|
LOG((PHONESP_TRACE, "TSPI_phoneDevSpecific - enter"));
|
|
LOG((PHONESP_TRACE, "TSPI_phoneDevSpecific - exit"));
|
|
return PHONEERR_OPERATIONUNAVAIL;
|
|
}
|
|
|
|
/***************************TSPI_phoneDevSpecific -End ***********************/
|
|
|
|
|
|
/******************************************************************************
|
|
TSPI_phoneGetButtonInfo:
|
|
This function returns information about a specified button.
|
|
|
|
Arguments:
|
|
IN HDRVPHONE hdPhone - The handle to the phone to be queried.
|
|
IN DWORD dwButtonLampID - A button on the phone device.
|
|
IN OUT LPPHONEBUTTONINFO lpButtonInfo - A pointer to memory into which
|
|
the TSP writes a variably sized structure of type PHONEBUTTONINFO.
|
|
This data structure describes the mode and function, and provides
|
|
additional descriptive text corresponding to the button.
|
|
|
|
Return Values
|
|
Returns zero if the function succeeds, or
|
|
An error number if an error occurs. Possible return values are as follows:
|
|
PHONEERR_INVALPHONEHANDLE, _INVALBUTTONLAMPID,_INVALPHONESTATE
|
|
|
|
******************************************************************************/
|
|
LONG
|
|
TSPIAPI
|
|
TSPI_phoneGetButtonInfo(
|
|
HDRVPHONE hdPhone,
|
|
DWORD dwButtonLampID,
|
|
LPPHONEBUTTONINFO lpButtonInfo
|
|
)
|
|
{
|
|
|
|
PPHONESP_PHONE_INFO pPhone;
|
|
PPHONESP_BUTTONINFO pButtonInfo;
|
|
DWORD dwNeededSize;
|
|
|
|
LOG((PHONESP_TRACE, "TSPI_phoneGetButtonInfo - enter"));
|
|
|
|
if (lpButtonInfo->dwTotalSize < sizeof(PHONEBUTTONINFO))
|
|
{
|
|
LOG((PHONESP_ERROR, "TSPI_phoneGetButtonInfo - structure too small"));
|
|
return PHONEERR_STRUCTURETOOSMALL;
|
|
}
|
|
|
|
EnterCriticalSection(&csAllPhones);
|
|
|
|
pPhone = (PPHONESP_PHONE_INFO) gpPhone[ (DWORD_PTR) hdPhone ];
|
|
|
|
// Check if pPhone points to a valid memory location - if not handle is
|
|
// invalid
|
|
if ( IsBadReadPtr( pPhone,sizeof(PHONESP_PHONE_INFO) ) )
|
|
{
|
|
LeaveCriticalSection(&csAllPhones);
|
|
LOG((PHONESP_ERROR, "TSPI_phoneGetButtonInfo - Phone handle invalid"));
|
|
return PHONEERR_INVALPHONEHANDLE;
|
|
}
|
|
|
|
|
|
EnterCriticalSection(&pPhone->csThisPhone);
|
|
LeaveCriticalSection(&csAllPhones);
|
|
|
|
// Check whether the phone handle is still in use
|
|
if ( !pPhone->bAllocated )
|
|
{
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
LOG((PHONESP_ERROR, "TSPI_GetButtonInfo - phone not allocated"));
|
|
return PHONEERR_NODEVICE;
|
|
}
|
|
|
|
// verify whether the phone is open
|
|
if ( ! (pPhone->bPhoneOpen) )
|
|
{
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
LOG((PHONESP_ERROR,"TSPI_GetButtonInfo - Phone not open"));
|
|
return PHONEERR_INVALPHONESTATE;
|
|
}
|
|
|
|
|
|
// Get the Button structure for the queried button id if it exists
|
|
// else pButtonInfo will be NULL
|
|
if ( ! ( pButtonInfo = GetButtonFromID(pPhone, dwButtonLampID) ) )
|
|
{
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
LOG((PHONESP_TRACE, "TSPI_phoneGetButtonInfo - Invalid Button ID"));
|
|
return PHONEERR_INVALBUTTONLAMPID;
|
|
}
|
|
|
|
// The needed size to store all the available information on the button
|
|
lpButtonInfo->dwNeededSize = sizeof(PHONEBUTTONINFO) +
|
|
(lstrlen(pButtonInfo->szButtonText) + 1) *
|
|
sizeof (WCHAR); // size of the Button Text
|
|
|
|
// Whether the button is a Feature Button, Keypad, etc
|
|
lpButtonInfo->dwButtonMode = pButtonInfo->dwButtonMode;
|
|
|
|
// The function associated with this button - will be _NONE for keypad
|
|
// buttons and _FLASH, _HOLD, etc for feature buttons
|
|
lpButtonInfo->dwButtonFunction = pButtonInfo->dwButtonFunction;
|
|
|
|
// The current button state
|
|
lpButtonInfo->dwButtonState = pButtonInfo->dwButtonState;
|
|
|
|
if (lpButtonInfo->dwTotalSize >= lpButtonInfo->dwNeededSize)
|
|
{
|
|
lpButtonInfo->dwUsedSize = lpButtonInfo->dwNeededSize;
|
|
|
|
// ButtonTextSize is the memory required to copy the string stored in
|
|
// szButtonText field of the PHONESP_BUTTON_INFO structure for this
|
|
// Button
|
|
lpButtonInfo->dwButtonTextSize = (lstrlen(pButtonInfo->szButtonText)+1)
|
|
* sizeof (WCHAR);
|
|
|
|
// Offset of the button text from the PHONEBUTTONINFO structure
|
|
lpButtonInfo->dwButtonTextOffset = sizeof(PHONEBUTTONINFO);
|
|
|
|
// Copy the button text at the lpButtonInfo->dwButtonTextOffset offset
|
|
// from the ButtonText stored in the PHONESP_BUTTON_INFO structure for
|
|
// this Button.
|
|
CopyMemory(
|
|
(LPBYTE)lpButtonInfo + lpButtonInfo->dwButtonTextOffset,
|
|
pButtonInfo->szButtonText,
|
|
lpButtonInfo->dwButtonTextSize
|
|
);
|
|
}
|
|
else
|
|
{
|
|
// no space to the store the button text info
|
|
lpButtonInfo->dwUsedSize = sizeof(PHONEBUTTONINFO);
|
|
lpButtonInfo->dwButtonTextSize = 0;
|
|
lpButtonInfo->dwButtonTextOffset = 0;
|
|
}
|
|
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
|
|
LOG((PHONESP_TRACE, "TSPI_phoneGetButtonInfo - exit"));
|
|
return 0;
|
|
}
|
|
/********************TSPI_phoneGetButtonInfo - end****************************/
|
|
|
|
|
|
/******************************************************************************
|
|
TSPI_phoneGetDevCaps:
|
|
|
|
This function queries a specified phone device to determine its telephony
|
|
capabilities.
|
|
|
|
Arguments:
|
|
DWORD dwDeviceID - The phone device to be queried.
|
|
DWORD dwTSPIVersion - The negotiated TSPI version number. This value is
|
|
negotiated for this device through the
|
|
TSPI_phoneNegotiateTSPIVersion function.
|
|
DWORD dwExtVersion - The negotiated extension version number. This
|
|
value is negotiated for this device through the
|
|
TSPI_phoneNegotiateExtVersion function.
|
|
PHONECAPS lpPhoneCaps - A pointer to memory into which the TSP writes a
|
|
variably sized structure of type PHONECAPS.
|
|
Upon successful completion of the request, this
|
|
structure is filled with phone device capability
|
|
information.
|
|
|
|
Returns LONG:
|
|
Zero if success
|
|
PHONEERR_ constants if an error occurs. Possible return values are:
|
|
_BADDEVICEID,
|
|
|
|
|
|
******************************************************************************/
|
|
LONG
|
|
TSPIAPI
|
|
TSPI_phoneGetDevCaps(
|
|
DWORD dwDeviceID,
|
|
DWORD dwTSPIVersion,
|
|
DWORD dwExtVersion,
|
|
LPPHONECAPS lpPhoneCaps
|
|
)
|
|
{
|
|
PPHONESP_PHONE_INFO pPhone;
|
|
PPHONESP_BUTTONINFO pButtonInfo;
|
|
|
|
LOG((PHONESP_TRACE, "TSPI_phoneGetDevCaps - enter"));
|
|
|
|
if (lpPhoneCaps->dwTotalSize < sizeof(PHONECAPS))
|
|
{
|
|
LOG((PHONESP_ERROR, "TSPI_phoneGetDevCaps - structure too small"));
|
|
return PHONEERR_STRUCTURETOOSMALL;
|
|
}
|
|
|
|
EnterCriticalSection(&csAllPhones);
|
|
|
|
// Given the deviceID retrieve the structure that contains the information
|
|
// for this device
|
|
pPhone = GetPhoneFromID(dwDeviceID, NULL);
|
|
|
|
if ( ! pPhone)
|
|
{
|
|
LeaveCriticalSection(&csAllPhones);
|
|
LOG((PHONESP_ERROR,"TSPI_phoneGetDevCaps - Bad Device ID"));
|
|
return PHONEERR_BADDEVICEID;
|
|
}
|
|
|
|
EnterCriticalSection(&pPhone->csThisPhone);
|
|
LeaveCriticalSection(&csAllPhones);
|
|
|
|
// Check whether the phone handle is still in use
|
|
if ( !pPhone->bAllocated )
|
|
{
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
LOG((PHONESP_ERROR, "TSPI_phoneGetDevCaps - phone not allocated"));
|
|
return PHONEERR_NODEVICE;
|
|
}
|
|
|
|
//
|
|
// The size in bytes for this data structure that is needed to hold all the
|
|
// returned information. The returned includes the providerInfo string,
|
|
// PhoneInfo string and Phone Name string and Buttons Info - Button Function
|
|
// and Button Mode.
|
|
//
|
|
lpPhoneCaps->dwNeededSize = sizeof (PHONECAPS) +
|
|
sizeof (WCHAR) *
|
|
( (lstrlenW(gszProviderInfo) + 1) +
|
|
(lstrlenW(pPhone->wszPhoneInfo) + 1) +
|
|
(lstrlenW(pPhone->wszPhoneName) + 1) ) +
|
|
(sizeof(DWORD) * pPhone->dwNumButtons * 2);
|
|
|
|
lpPhoneCaps->dwUsedSize = sizeof(PHONECAPS);
|
|
|
|
// lpPhoneCaps->dwPermanentPhoneID = ;
|
|
|
|
//The string format to be used with this phone device
|
|
lpPhoneCaps->dwStringFormat = STRINGFORMAT_UNICODE;
|
|
|
|
// The state changes for this phone device for which the application can be
|
|
// notified in a PHONE_STATE message. The Phone Info structure for each
|
|
// maintains this information
|
|
lpPhoneCaps->dwPhoneStates = pPhone->dwPhoneStates;
|
|
|
|
// Specifies the phone's hookswitch devices. Again the Phone Info structure
|
|
// maintains this information
|
|
lpPhoneCaps->dwHookSwitchDevs = pPhone->dwHookSwitchDevs;
|
|
|
|
// Specifies that we are a generic phone device. This means that in TAPI 3.1
|
|
// we will be able to function on a variety of addresses.
|
|
lpPhoneCaps->dwPhoneFeatures = PHONEFEATURE_GENERICPHONE;
|
|
|
|
if(pPhone->dwHandset)
|
|
{ // Specifies the phone's hookswitch mode capabilities of the handset.
|
|
// The member is only meaningful if the hookswitch device is listed in
|
|
// dwHookSwitchDevs.
|
|
lpPhoneCaps->dwHandsetHookSwitchModes = PHONEHOOKSWITCHMODE_ONHOOK | PHONEHOOKSWITCHMODE_MICSPEAKER;
|
|
|
|
lpPhoneCaps->dwPhoneFeatures |= PHONEFEATURE_GETHOOKSWITCHHANDSET;
|
|
}
|
|
|
|
if(pPhone->dwSpeaker)
|
|
{
|
|
// Specifies the phone's hookswitch mode capabilities of the speaker.
|
|
// The member is only meaningful if the hookswitch device is listed in
|
|
// dwHookSwitchDevs.
|
|
lpPhoneCaps->dwSpeakerHookSwitchModes = PHONEHOOKSWITCHMODE_ONHOOK | PHONEHOOKSWITCHMODE_MICSPEAKER;
|
|
|
|
lpPhoneCaps->dwPhoneFeatures |= PHONEFEATURE_GETHOOKSWITCHSPEAKER |
|
|
PHONEFEATURE_SETHOOKSWITCHSPEAKER;
|
|
}
|
|
|
|
// The ring capabilities of the phone device. The phone is able to ring
|
|
// with dwNumRingModes different ring patterns, identified as 1, 2, through
|
|
// dwNumRingModes minus one. If the value of this member is 0, applications
|
|
// have no control over the ring mode of the phone. If the value of this
|
|
// member is greater than 0, it indicates the number of ring modes in
|
|
// addition to silence that are supported by the TSP. In this case, only one
|
|
// mode is supported.
|
|
if(pPhone->dwRing)
|
|
{
|
|
lpPhoneCaps->dwNumRingModes = 1;
|
|
|
|
lpPhoneCaps->dwPhoneFeatures |= PHONEFEATURE_GETRING |
|
|
PHONEFEATURE_SETRING;
|
|
}
|
|
|
|
if(pPhone->dwNumButtons)
|
|
{
|
|
// Specifies the number of button/lamps on the phone device that are
|
|
// detectable in TAPI. Button/lamps are identified by their identifier.
|
|
lpPhoneCaps->dwNumButtonLamps = pPhone->dwNumButtons;
|
|
|
|
lpPhoneCaps->dwPhoneFeatures |= PHONEFEATURE_GETBUTTONINFO;
|
|
}
|
|
|
|
if(lpPhoneCaps->dwTotalSize >= lpPhoneCaps->dwNeededSize)
|
|
{
|
|
DWORD dwAlignedSize;
|
|
DWORD dwRealSize;
|
|
|
|
|
|
///////////////////
|
|
// Provider Info
|
|
///////////////////
|
|
|
|
// Size of the Provider Info string in bytes
|
|
lpPhoneCaps->dwProviderInfoSize = ( lstrlen(gszProviderInfo) + 1) *
|
|
sizeof (WCHAR);
|
|
dwRealSize = lpPhoneCaps->dwProviderInfoSize;
|
|
|
|
// Offset of the Provider Info String from the PHONECAPS structure
|
|
lpPhoneCaps->dwProviderInfoOffset = lpPhoneCaps->dwUsedSize;
|
|
|
|
|
|
// Align it across DWORD boundary
|
|
if (dwRealSize % sizeof(DWORD))
|
|
{
|
|
dwAlignedSize = dwRealSize - (dwRealSize % sizeof(DWORD)) +
|
|
sizeof(DWORD);
|
|
}
|
|
else
|
|
{
|
|
dwAlignedSize = dwRealSize;
|
|
}
|
|
|
|
// Copy the provider Info string at the offset specified by
|
|
// lpPhoneCaps->dwProviderInfoOffset
|
|
CopyMemory(
|
|
((LPBYTE)lpPhoneCaps) + lpPhoneCaps->dwProviderInfoOffset,
|
|
gszProviderInfo,
|
|
lpPhoneCaps->dwProviderInfoSize
|
|
);
|
|
|
|
lpPhoneCaps->dwNeededSize += dwAlignedSize - dwRealSize;
|
|
|
|
///////////////////
|
|
// Phone Info
|
|
///////////////////
|
|
|
|
// Size of the Phone Info string in bytes
|
|
lpPhoneCaps->dwPhoneInfoSize = (lstrlen(pPhone->wszPhoneInfo) + 1) *
|
|
sizeof(WCHAR);
|
|
dwRealSize = lpPhoneCaps->dwPhoneInfoSize;
|
|
|
|
// Offset of the Phone Info String from the PHONECAPS structure
|
|
lpPhoneCaps->dwPhoneInfoOffset = lpPhoneCaps->dwProviderInfoOffset +
|
|
dwAlignedSize;
|
|
|
|
// Align it across DWORD boundary
|
|
if (dwRealSize % sizeof(DWORD))
|
|
{
|
|
dwAlignedSize = dwRealSize - (dwRealSize % sizeof(DWORD)) +
|
|
sizeof(DWORD);
|
|
}
|
|
else
|
|
{
|
|
dwAlignedSize = dwRealSize;
|
|
}
|
|
|
|
// Copy the Phone Info string at the offset specified by
|
|
// lpPhoneCaps->dwPhoneInfoOffset
|
|
CopyMemory(
|
|
((LPBYTE)lpPhoneCaps) + lpPhoneCaps->dwPhoneInfoOffset,
|
|
pPhone->wszPhoneInfo,
|
|
lpPhoneCaps->dwPhoneInfoSize
|
|
);
|
|
|
|
lpPhoneCaps->dwNeededSize += dwAlignedSize - dwRealSize;
|
|
|
|
///////////////////
|
|
// Phone Name
|
|
///////////////////
|
|
|
|
// Size of the Phone Name string in bytes
|
|
lpPhoneCaps->dwPhoneNameSize = (lstrlen(pPhone->wszPhoneName)+ 1) *
|
|
sizeof (WCHAR);
|
|
|
|
dwRealSize = lpPhoneCaps->dwPhoneNameSize;
|
|
|
|
// Offset of the Phone Name String from the PHONECAPS structure
|
|
lpPhoneCaps->dwPhoneNameOffset = lpPhoneCaps->dwPhoneInfoOffset +
|
|
dwAlignedSize;
|
|
|
|
// Align it across DWORD boundary
|
|
if (dwRealSize % sizeof(DWORD))
|
|
{
|
|
dwAlignedSize = dwRealSize - (dwRealSize % sizeof(DWORD)) +
|
|
sizeof(DWORD);
|
|
}
|
|
else
|
|
{
|
|
dwAlignedSize = dwRealSize;
|
|
}
|
|
|
|
// Copy the phone name string at the offset specified by
|
|
// lpPhoneCaps->dwPhoneNameOffset
|
|
CopyMemory(
|
|
((LPBYTE)lpPhoneCaps) + lpPhoneCaps->dwPhoneNameOffset,
|
|
pPhone->wszPhoneName,
|
|
lpPhoneCaps->dwPhoneNameSize
|
|
);
|
|
|
|
lpPhoneCaps->dwNeededSize += dwAlignedSize - dwRealSize;
|
|
|
|
////////////////////////////
|
|
// Button Modes & Functions
|
|
////////////////////////////
|
|
|
|
// If the phone has buttons, dial, feature, etc
|
|
if(pPhone->dwNumButtons)
|
|
{
|
|
DWORD i;
|
|
|
|
// The size in bytes of the variably sized field containing the
|
|
// button modes of the phone's buttons, and the offset in bytes
|
|
// from the beginning of this data structure. This member uses the
|
|
// values specified by the PHONEBUTTONMODE_ constants. The
|
|
// array is indexed by button/lamp identifier.
|
|
lpPhoneCaps->dwButtonModesSize = (pPhone->dwNumButtons) *
|
|
sizeof (DWORD);
|
|
lpPhoneCaps->dwButtonModesOffset = lpPhoneCaps->dwPhoneNameOffset +
|
|
dwAlignedSize;
|
|
|
|
//
|
|
// The size in bytes of the variably sized field containing the
|
|
// button modes of the phone's buttons, and the offset in bytes
|
|
// from the beginning of this data structure. This member uses the
|
|
// values specified by the PHONEBUTTONFUNCTION_ constants. The
|
|
// array is indexed by button/lamp identifier.
|
|
//
|
|
lpPhoneCaps->dwButtonFunctionsSize = pPhone->dwNumButtons *
|
|
sizeof (DWORD);
|
|
lpPhoneCaps->dwButtonFunctionsOffset =
|
|
lpPhoneCaps->dwButtonModesOffset +
|
|
lpPhoneCaps->dwButtonModesSize;
|
|
|
|
pButtonInfo = pPhone->pButtonInfo;
|
|
|
|
//
|
|
// For each button on the phone copy the Button Function and Mode
|
|
// at the appropriate position
|
|
//
|
|
for ( i = 0; i < pPhone->dwNumButtons; i++, pButtonInfo++)
|
|
{
|
|
|
|
CopyMemory(
|
|
((LPBYTE)lpPhoneCaps) +
|
|
lpPhoneCaps->dwButtonModesOffset + i*sizeof(DWORD),
|
|
&pButtonInfo->dwButtonMode,
|
|
sizeof (DWORD)
|
|
);
|
|
|
|
CopyMemory(
|
|
((LPBYTE)lpPhoneCaps) +
|
|
lpPhoneCaps->dwButtonFunctionsOffset + i*sizeof(DWORD),
|
|
&pButtonInfo->dwButtonFunction,
|
|
sizeof (DWORD)
|
|
);
|
|
}
|
|
|
|
}
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
|
|
lpPhoneCaps->dwNumGetData = 0;
|
|
lpPhoneCaps->dwNumSetData = 0;
|
|
lpPhoneCaps->dwDevSpecificSize = 0;
|
|
|
|
lpPhoneCaps->dwUsedSize = lpPhoneCaps->dwNeededSize;
|
|
}
|
|
else
|
|
{
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
LOG((PHONESP_ERROR, "TSPI_phoneGetDevCaps - "
|
|
"Not enough memory for Phonecaps [needed %d] [total %d]",
|
|
lpPhoneCaps->dwNeededSize, lpPhoneCaps->dwTotalSize));
|
|
}
|
|
|
|
LOG((PHONESP_TRACE, "TSPI_phoneGetDevCaps - exit"));
|
|
return 0;
|
|
}
|
|
/**************************TSPI_phoneGetDevCaps - end*************************/
|
|
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
TSPI_phoneGetDisplay:
|
|
|
|
This function returns the current contents of the specified phone display.
|
|
|
|
Comments: To be implemented in Tier 2
|
|
******************************************************************************/
|
|
|
|
LONG
|
|
TSPIAPI
|
|
TSPI_phoneGetDisplay(
|
|
HDRVPHONE hdPhone,
|
|
LPVARSTRING lpDisplay
|
|
)
|
|
{
|
|
LOG((PHONESP_TRACE, "TSPI_phoneGetDisplay - enter"));
|
|
LOG((PHONESP_TRACE, "TSPI_phoneGetDisplay - exit"));
|
|
return PHONEERR_OPERATIONUNAVAIL;
|
|
}
|
|
/***********************TSPI_phoneGetDisplay - end****************************/
|
|
|
|
|
|
/******************************************************************************
|
|
TSPI_phoneGetExtensionID:
|
|
|
|
This function retrieves the extension identifier that the TSP supports for
|
|
the indicated phone device.
|
|
|
|
Comments: To be implemented in Tier 2
|
|
******************************************************************************/
|
|
|
|
|
|
LONG
|
|
TSPIAPI
|
|
TSPI_phoneGetExtensionID(
|
|
DWORD dwDeviceID,
|
|
DWORD dwTSPIVersion,
|
|
LPPHONEEXTENSIONID lpExtensionID
|
|
)
|
|
{
|
|
LOG((PHONESP_TRACE, "TSPI_phoneGetExtensionID - enter"));
|
|
LOG((PHONESP_TRACE, "TSPI_phoneGetExtensionID - exit"));
|
|
return 0;
|
|
}
|
|
|
|
/**********************TSPI_phoneGetExtensionID - end*************************/
|
|
|
|
|
|
/******************************************************************************
|
|
TSPI_phoneGetHookSwitch:
|
|
|
|
This function returns the current hookswitch mode of the specified open
|
|
phone device.
|
|
|
|
Arguments:
|
|
HDRVPHONE hdPhone - The handle to the phone
|
|
LPDWORD lpdwHookSwitchDevs - The TSP writes the mode of the phone's
|
|
hookswitch devices. This parameter uses the
|
|
PHONEHOOKSWITCHDEV_ constants. If a bit position is False,
|
|
the corresponding hookswitch device is onhook.
|
|
|
|
Returns LONG:
|
|
Zero is the function succeeded
|
|
else PHONEERR_ constants for error conditions
|
|
|
|
|
|
*******************************************************************************/
|
|
LONG
|
|
TSPIAPI
|
|
TSPI_phoneGetHookSwitch(
|
|
HDRVPHONE hdPhone,
|
|
LPDWORD lpdwHookSwitchDevs
|
|
)
|
|
{
|
|
PPHONESP_PHONE_INFO pPhone;
|
|
LOG((PHONESP_TRACE, "TSPI_phoneGetHookSwitch - enter"));
|
|
|
|
EnterCriticalSection(&csAllPhones);
|
|
|
|
pPhone = (PPHONESP_PHONE_INFO) gpPhone[ (DWORD_PTR) hdPhone ];
|
|
|
|
// check whether the phone handle is valid
|
|
if ( IsBadReadPtr(pPhone,sizeof(PHONESP_PHONE_INFO) ) )
|
|
{
|
|
LeaveCriticalSection(&csAllPhones);
|
|
LOG((PHONESP_ERROR, "TSPI_phoneGetHookSwitch - Invalid Phone Handle"));
|
|
return PHONEERR_INVALPHONEHANDLE;
|
|
}
|
|
|
|
|
|
EnterCriticalSection(&pPhone->csThisPhone);
|
|
LeaveCriticalSection(&csAllPhones);
|
|
|
|
// Check whether the phone handle is still in use
|
|
if ( !pPhone->bAllocated )
|
|
{
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
LOG((PHONESP_ERROR, "TSPI_phoneGetHookSwitch - phone not allocated"));
|
|
return PHONEERR_NODEVICE;
|
|
}
|
|
|
|
// Check whether the phone is open
|
|
if (! (pPhone->bPhoneOpen) )
|
|
{
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
LOG((PHONESP_ERROR, "TSPI_phoneGetHookSwitch - Phone Not Open"));
|
|
return PHONEERR_INVALPHONESTATE;
|
|
}
|
|
|
|
*lpdwHookSwitchDevs = 0;
|
|
|
|
// We are interested in only handset and speaker hookswitch - headset is not
|
|
// supported
|
|
if (pPhone->dwHandset)
|
|
{
|
|
if ( (pPhone->dwHandsetHookSwitchMode != PHONEHOOKSWITCHMODE_ONHOOK) )
|
|
{
|
|
*lpdwHookSwitchDevs = PHONEHOOKSWITCHDEV_HANDSET;
|
|
}
|
|
}
|
|
|
|
if (pPhone->dwSpeaker)
|
|
{
|
|
if( pPhone->dwSpeakerHookSwitchMode != PHONEHOOKSWITCHMODE_ONHOOK)
|
|
{
|
|
*lpdwHookSwitchDevs |= PHONEHOOKSWITCHDEV_SPEAKER;
|
|
}
|
|
}
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
|
|
LOG((PHONESP_TRACE, "TSPI_phoneGetHookSwitch - exit"));
|
|
return 0;
|
|
}
|
|
/************************TSPI_phoneGetHookSwitch - end************************/
|
|
|
|
|
|
/******************************************************************************
|
|
TSPI_phoneGetID:
|
|
|
|
This function returns a device identifier for the given device class
|
|
associated with the specified phone device.
|
|
|
|
Arguments:
|
|
HDRVPHONE hdPhone - The handle to the phone to be queried.
|
|
LPVARSTRING lpDeviceID - Pointer to the data structure of type VARSTRING
|
|
where the device idnetifier is returned.
|
|
LPCWSTR lpszDeviceClass - Specifies the device class of the device whose
|
|
identiifer is requested
|
|
HANDLE hTargetProcess - The process handle of the application on behalf
|
|
of which this function is being invoked.
|
|
|
|
Returns LONG:
|
|
Zero if the function succeeds
|
|
PHONEERR_ constants if an error occurs.
|
|
|
|
Comments: Currently supporting wave/in and wave/out only.
|
|
|
|
******************************************************************************/
|
|
LONG
|
|
TSPIAPI
|
|
TSPI_phoneGetID(
|
|
HDRVPHONE hdPhone,
|
|
LPVARSTRING lpDeviceID,
|
|
LPCWSTR lpszDeviceClass,
|
|
HANDLE hTargetProcess
|
|
)
|
|
{
|
|
PPHONESP_PHONE_INFO pPhone;
|
|
HRESULT hr;
|
|
|
|
LOG((PHONESP_TRACE, "TSPI_phoneGetID - enter"));
|
|
|
|
if (lpDeviceID->dwTotalSize < sizeof(VARSTRING))
|
|
{
|
|
LOG((PHONESP_ERROR, "TSPI_phoneGetID - structure too small"));
|
|
return PHONEERR_STRUCTURETOOSMALL;
|
|
}
|
|
|
|
EnterCriticalSection(&csAllPhones);
|
|
|
|
pPhone = (PPHONESP_PHONE_INFO) gpPhone[ (DWORD_PTR) hdPhone ];
|
|
|
|
// Verify whether the phone handle is valid
|
|
if ( IsBadReadPtr(pPhone, sizeof(PHONESP_PHONE_INFO) ) )
|
|
{
|
|
LeaveCriticalSection(&csAllPhones);
|
|
LOG((PHONESP_ERROR,"TSPI_phoneGetID - Invalid Phone Handle"));
|
|
return PHONEERR_INVALPHONEHANDLE;
|
|
}
|
|
|
|
EnterCriticalSection(&pPhone->csThisPhone);
|
|
LeaveCriticalSection(&csAllPhones);
|
|
|
|
// Check whether the phone handle is still in use
|
|
if ( !pPhone->bAllocated )
|
|
{
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
LOG((PHONESP_ERROR, "TSPI_phoneGetID - phone not allocated"));
|
|
return PHONEERR_NODEVICE;
|
|
}
|
|
|
|
// verify whether the phone is open
|
|
if ( ! pPhone->bPhoneOpen )
|
|
{
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
LOG((PHONESP_ERROR,"TSPI_phoneGetID - Phone not open"));
|
|
return PHONEERR_INVALPHONESTATE;
|
|
}
|
|
|
|
lpDeviceID->dwNeededSize = sizeof(VARSTRING) + sizeof (DWORD);
|
|
|
|
lpDeviceID->dwStringFormat = STRINGFORMAT_BINARY;
|
|
|
|
if ( lpDeviceID->dwTotalSize >= lpDeviceID->dwNeededSize )
|
|
{
|
|
// whether the requested ID is capture class
|
|
if ( ! lstrcmpi(lpszDeviceClass, _T("wave/in") ) )
|
|
{
|
|
LOG((PHONESP_TRACE,"TSPI_phoneGetID - 'wave/in'"));
|
|
|
|
if(pPhone->bCapture == TRUE)
|
|
{
|
|
// Discover Capture Wave ID
|
|
|
|
hr = DiscoverAssociatedWaveId(pPhone->pHidDevice->dwDevInst,
|
|
FALSE,
|
|
&pPhone->dwCaptureWaveId);
|
|
|
|
if (hr != S_OK)
|
|
{
|
|
LOG((PHONESP_ERROR, "TSPI_phoneGetID - "
|
|
"DiscoverAssociatedWaveID failed %0x", hr));
|
|
}
|
|
|
|
lpDeviceID->dwStringOffset = sizeof(VARSTRING);
|
|
lpDeviceID->dwStringSize = sizeof(DWORD);
|
|
|
|
CopyMemory (
|
|
(LPBYTE) lpDeviceID + lpDeviceID->dwStringOffset,
|
|
&pPhone->dwCaptureWaveId,
|
|
sizeof(DWORD)
|
|
);
|
|
}
|
|
else
|
|
{
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
LOG((PHONESP_ERROR,"TSPI_phoneGetID - No Capture Device"));
|
|
return PHONEERR_NODEVICE;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
// the wave ID is render class
|
|
if ( ! lstrcmpi(lpszDeviceClass, _T("wave/out") ) )
|
|
{
|
|
LOG((PHONESP_TRACE,"TSPI_phoneGetID - 'wave/out'"));
|
|
|
|
if(pPhone->bRender == TRUE)
|
|
{
|
|
// Discover Render Wave ID
|
|
|
|
hr = DiscoverAssociatedWaveId(pPhone->pHidDevice->dwDevInst,
|
|
TRUE,
|
|
&pPhone->dwRenderWaveId);
|
|
|
|
if (hr != S_OK)
|
|
{
|
|
LOG((PHONESP_ERROR, "TSPI_phoneGetID - "
|
|
"DiscoverAssociatedWaveID failed %0x", hr));
|
|
}
|
|
|
|
lpDeviceID->dwStringOffset = sizeof(VARSTRING);
|
|
lpDeviceID->dwStringSize = sizeof(DWORD);
|
|
|
|
CopyMemory (
|
|
(LPBYTE) lpDeviceID + lpDeviceID->dwStringOffset,
|
|
&pPhone->dwRenderWaveId,
|
|
sizeof(DWORD)
|
|
);
|
|
}
|
|
else
|
|
{
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
LOG((PHONESP_ERROR,"TSPI_phoneGetID - No Render Device"));
|
|
return PHONEERR_NODEVICE;
|
|
}
|
|
|
|
}
|
|
else
|
|
{ // the other classes are not supported or the phone does not have the
|
|
// specified device
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
LOG((PHONESP_TRACE,"TSPI_phoneGetID - unsupported device class '%ws'", lpszDeviceClass));
|
|
|
|
return PHONEERR_INVALDEVICECLASS;
|
|
}
|
|
}
|
|
lpDeviceID->dwUsedSize = lpDeviceID->dwNeededSize;
|
|
|
|
}
|
|
else
|
|
{
|
|
LOG((PHONESP_ERROR,"TSPI_phoneGetID : not enough total size"));
|
|
lpDeviceID->dwUsedSize = sizeof(VARSTRING);
|
|
}
|
|
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
|
|
|
|
LOG((PHONESP_TRACE, "TSPI_phoneGetID - exit"));
|
|
return 0;
|
|
}
|
|
/************************TSPI_phoneGetID - end*******************************/
|
|
|
|
|
|
/******************************************************************************
|
|
TSPI_phoneGetLamp:
|
|
|
|
This function returns the current lamp mode of the specified lamp.
|
|
|
|
Comments: To be implememted in Tier 2
|
|
|
|
******************************************************************************/
|
|
LONG
|
|
TSPIAPI
|
|
TSPI_phoneGetLamp(
|
|
HDRVPHONE hdPhone,
|
|
DWORD dwButtonLampID,
|
|
LPDWORD lpdwLampMode
|
|
)
|
|
{
|
|
LOG((PHONESP_TRACE, "TSPI_phoneGetLamp - enter"));
|
|
LOG((PHONESP_TRACE, "TSPI_phoneGetLamp - exit"));
|
|
return PHONEERR_OPERATIONUNAVAIL;
|
|
}
|
|
|
|
/********************TSPI_phoneGetLamp - end**********************************/
|
|
|
|
|
|
/******************************************************************************
|
|
TSPI_phoneGetRing:
|
|
|
|
This function enables an application to query the specified open phone
|
|
device as to its current ring mode.
|
|
|
|
Arguments:
|
|
HDRVPHONE hdPhone - The handle to the phone whose ring mode is to be
|
|
queried.
|
|
LPDWORD lpdwRingMode - The ringing pattern with which the phone is
|
|
ringing. Zero indicates that the phone is not ringing.
|
|
LPDWORD lpdwVolume - The volume level with which the phone is ringing.
|
|
This is a number in the range from 0x00000000 (silence)
|
|
through 0x0000FFFF (maximum volume).
|
|
|
|
Returns LONG:
|
|
Zero on Success
|
|
PHONEERR_ constants on error
|
|
|
|
******************************************************************************/
|
|
LONG
|
|
TSPIAPI
|
|
TSPI_phoneGetRing(
|
|
HDRVPHONE hdPhone,
|
|
LPDWORD lpdwRingMode,
|
|
LPDWORD lpdwVolume
|
|
)
|
|
{
|
|
PPHONESP_PHONE_INFO pPhone;
|
|
|
|
LOG((PHONESP_TRACE, "TSPI_phoneGetRing - enter"));
|
|
|
|
EnterCriticalSection(&csAllPhones);
|
|
|
|
pPhone = (PPHONESP_PHONE_INFO) gpPhone[ (DWORD_PTR) hdPhone ];
|
|
// if the phone handle is valid
|
|
if ( IsBadReadPtr(pPhone, sizeof(PHONESP_PHONE_INFO) ) )
|
|
{
|
|
LeaveCriticalSection(&csAllPhones);
|
|
LOG((PHONESP_ERROR, "TSPI_phoneGetRing - Invalid Phone Handle"));
|
|
return PHONEERR_INVALPHONEHANDLE;
|
|
}
|
|
|
|
EnterCriticalSection(&pPhone->csThisPhone);
|
|
LeaveCriticalSection(&csAllPhones);
|
|
|
|
// Check whether the phone handle is still in use
|
|
if ( !pPhone->bAllocated )
|
|
{
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
LOG((PHONESP_ERROR, "TSPI_phoneGetRing - phone not allocated"));
|
|
return PHONEERR_NODEVICE;
|
|
}
|
|
|
|
// whether the phone is open
|
|
if ( ! pPhone->bPhoneOpen )
|
|
{
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
LOG((PHONESP_ERROR, "TSPI_phoneGetRing - Phone Not Open"));
|
|
return PHONEERR_INVALPHONESTATE;
|
|
}
|
|
|
|
// if the phone has a ringer attached to it
|
|
if( ! pPhone->dwRing)
|
|
{
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
LOG((PHONESP_ERROR, "TSPI_phoneGetRing - "
|
|
"Phone does not have a ringer"));
|
|
return PHONEERR_RESOURCEUNAVAIL;
|
|
}
|
|
|
|
*lpdwRingMode = pPhone->dwRingMode;
|
|
|
|
// if ringmode is 0, it indicates that the phone is not ringing
|
|
if(pPhone->dwRingMode)
|
|
{
|
|
// The ring volume is maximum if the phone is ringing
|
|
*lpdwVolume = 0x0000FFFF;
|
|
}
|
|
else
|
|
{
|
|
// If the phone is not ringing the ring volume is 0
|
|
*lpdwVolume = 0;
|
|
}
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
|
|
LOG((PHONESP_TRACE, "TSPI_phoneGetRing - exit"));
|
|
return 0;
|
|
}
|
|
|
|
/******************************TSPI_phoneGetRing - end************************/
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
TSPI_phoneGetStatus:
|
|
|
|
This function queries the specified open phone device for its overall
|
|
status.
|
|
|
|
Arguments:
|
|
|
|
hdPhone - The handle to the phone to be queried.
|
|
lpPhoneStatus - A pointer to a variably sized data structure of type
|
|
PHONESTATUS, into which the TSP writes information about the
|
|
phone's status. Prior to calling TSPI_phoneGetStatus, the
|
|
application sets the dwTotalSize member of this structure to
|
|
indicate the amount of memory available to TAPI for returning
|
|
information.
|
|
|
|
Returns LONG:
|
|
|
|
Zero if the function succeeds, or
|
|
An error number if an error occurs. Possible return values are as follows:
|
|
PHONEERR_INVALPHONEHANDLE.
|
|
******************************************************************************/
|
|
|
|
LONG
|
|
TSPIAPI
|
|
TSPI_phoneGetStatus(
|
|
HDRVPHONE hdPhone,
|
|
LPPHONESTATUS lpPhoneStatus
|
|
)
|
|
{
|
|
PPHONESP_PHONE_INFO pPhone;
|
|
|
|
LOG((PHONESP_TRACE, "TSPI_phoneGetStatus - enter"));
|
|
|
|
if (lpPhoneStatus->dwTotalSize < sizeof(PHONESTATUS))
|
|
{
|
|
LOG((PHONESP_ERROR, "TSPI_phoneGetStatus - structure too small"));
|
|
return PHONEERR_STRUCTURETOOSMALL;
|
|
}
|
|
|
|
EnterCriticalSection(&csAllPhones);
|
|
|
|
pPhone = (PPHONESP_PHONE_INFO) gpPhone[ (DWORD_PTR) hdPhone ];
|
|
|
|
// check whether the phone handle is valid
|
|
if ( IsBadReadPtr(pPhone, sizeof(PHONESP_PHONE_INFO) ) )
|
|
{
|
|
LeaveCriticalSection(&csAllPhones);
|
|
LOG((PHONESP_TRACE,"TSPI_phoneGetStatus - INVALID PHONE HANDLE"));
|
|
return PHONEERR_INVALPHONEHANDLE;
|
|
}
|
|
|
|
EnterCriticalSection(&pPhone->csThisPhone);
|
|
LeaveCriticalSection(&csAllPhones);
|
|
|
|
// Check whether the phone handle is still in use
|
|
if ( !pPhone->bAllocated )
|
|
{
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
LOG((PHONESP_ERROR, "TSPI_phoneGetStatus - phone not allocated"));
|
|
return PHONEERR_NODEVICE;
|
|
}
|
|
|
|
if( ! pPhone->bPhoneOpen)
|
|
{
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
LOG((PHONESP_TRACE,"TSPI_phoneGetStatus - PHONE not Open"));
|
|
return PHONEERR_INVALPHONEHANDLE;
|
|
}
|
|
|
|
lpPhoneStatus->dwNeededSize = sizeof(PHONESTATUS);
|
|
|
|
if(lpPhoneStatus->dwTotalSize >= lpPhoneStatus->dwNeededSize)
|
|
{
|
|
lpPhoneStatus->dwUsedSize = sizeof (PHONESTATUS);
|
|
lpPhoneStatus->dwStatusFlags = PHONESTATUSFLAGS_CONNECTED;
|
|
|
|
// If the phone has a ringer
|
|
if(pPhone->dwRing)
|
|
{
|
|
lpPhoneStatus->dwRingMode = pPhone->dwRingMode;
|
|
// If the Ring Mode is 0, the phone is not ringing
|
|
if (pPhone->dwRingMode)
|
|
{
|
|
// by default the phone volume is 0xffff if it is ringing
|
|
lpPhoneStatus->dwRingVolume = 0xffff;
|
|
}
|
|
else
|
|
{
|
|
// the phone volume is 0 if not ringing
|
|
lpPhoneStatus->dwRingVolume = 0;
|
|
}
|
|
}
|
|
|
|
lpPhoneStatus->dwHandsetHookSwitchMode = pPhone->dwHandsetHookSwitchMode;
|
|
lpPhoneStatus->dwHandsetVolume = 0;
|
|
lpPhoneStatus->dwHandsetGain = 0;
|
|
|
|
if (pPhone->dwSpeaker)
|
|
{
|
|
lpPhoneStatus->dwSpeakerHookSwitchMode = pPhone->dwSpeakerHookSwitchMode;
|
|
lpPhoneStatus->dwSpeakerVolume = 0;
|
|
lpPhoneStatus->dwSpeakerGain = 0;
|
|
}
|
|
}
|
|
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
|
|
LOG((PHONESP_TRACE, "TSPI_phoneGetStatus - exit"));
|
|
return 0;
|
|
}
|
|
/****************************TSPI_phoneGetStatus - end************************/
|
|
|
|
/******************************************************************************
|
|
TSPI_phoneNegotiateTSPIVersion:
|
|
|
|
This function returns the highest SPI version the TSP can operate under for
|
|
this device, given the range of possible SPI versions.
|
|
|
|
Arguments:
|
|
|
|
Return LONG:
|
|
|
|
******************************************************************************/
|
|
LONG
|
|
TSPIAPI
|
|
TSPI_phoneNegotiateTSPIVersion(
|
|
DWORD dwDeviceID,
|
|
DWORD dwLowVersion,
|
|
DWORD dwHighVersion,
|
|
LPDWORD lpdwTSPIVersion
|
|
)
|
|
{
|
|
PPHONESP_PHONE_INFO pPhone;
|
|
|
|
LOG((PHONESP_TRACE, "TSPI_phoneNegotiateTSPIVersion - enter"));
|
|
|
|
if (dwHighVersion >= HIGH_VERSION)
|
|
{
|
|
if (dwLowVersion <= HIGH_VERSION)
|
|
{
|
|
*lpdwTSPIVersion = (DWORD) HIGH_VERSION;
|
|
}
|
|
else
|
|
{ // the app is too new for us
|
|
return PHONEERR_INCOMPATIBLEAPIVERSION;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(dwHighVersion >= LOW_VERSION)
|
|
{
|
|
*lpdwTSPIVersion = dwHighVersion;
|
|
}
|
|
else
|
|
{
|
|
//we are too new for the app
|
|
return PHONEERR_INCOMPATIBLEAPIVERSION;
|
|
}
|
|
}
|
|
|
|
EnterCriticalSection(&csAllPhones);
|
|
|
|
// Given the deviceID retrieve the structure that contains the information
|
|
// for this device
|
|
pPhone = GetPhoneFromID(dwDeviceID, NULL);
|
|
|
|
if ( ! pPhone)
|
|
{
|
|
LeaveCriticalSection(&csAllPhones);
|
|
LOG((PHONESP_ERROR,"TSPI_phoneNegotiateTSPIVersion - Bad Device ID"));
|
|
return PHONEERR_BADDEVICEID;
|
|
}
|
|
|
|
EnterCriticalSection(&pPhone->csThisPhone);
|
|
LeaveCriticalSection(&csAllPhones);
|
|
|
|
// Check whether the phone handle is still in use
|
|
if ( !pPhone->bAllocated )
|
|
{
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
LOG((PHONESP_ERROR, "TSPI_phoneNegotiateTSPIVersion - phone not allocated"));
|
|
return PHONEERR_NODEVICE;
|
|
}
|
|
|
|
// Store the version negotiated for this phone
|
|
pPhone->dwVersion = *lpdwTSPIVersion;
|
|
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
|
|
LOG((PHONESP_TRACE, "TSPI_phoneNegotiateTSPIVersion - exit"));
|
|
return 0;
|
|
}
|
|
/**********************TSPI_phoneNegotiateTSPIVersion - end*******************/
|
|
|
|
|
|
/******************************************************************************
|
|
TSPI_phoneOpen:
|
|
|
|
This function opens the phone device whose device identifier is given,
|
|
returning the TSP's opaque handle for the device and retaining TAPI's
|
|
opaque handle for the device for use in subsequent calls to the PHONEEVENT
|
|
procedure.
|
|
|
|
Arguments:
|
|
|
|
Returns:
|
|
******************************************************************************/
|
|
|
|
LONG
|
|
TSPIAPI
|
|
TSPI_phoneOpen(
|
|
DWORD dwDeviceID,
|
|
HTAPIPHONE htPhone,
|
|
LPHDRVPHONE lphdPhone,
|
|
DWORD dwTSPIVersion,
|
|
PHONEEVENT lpfnEventProc
|
|
)
|
|
{
|
|
LPPHONEBUTTONINFO lpButtonInfo;
|
|
DWORD dwPhoneID;
|
|
PPHONESP_PHONE_INFO pPhone;
|
|
|
|
LOG((PHONESP_TRACE, "TSPI_phoneOpen - enter"));
|
|
|
|
EnterCriticalSection(&csAllPhones);
|
|
|
|
// if the device id is not valid return error condition
|
|
if ( ! ( pPhone = GetPhoneFromID(dwDeviceID, &dwPhoneID) ) )
|
|
{
|
|
LeaveCriticalSection(&csAllPhones);
|
|
LOG((PHONESP_ERROR,"TSPI_phoneOpen - Invalid Phone Handle"));
|
|
return PHONEERR_BADDEVICEID;
|
|
}
|
|
|
|
EnterCriticalSection(&pPhone->csThisPhone);
|
|
LeaveCriticalSection(&csAllPhones);
|
|
|
|
// Check whether the phone handle is still in use
|
|
if ( !pPhone->bAllocated )
|
|
{
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
LOG((PHONESP_ERROR, "TSPI_phoneOpen - phone not allocated"));
|
|
return PHONEERR_NODEVICE;
|
|
}
|
|
|
|
// if the phone is already open then return error condition
|
|
if (pPhone->bPhoneOpen)
|
|
{
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
LOG((PHONESP_ERROR,"TSPI_phoneOpen - Phone is open"));
|
|
return PHONEERR_INUSE;
|
|
}
|
|
|
|
// Create an event that signals the receipt of an input report from
|
|
// the phone device
|
|
if ( ! ( pPhone->hInputReportEvent =
|
|
CreateEvent ((LPSECURITY_ATTRIBUTES) NULL,
|
|
FALSE, // manual reset
|
|
FALSE, // non-signaled
|
|
NULL // unnamed
|
|
) ) )
|
|
{
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
|
|
LOG((PHONESP_ERROR,"TSPI_phoneOpen - Create Event: hInputReportEvent"
|
|
" Failed: %d", GetLastError()));
|
|
return PHONEERR_NOMEM;
|
|
}
|
|
|
|
// Create an event that we will signal when we close the phone to
|
|
// allow the read thread to exit
|
|
if ( ! ( pPhone->hCloseEvent =
|
|
CreateEvent ((LPSECURITY_ATTRIBUTES) NULL,
|
|
FALSE, // manual reset
|
|
FALSE, // non-signaled
|
|
NULL // unnamed
|
|
) ) )
|
|
{
|
|
CloseHandle(pPhone->hInputReportEvent);
|
|
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
|
|
LOG((PHONESP_ERROR,"TSPI_phoneOpen - Create Event: hWaitCompletionEvent"
|
|
" Failed: %d", GetLastError()));
|
|
return PHONEERR_NOMEM;
|
|
}
|
|
|
|
//
|
|
// The overlapped structure contains the event to be set when an input
|
|
// report is received. The event to be set is the hInputReportEvent
|
|
// which is part of the PHONESP_PHONE_INFO structure. This overlapped
|
|
// structure is passed to the ReadFile function call.
|
|
//
|
|
if( ! ( pPhone->lpOverlapped = (LPOVERLAPPED)
|
|
MemAlloc (sizeof(OVERLAPPED)) ))
|
|
{
|
|
CloseHandle(pPhone->hCloseEvent);
|
|
CloseHandle(pPhone->hInputReportEvent);
|
|
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
|
|
LOG((PHONESP_ERROR,"TSPI_phoneOpen - Not enough memory for"
|
|
" lpOverlapped structure "));
|
|
|
|
return PHONEERR_NOMEM;
|
|
}
|
|
pPhone->lpOverlapped->Offset = 0;
|
|
pPhone->lpOverlapped->OffsetHigh = 0;
|
|
pPhone->lpOverlapped->hEvent = pPhone->hInputReportEvent;
|
|
|
|
//
|
|
// Open the HID file handle
|
|
//
|
|
if ( ! OpenHidFile(pPhone->pHidDevice) )
|
|
{
|
|
MemFree(pPhone->lpOverlapped);
|
|
CloseHandle(pPhone->hCloseEvent);
|
|
CloseHandle(pPhone->hInputReportEvent);
|
|
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
|
|
LOG((PHONESP_ERROR,"TSPI_phoneOpen - HidOpenFile failed"));
|
|
|
|
return PHONEERR_OPERATIONFAILED;
|
|
}
|
|
|
|
|
|
// Increase the number of packets that the HID class driver ring buffer
|
|
// holds for the device
|
|
if ( ! HidD_SetNumInputBuffers(pPhone->pHidDevice->HidDevice,
|
|
20) )
|
|
{
|
|
CloseHidFile(pPhone->pHidDevice);
|
|
MemFree(pPhone->lpOverlapped);
|
|
CloseHandle(pPhone->hCloseEvent);
|
|
CloseHandle(pPhone->hInputReportEvent);
|
|
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
|
|
LOG((PHONESP_ERROR,"TSPI_phoneOpen - HidD_SetNumInputBuffers"
|
|
" Failed: %d", GetLastError()));
|
|
|
|
return PHONEERR_OPERATIONFAILED;
|
|
}
|
|
|
|
//
|
|
// Start a thread for waiting for input reports from the device. We
|
|
// cannot use the thread pool for this because we will need to cancel
|
|
// pending reads if we want to close the device.
|
|
//
|
|
if ( ! ( pPhone->hReadThread =
|
|
CreateThread ((LPSECURITY_ATTRIBUTES) NULL,
|
|
0,
|
|
(LPTHREAD_START_ROUTINE) ReadThread,
|
|
pPhone,
|
|
0,
|
|
NULL
|
|
) ) )
|
|
{
|
|
CloseHidFile(pPhone->pHidDevice);
|
|
MemFree(pPhone->lpOverlapped);
|
|
CloseHandle(pPhone->hCloseEvent);
|
|
CloseHandle(pPhone->hInputReportEvent);
|
|
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
|
|
LOG((PHONESP_ERROR,"TSPI_phoneOpen - Create Thread: hReadThread"
|
|
" Failed: %d", GetLastError()));
|
|
return PHONEERR_NOMEM;
|
|
}
|
|
|
|
//
|
|
// Set phone open
|
|
//
|
|
pPhone->bPhoneOpen = TRUE;
|
|
pPhone->htPhone = htPhone;
|
|
pPhone->lpfnPhoneEventProc = lpfnEventProc;
|
|
|
|
*lphdPhone = (HDRVPHONE)IntToPtr(dwPhoneID);
|
|
|
|
//
|
|
// Update values for phone features (such as hookswitch state)
|
|
//
|
|
UpdatePhoneFeatures( pPhone );
|
|
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
|
|
LOG((PHONESP_TRACE, "TSPI_phoneOpen - exit"));
|
|
return 0;
|
|
}
|
|
/********************TSPI_phoneOpen - end*************************************/
|
|
|
|
|
|
/******************************************************************************
|
|
TSPI_phoneSelectExtVersion:
|
|
|
|
This function selects the indicated extension version for the indicated
|
|
phone device. Subsequent requests operate according to that extension
|
|
version.
|
|
|
|
Comments: To be implemented in Tier 2
|
|
|
|
******************************************************************************/
|
|
LONG
|
|
TSPIAPI
|
|
TSPI_phoneSelectExtVersion(
|
|
HDRVPHONE hdPhone,
|
|
DWORD dwExtVersion
|
|
)
|
|
{
|
|
LOG((PHONESP_TRACE, "TSPI_phoneSelectExtVersion- enter"));
|
|
LOG((PHONESP_TRACE, "TSPI_phoneSelectExtVersion - exit"));
|
|
return PHONEERR_OPERATIONUNAVAIL;
|
|
}
|
|
/****************************TSPI_phoneSelectExtVersion - end*****************/
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
TSPI_phoneSetDisplay:
|
|
|
|
This function causes the specified string to be displayed on the specified
|
|
open phone device.
|
|
|
|
Comments: To be implemented in Tier 2
|
|
******************************************************************************/
|
|
LONG
|
|
TSPIAPI
|
|
TSPI_phoneSetDisplay(
|
|
DRV_REQUESTID dwRequestID,
|
|
HDRVPHONE hdPhone,
|
|
DWORD dwRow,
|
|
DWORD dwColumn,
|
|
LPCWSTR lpsDisplay,
|
|
DWORD dwSize
|
|
)
|
|
{
|
|
LOG((PHONESP_TRACE, "TSPI_phoneSetDisplay - enter"));
|
|
LOG((PHONESP_TRACE, "TSPI_phoneSetDisplay - exit"));
|
|
return PHONEERR_OPERATIONUNAVAIL;
|
|
}
|
|
|
|
/****************************TSPI_phoneSetDisplay - end***********************/
|
|
|
|
/******************************************************************************
|
|
TSPI_phoneSetHookSwitch_AsyncProc:
|
|
|
|
This function sets the hook state of the specified open phone's hookswitch
|
|
devices to the specified mode. Only the hookswitch state of the hookswitch
|
|
devices listed is affected.
|
|
|
|
Arguments:
|
|
PMYFUNC_INFO pAsyncFuncInfo - The parameters passed to this function
|
|
Param1 - Pointer to the phone structure
|
|
Param2 - dwRequestID which is needed while calling
|
|
ASYNC_COMPLETION to inform TAPI about the result
|
|
of the operation. This was passed by tapi when
|
|
calling TSPI_phoneSetHookSwitch
|
|
Param3 - PHONEHOOKSWITCHDEV_ constant. Currently only
|
|
_SPEAKER is supported.
|
|
Param4 - The HookSwitchMode that has to be set for
|
|
the HookSwitch. This again is supplied by TAPI
|
|
Currently only PHONEHOOKSWITCHMODE_ONHOOK and
|
|
_MICSPEAKER is supported.
|
|
RETURNS VOID:
|
|
|
|
******************************************************************************/
|
|
|
|
VOID
|
|
CALLBACK
|
|
TSPI_phoneSetHookSwitch_AsyncProc(
|
|
PPHONESP_FUNC_INFO pAsyncFuncInfo
|
|
)
|
|
{
|
|
PPHONESP_PHONE_INFO pPhone;
|
|
LONG lResult = 0;
|
|
|
|
LOG((PHONESP_TRACE, "TSPI_phoneSetHookSwitch_AsyncProc - enter"));
|
|
|
|
EnterCriticalSection(&csAllPhones);
|
|
|
|
pPhone = (PPHONESP_PHONE_INFO)pAsyncFuncInfo->dwParam1;
|
|
|
|
// if the phone is not open
|
|
if( IsBadReadPtr(pPhone, sizeof(PHONESP_PHONE_INFO)) ||
|
|
( ! pPhone->bAllocated) ||
|
|
( ! pPhone->bPhoneOpen) ||
|
|
( ! pPhone->pHidDevice) )
|
|
{
|
|
// This case may never arise since phone close waits for all
|
|
// asynchornous opreations on the phone to complete before closing the
|
|
// phone
|
|
LONG lResult = PHONEERR_INVALPHONEHANDLE;
|
|
|
|
LeaveCriticalSection(&csAllPhones);
|
|
// Notify TAPISRV about the error condition
|
|
(*(glpfnCompletionProc))(
|
|
(DRV_REQUESTID) pAsyncFuncInfo->dwParam2,
|
|
lResult
|
|
);
|
|
LOG((PHONESP_ERROR, "TSPI_phoneSetHookSwitch_AsyncProc - Invalid Phone"
|
|
" Handle"));
|
|
}
|
|
else
|
|
{
|
|
EnterCriticalSection(&pPhone->csThisPhone);
|
|
LeaveCriticalSection(&csAllPhones);
|
|
|
|
switch (pAsyncFuncInfo->dwParam4)
|
|
{
|
|
case PHONEHOOKSWITCHMODE_ONHOOK:
|
|
if ( pPhone->dwSpeakerHookSwitchMode != PHONEHOOKSWITCHMODE_ONHOOK )
|
|
{
|
|
//Inform tapi about the change in state of the hookswitch
|
|
SendPhoneEvent(
|
|
pPhone,
|
|
PHONE_STATE,
|
|
PHONESTATE_SPEAKERHOOKSWITCH,
|
|
PHONEHOOKSWITCHMODE_ONHOOK,
|
|
(DWORD) 0
|
|
);
|
|
|
|
pPhone->dwSpeakerHookSwitchMode = PHONEHOOKSWITCHMODE_ONHOOK;
|
|
}
|
|
lResult = ERROR_SUCCESS;
|
|
break;
|
|
|
|
case PHONEHOOKSWITCHMODE_MICSPEAKER:
|
|
if ( pPhone->dwSpeakerHookSwitchMode != PHONEHOOKSWITCHMODE_MICSPEAKER )
|
|
{
|
|
//Inform tapi about the change in state of the hookswitch
|
|
SendPhoneEvent(
|
|
pPhone,
|
|
PHONE_STATE,
|
|
PHONESTATE_SPEAKERHOOKSWITCH,
|
|
PHONEHOOKSWITCHMODE_MICSPEAKER,
|
|
(DWORD) 0
|
|
);
|
|
|
|
pPhone->dwSpeakerHookSwitchMode = PHONEHOOKSWITCHMODE_MICSPEAKER;
|
|
}
|
|
lResult = ERROR_SUCCESS;
|
|
break;
|
|
|
|
default:
|
|
lResult = PHONEERR_RESOURCEUNAVAIL;
|
|
break;
|
|
}
|
|
|
|
// Send the result of the operation to TAPI
|
|
(*(glpfnCompletionProc))(
|
|
(DRV_REQUESTID) pAsyncFuncInfo->dwParam2,
|
|
lResult // Result of the operation
|
|
);
|
|
|
|
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
}
|
|
|
|
LOG((PHONESP_TRACE, "TSPI_phoneSetHookSwitch_AsyncProc - exit"));
|
|
}
|
|
|
|
/******************************************************************************
|
|
TSPI_phoneSetHookSwitch:
|
|
|
|
This function sets the hook state of the specified open phone's hookswitch
|
|
devices to the specified mode. Only the hookswitch state of the hookswitch
|
|
devices listed is affected.
|
|
|
|
Arguments:
|
|
dwRequestID - The identifier of the asynchronous request.
|
|
hdPhone - The handle to the phone containing the hookswitch
|
|
devices whose modes are to be set.
|
|
dwHookSwitchDevs - The device(s) whose hookswitch mode is to be set.
|
|
This parameter uses the following PHONEHOOKSWITCHDEV_
|
|
constants: PHONEHOOKSWITCHDEV_HANDSET,
|
|
PHONEHOOKSWITCHDEV_SPEAKER, PHONEHOOKSWITCHDEV_HEADSET
|
|
dwHookSwitchMode - The hookswitch mode to set. This parameter can have
|
|
only one of the following PHONEHOOKSWITCHMODE_ bits
|
|
set: PHONEHOOKSWITCHMODE_ONHOOK, _MIC, _SPEAKER,
|
|
_MICSPEAKER
|
|
|
|
Return LONG:
|
|
Returns dwRequestID or an error number if an error occurs.
|
|
The lResult actual parameter of the corresponding ASYNC_COMPLETION is
|
|
zero if the function succeeds or it is an error number if an error
|
|
occurs. Possible return values are as follows:
|
|
PHONEERR_INVALPHONEHANDLE, PHONEERR_RESOURCEUNAVAIL,
|
|
PHONEERR_INVALHOOKSWITCHMODE,
|
|
|
|
Remarks
|
|
A PHONE_STATE message is sent to the application after the hookswitch
|
|
state has changed.
|
|
|
|
******************************************************************************/
|
|
LONG
|
|
TSPIAPI
|
|
TSPI_phoneSetHookSwitch(
|
|
DRV_REQUESTID dwRequestID,
|
|
HDRVPHONE hdPhone,
|
|
DWORD dwHookSwitchDevs,
|
|
DWORD dwHookSwitchMode
|
|
)
|
|
{
|
|
PPHONESP_PHONE_INFO pPhone;
|
|
|
|
//
|
|
// Since only mode should be selected. We are making sure that only one
|
|
// mode is selected at a time..
|
|
//
|
|
BOOL ONHOOK = ~(dwHookSwitchMode ^ PHONEHOOKSWITCHMODE_ONHOOK),
|
|
MIC = ~(dwHookSwitchMode ^ PHONEHOOKSWITCHMODE_MIC),
|
|
SPEAKER = ~(dwHookSwitchMode ^ PHONEHOOKSWITCHMODE_SPEAKER),
|
|
MICSPEAKER = ~(dwHookSwitchMode ^ PHONEHOOKSWITCHMODE_MICSPEAKER);
|
|
|
|
PPHONESP_ASYNC_REQ_INFO pAsyncReqInfo;
|
|
PPHONESP_FUNC_INFO pFuncInfo;
|
|
|
|
LOG((PHONESP_TRACE, "TSPI_phoneSetHookSwitch - enter"));
|
|
|
|
EnterCriticalSection(&csAllPhones);
|
|
|
|
pPhone = (PPHONESP_PHONE_INFO) gpPhone[ (DWORD_PTR) hdPhone ];
|
|
|
|
// if the phone handle is valid and the phone is open
|
|
if( IsBadReadPtr(pPhone, sizeof(PHONESP_PHONE_INFO) ) ||
|
|
(! pPhone->bPhoneOpen) )
|
|
{
|
|
LeaveCriticalSection(&csAllPhones);
|
|
return PHONEERR_INVALPHONEHANDLE;
|
|
}
|
|
|
|
EnterCriticalSection(&pPhone->csThisPhone);
|
|
LeaveCriticalSection(&csAllPhones);
|
|
|
|
// Check whether the phone handle is still in use
|
|
if ( !pPhone->bAllocated )
|
|
{
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
LOG((PHONESP_ERROR, "TSPI_phoneSetHookSwitch - phone not allocated"));
|
|
return PHONEERR_NODEVICE;
|
|
}
|
|
|
|
//
|
|
// Only the speaker phone can be set, the other hookswitch types are error
|
|
// conditions
|
|
//
|
|
if( ! (dwHookSwitchDevs & PHONEHOOKSWITCHDEV_SPEAKER) )
|
|
{
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
LOG((PHONESP_ERROR, "TSPI_phoneSetHookSwitch - only speaker hookswitch is supported"));
|
|
return PHONEERR_RESOURCEUNAVAIL;
|
|
}
|
|
|
|
LOG((PHONESP_TRACE, "PHONEHOOKSWITCHDEV_SPEAKER"));
|
|
|
|
//
|
|
// Make sure the phone supports a speakerphone
|
|
//
|
|
if ( ! ( pPhone->dwSpeaker ) )
|
|
{
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
LOG((PHONESP_ERROR, "No speaker"));
|
|
return PHONEERR_RESOURCEUNAVAIL;
|
|
}
|
|
|
|
// Inorder to confirm that one mode is set
|
|
if( ! ( ONHOOK | MIC | SPEAKER| MICSPEAKER ) )
|
|
{
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
LOG((PHONESP_ERROR, "Mulitple modes set for the speaker"));
|
|
return PHONEERR_INVALHOOKSWITCHMODE;
|
|
}
|
|
|
|
// Build the structure for queueing the request in the Async queue
|
|
if( ! (pFuncInfo = (PPHONESP_FUNC_INFO)
|
|
MemAlloc( sizeof (PHONESP_FUNC_INFO)) ) )
|
|
{
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
return PHONEERR_NOMEM;
|
|
}
|
|
|
|
pFuncInfo->dwParam1 = (ULONG_PTR) pPhone;
|
|
|
|
pFuncInfo->dwParam2 = dwRequestID;
|
|
|
|
pFuncInfo->dwParam3 = (ULONG_PTR) PHONEHOOKSWITCHDEV_SPEAKER;
|
|
pFuncInfo->dwParam4 = (ULONG_PTR) dwHookSwitchMode;
|
|
pFuncInfo->dwNumParams = 4;
|
|
|
|
if ( ! ( pAsyncReqInfo = (PPHONESP_ASYNC_REQ_INFO)
|
|
MemAlloc(sizeof (PHONESP_ASYNC_REQ_INFO)) ) )
|
|
{
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
MemFree(pFuncInfo);
|
|
return PHONEERR_NOMEM;
|
|
}
|
|
|
|
pAsyncReqInfo->pfnAsyncProc = TSPI_phoneSetHookSwitch_AsyncProc;
|
|
pAsyncReqInfo->pFuncInfo = pFuncInfo;
|
|
|
|
//
|
|
// if Queue the request to perform asynchronously fails then we need to
|
|
// decrement the counter of number of pending requests on the phone
|
|
//
|
|
if( AsyncRequestQueueIn(pAsyncReqInfo) )
|
|
{
|
|
// Reset the event for number of pending requests in the queue for this
|
|
// phone and increment the counter
|
|
if (pPhone->dwNumPendingReqInQueue == 0)
|
|
{
|
|
ResetEvent(pPhone->hNoPendingReqInQueueEvent);
|
|
}
|
|
pPhone->dwNumPendingReqInQueue++;
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
}
|
|
else
|
|
{
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
MemFree(pAsyncReqInfo);
|
|
MemFree(pFuncInfo);
|
|
// maybe need to free the request memory
|
|
return PHONEERR_NOMEM;
|
|
}
|
|
|
|
|
|
LOG((PHONESP_TRACE, "TSPI_phoneSetHookSwitch - exit"));
|
|
return dwRequestID;
|
|
}
|
|
/*******************TSPI_phoneSetHookSwitch - end****************************/
|
|
|
|
|
|
/*****************************************************************************
|
|
TSPI_phoneSetLamp:
|
|
|
|
This function causes the specified lamp to be set on the specified open
|
|
phone device in the specified lamp mode.
|
|
|
|
Comments: To be implemented in Tier 2
|
|
******************************************************************************/
|
|
LONG
|
|
TSPIAPI
|
|
TSPI_phoneSetLamp(
|
|
DRV_REQUESTID dwRequestID,
|
|
HDRVPHONE hdPhone,
|
|
DWORD dwButtonLampID,
|
|
DWORD dwLampMode
|
|
)
|
|
{
|
|
LOG((PHONESP_TRACE, "TSPI_phoneSetLamp - enter"));
|
|
LOG((PHONESP_TRACE, "TSPI_phoneSetLamp - exit"));
|
|
return PHONEERR_OPERATIONUNAVAIL;
|
|
}
|
|
/****************************TSPI_phoneSetLamp - end**************************/
|
|
|
|
/******************************************************************************
|
|
TSPI_phoneSetRing_AsyncProc:
|
|
|
|
Arguments:
|
|
|
|
Returns:
|
|
|
|
Comments: To be implemented. currently there is no corresponding usage in
|
|
Hid hence no output report is sent.
|
|
******************************************************************************/
|
|
VOID
|
|
CALLBACK
|
|
TSPI_phoneSetRing_AsyncProc(
|
|
PPHONESP_FUNC_INFO pAsyncFuncInfo
|
|
)
|
|
{
|
|
PPHONESP_PHONE_INFO pPhone;
|
|
LONG lResult = 0;
|
|
|
|
LOG((PHONESP_TRACE,"TSPI_phoneSetRing_AsyncProc - enter"));
|
|
|
|
EnterCriticalSection(&csAllPhones);
|
|
|
|
pPhone = (PPHONESP_PHONE_INFO)pAsyncFuncInfo->dwParam1;
|
|
|
|
// if the phone is not open
|
|
if( IsBadReadPtr(pPhone, sizeof(PHONESP_PHONE_INFO)) ||
|
|
( ! pPhone->bPhoneOpen) ||
|
|
( ! pPhone->bAllocated) ||
|
|
( ! pPhone->pHidDevice) )
|
|
{
|
|
// This case may never arise since phone close waits for all
|
|
// asynchornous opreations on the phone to complete before closing the
|
|
// phone
|
|
LONG lResult = PHONEERR_INVALPHONEHANDLE;
|
|
|
|
LeaveCriticalSection(&csAllPhones);
|
|
// Notify TAPISRV about the error condition
|
|
(*(glpfnCompletionProc))(
|
|
(DRV_REQUESTID) pAsyncFuncInfo->dwParam2,
|
|
lResult
|
|
);
|
|
LOG((PHONESP_ERROR, "TSPI_phoneSetRing_AsyncProc - Invalid Phone"
|
|
" Handle"));
|
|
}
|
|
else
|
|
{
|
|
EnterCriticalSection(&pPhone->csThisPhone);
|
|
LeaveCriticalSection(&csAllPhones);
|
|
|
|
|
|
lResult = SendOutputReport(
|
|
pPhone->pHidDevice,
|
|
HID_USAGE_TELEPHONY_RINGER,
|
|
((pAsyncFuncInfo->dwParam3 == 0) ? FALSE : TRUE)
|
|
);
|
|
|
|
if(lResult == ERROR_SUCCESS)
|
|
{
|
|
lResult = 0;
|
|
|
|
pPhone->dwRingMode = (DWORD)pAsyncFuncInfo->dwParam3;
|
|
|
|
//Inform tapi about the change in state of the hookswitch
|
|
SendPhoneEvent(
|
|
pPhone,
|
|
PHONE_STATE,
|
|
PHONESTATE_RINGMODE,
|
|
(DWORD) pAsyncFuncInfo->dwParam3,
|
|
(DWORD) pAsyncFuncInfo->dwParam4
|
|
);
|
|
}
|
|
else
|
|
{
|
|
LOG((PHONESP_ERROR, "TSPI_phoneSetHookSwitch_AsyncProc - "
|
|
"SendOutputReport Failed"));
|
|
lResult = PHONEERR_RESOURCEUNAVAIL;
|
|
}
|
|
|
|
// Send the result of the operation to TAPI
|
|
(*(glpfnCompletionProc))(
|
|
(DRV_REQUESTID) pAsyncFuncInfo->dwParam2,
|
|
lResult // Result of the operation
|
|
);
|
|
|
|
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
}
|
|
|
|
|
|
LOG((PHONESP_TRACE,"TSPI_phoneSetRing_AsyncProc - exit"));
|
|
}
|
|
/*******************TSPI_phoneSetRing_AsyncProc - end*************************/
|
|
|
|
/******************************************************************************
|
|
TSPI_phoneSetRing:
|
|
|
|
This function rings the specified open phone device using the specified
|
|
ring mode and volume.
|
|
|
|
Arguments:
|
|
DRV_REQUESTID dwRequestID - Identifier of the asynchronous request.
|
|
HDRVPHONE hdPhone - Handle to the phone to be rung.
|
|
DWORD dwRingMode - The ringing pattern with which to ring the phone.
|
|
This parameter must be within the range from zero
|
|
through the value of the dwNumRingModes member in the
|
|
PHONECAPS structure. If dwNumRingModes is zero, the
|
|
ring mode of the phone cannot be controlled; if
|
|
dwNumRingModes is 1, a value of 0 for dwRingMode
|
|
indicates that the phone should not be rung (silence),
|
|
and other values from 1 through dwNumRingModes are
|
|
valid ring modes for the phone device.
|
|
DWORD dwVolume - The volume level with which the phone is to be rung.
|
|
This is a number in the range from 0x00000000
|
|
(silence) through 0x0000FFFF (maximum volume).
|
|
|
|
Returns LONG:
|
|
Zero if success
|
|
PHONEERR_ constants if an error occurs
|
|
|
|
******************************************************************************/
|
|
|
|
LONG
|
|
TSPIAPI
|
|
TSPI_phoneSetRing(
|
|
DRV_REQUESTID dwRequestID,
|
|
HDRVPHONE hdPhone,
|
|
DWORD dwRingMode,
|
|
DWORD dwVolume
|
|
)
|
|
{
|
|
PPHONESP_PHONE_INFO pPhone = (PPHONESP_PHONE_INFO)gpPhone[ (DWORD_PTR) hdPhone ];
|
|
|
|
LOG((PHONESP_TRACE, "TSPI_phoneSetRing - enter"));
|
|
|
|
EnterCriticalSection(&csAllPhones);
|
|
|
|
// to confirm that the phone is open
|
|
if( ! (pPhone && pPhone->htPhone) )
|
|
{
|
|
LeaveCriticalSection(&csAllPhones);
|
|
return PHONEERR_INVALPHONEHANDLE;
|
|
}
|
|
|
|
EnterCriticalSection(&pPhone->csThisPhone);
|
|
LeaveCriticalSection(&csAllPhones);
|
|
|
|
// Check whether the phone handle is still in use
|
|
if ( !pPhone->bAllocated )
|
|
{
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
LOG((PHONESP_ERROR, "TSPI_phoneSetRing - phone not allocated"));
|
|
return PHONEERR_NODEVICE;
|
|
}
|
|
|
|
// The ringer can only be set if the phone has an output feature for this
|
|
// usage
|
|
if( ! (pPhone->dwRing & OUTPUT_REPORT) )
|
|
{
|
|
// The phone has a ringer but no output feature
|
|
if(pPhone->dwRing)
|
|
{
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
return PHONEERR_OPERATIONUNAVAIL;
|
|
}
|
|
// The phone does not have a ringer
|
|
else
|
|
{
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
return PHONEERR_RESOURCEUNAVAIL;
|
|
}
|
|
}
|
|
|
|
if ( (dwRingMode == 0) || (dwRingMode == 1) )
|
|
{
|
|
// Check whether the volume is within range
|
|
if(dwVolume <= 0x0000FFFF)
|
|
{
|
|
PPHONESP_ASYNC_REQ_INFO pAsyncReqInfo;
|
|
PPHONESP_FUNC_INFO pFuncInfo;
|
|
|
|
// Build the structure for the queueing the request in Async queue
|
|
if ( ! (pFuncInfo = (PPHONESP_FUNC_INFO)
|
|
MemAlloc(sizeof (PHONESP_FUNC_INFO)) ) )
|
|
{
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
return PHONEERR_NOMEM;
|
|
}
|
|
|
|
pFuncInfo->dwNumParams = 4;
|
|
pFuncInfo->dwParam1 = (ULONG_PTR) pPhone;
|
|
pFuncInfo->dwParam2 = dwRequestID;
|
|
pFuncInfo->dwParam3 = (ULONG_PTR) dwRingMode;
|
|
pFuncInfo->dwParam4 = (ULONG_PTR) dwVolume;
|
|
|
|
if ( ! ( pAsyncReqInfo = (PPHONESP_ASYNC_REQ_INFO)
|
|
MemAlloc(sizeof(PHONESP_ASYNC_REQ_INFO))))
|
|
{
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
MemFree(pFuncInfo);
|
|
return PHONEERR_NOMEM;
|
|
}
|
|
pAsyncReqInfo->pfnAsyncProc = TSPI_phoneSetRing_AsyncProc;
|
|
pAsyncReqInfo->pFuncInfo = pFuncInfo;
|
|
|
|
// Queue the request to perform the operation asynchronously
|
|
if( AsyncRequestQueueIn(pAsyncReqInfo) )
|
|
{
|
|
// Reset the event for number of pending requests in the queue
|
|
// for this phone and increment the counter
|
|
if (pPhone->dwNumPendingReqInQueue == 0)
|
|
{
|
|
ResetEvent(pPhone->hNoPendingReqInQueueEvent);
|
|
}
|
|
pPhone->dwNumPendingReqInQueue++;
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
}
|
|
else
|
|
{
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
MemFree(pFuncInfo);
|
|
MemFree(pAsyncReqInfo);
|
|
return PHONEERR_NOMEM;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
return PHONEERR_INVALPARAM;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
return PHONEERR_INVALRINGMODE;
|
|
}
|
|
|
|
LOG((PHONESP_TRACE, "TSPI_phoneSetRing - exit"));
|
|
return 0;
|
|
}
|
|
/********************TSPI_phoneSetRing - end**********************************/
|
|
|
|
|
|
/******************************************************************************
|
|
TSPI_phoneSetStatusMessages:
|
|
|
|
This function causes the TSP to filter status messages that are not
|
|
currently of interest to any application.
|
|
|
|
Arguments:
|
|
|
|
Returns:
|
|
|
|
******************************************************************************/
|
|
LONG
|
|
TSPIAPI
|
|
TSPI_phoneSetStatusMessages(
|
|
HDRVPHONE hdPhone,
|
|
DWORD dwPhoneStates,
|
|
DWORD dwButtonModes,
|
|
DWORD dwButtonStates
|
|
)
|
|
{
|
|
PPHONESP_PHONE_INFO pPhone = (PPHONESP_PHONE_INFO)gpPhone[ (DWORD_PTR) hdPhone ];
|
|
|
|
LOG((PHONESP_TRACE, "TSPI_phoneSetStatusMessages - enter"));
|
|
|
|
EnterCriticalSection(&csAllPhones);
|
|
if( ! (pPhone && pPhone->htPhone) )
|
|
{
|
|
LeaveCriticalSection(&csAllPhones);
|
|
return PHONEERR_INVALPHONEHANDLE;
|
|
}
|
|
|
|
EnterCriticalSection(&pPhone->csThisPhone);
|
|
LeaveCriticalSection(&csAllPhones);
|
|
|
|
// Check whether the phone handle is still in use
|
|
if ( !pPhone->bAllocated )
|
|
{
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
LOG((PHONESP_ERROR, "TSPI_phoneSetStatusMessages - phone not allocated"));
|
|
return PHONEERR_NODEVICE;
|
|
}
|
|
|
|
pPhone->dwPhoneStateMsgs = dwPhoneStates;
|
|
if (dwButtonModes)
|
|
{
|
|
if(dwButtonStates)
|
|
{
|
|
pPhone->dwButtonModesMsgs = dwButtonModes;
|
|
pPhone->dwButtonStateMsgs = dwButtonStates;
|
|
}
|
|
}
|
|
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
LOG((PHONESP_TRACE, "TSPI_phoneSetStatusMessages - exit"));
|
|
return 0;
|
|
}
|
|
|
|
/********************TSPI_phoneSetStatusMessages - end************************/
|
|
|
|
//
|
|
// ------------------------- TSPI_providerXxx funcs ---------------------------
|
|
|
|
/******************************************************************************
|
|
TSPI_providerCreatePhoneDevice
|
|
|
|
The TSP will use this function to implement PNP support. TapiSrv will call
|
|
the TSP back with this function when the TSP sends the PHONE_CREATE message
|
|
to Tapisrv, which allows the dynamic creation of a new phone device.
|
|
|
|
Arguments:
|
|
dwTempID - The temporary device identifier that the TSP passed to
|
|
TAPI in the PHONE_CREATE message.
|
|
dwDeviceID - The device identifier that TAPI assigns to this device if
|
|
this function succeeds.
|
|
|
|
Returns LONG:
|
|
Zero if the request succeeds
|
|
An error number if an error occurs.
|
|
|
|
Comments:
|
|
|
|
******************************************************************************/
|
|
LONG
|
|
TSPIAPI
|
|
TSPI_providerCreatePhoneDevice(
|
|
DWORD_PTR dwTempID,
|
|
DWORD dwDeviceID
|
|
)
|
|
{
|
|
PPHONESP_PHONE_INFO pPhone;
|
|
|
|
LOG((PHONESP_TRACE, "TSPI_providerCreatePhoneDevice - enter"));
|
|
|
|
EnterCriticalSection(&csAllPhones);
|
|
|
|
pPhone = (PPHONESP_PHONE_INFO)gpPhone[ (DWORD_PTR) dwTempID ];
|
|
|
|
// check whether the phone handle is valid
|
|
if ( IsBadReadPtr(pPhone, sizeof(PHONESP_PHONE_INFO) ) )
|
|
{
|
|
LeaveCriticalSection(&csAllPhones);
|
|
LOG((PHONESP_ERROR,"TSPI_providerCreatePhoneDevice - invalid temp id"));
|
|
return PHONEERR_INVALPHONEHANDLE;
|
|
}
|
|
|
|
EnterCriticalSection(&pPhone->csThisPhone);
|
|
LeaveCriticalSection(&csAllPhones);
|
|
|
|
if (pPhone->bCreatePending)
|
|
{
|
|
//
|
|
// Set the device ID and mark create complete
|
|
//
|
|
pPhone->dwDeviceID = dwDeviceID;
|
|
pPhone->bCreatePending = FALSE;
|
|
}
|
|
else
|
|
{
|
|
LOG((PHONESP_ERROR, "TSPI_providerCreatePhoneDevice - phone is not marked create pending"));
|
|
}
|
|
|
|
LOG((PHONESP_TRACE, "TSPI_providerCreatePhoneDevice - phone create complete [dwTempID %d] [dwDeviceID %d] ", dwTempID, dwDeviceID));
|
|
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
|
|
LOG((PHONESP_TRACE, "TSPI_providerCreatePhoneDevice - exit"));
|
|
return 0;
|
|
}
|
|
|
|
/*****************TSPI_providerCreatePhoneDevice - end************************/
|
|
|
|
/******************************************************************************
|
|
TSPI_providerEnumDevices:
|
|
|
|
TAPI calls the this function before TSPI_providerInit to determine the
|
|
number of line and phone devices supported by the TSP.
|
|
|
|
Arguments:
|
|
dwPermanentProviderID - The permanent identifier,unique within the TSPs
|
|
on this system, of the TSP being initialized.
|
|
lpdwNumLines(ignored) - TAPI initializes the value to 0.
|
|
lpdwNumPhones - A pointer to a DWORD-sized memory location into
|
|
which the TSP must write the number of phone
|
|
devices it is configured to support. TAPI
|
|
initializes the value to 0.
|
|
hProvider - An opaque DWORD-sized value that uniquely
|
|
identifies this instance of this TSP during this
|
|
execution of the Win32 Telephony environment.
|
|
lpfnLineCreateProc(ignored)- A pointer to the LINEEVENT callback
|
|
procedure supplied by TAPI. Ignored by this TSP
|
|
lpfnPhoneCreateProc - A pointer to the PHONEEVENT callback procedure
|
|
supplied by TAPI. The TSP uses this function to
|
|
send PHONE_CREATE messages when a new phone
|
|
device needs to be created.
|
|
|
|
Returns LONG:
|
|
Zero if the request succeeds or
|
|
An error number if an error occurs.
|
|
|
|
Comments:Gets a pointer to the Hid Devices belonging to the telephony page.
|
|
|
|
******************************************************************************/
|
|
|
|
LONG
|
|
TSPIAPI
|
|
TSPI_providerEnumDevices(
|
|
DWORD dwPermanentProviderID,
|
|
LPDWORD lpdwNumLines,
|
|
LPDWORD lpdwNumPhones,
|
|
HPROVIDER hProvider,
|
|
LINEEVENT lpfnLineCreateProc,
|
|
PHONEEVENT lpfnPhoneCreateProc
|
|
)
|
|
{
|
|
PPHONESP_PHONE_INFO *pPhone;
|
|
|
|
DWORD dwPhoneCnt, dwNumChars, dwCount;
|
|
|
|
LONG lResult = 0;
|
|
|
|
PHID_DEVICE pHidDevice;
|
|
PHID_DEVICE pHidDevices;
|
|
ULONG NumHidDevices;
|
|
|
|
HRESULT hr;
|
|
|
|
LOG((PHONESP_TRACE, "TSPI_providerEnumDevices - enter"));
|
|
|
|
//
|
|
// Initialise critical section for all phones which is the global object.
|
|
// Before accessing the phone structure, the thread must grab this object
|
|
//
|
|
__try
|
|
{
|
|
InitializeCriticalSection(&csAllPhones);
|
|
}
|
|
__except(1)
|
|
{
|
|
LOG((PHONESP_ERROR, "TSPI_providerEnumDevices - Initialize Critical Section"
|
|
" Failed for csAllPhones"));
|
|
return PHONEERR_NOMEM;
|
|
}
|
|
|
|
//
|
|
// Initialise critical section for all hid devices which is the global object.
|
|
// Before accessing the hid list, the thread must grab this object
|
|
//
|
|
__try
|
|
{
|
|
InitializeCriticalSection(&csHidList);
|
|
}
|
|
__except(1)
|
|
{
|
|
DeleteCriticalSection(&csAllPhones);
|
|
LOG((PHONESP_ERROR, "TSPI_providerEnumDevices - Initialize Critical Section"
|
|
" Failed for csHidList"));
|
|
return PHONEERR_NOMEM;
|
|
}
|
|
|
|
#if DBG
|
|
//Initialize critical section for memory tracing
|
|
__try
|
|
{
|
|
InitializeCriticalSection(&csMemoryList);
|
|
}
|
|
__except(1)
|
|
{
|
|
DeleteCriticalSection(&csAllPhones);
|
|
DeleteCriticalSection(&csHidList);
|
|
|
|
LOG((PHONESP_ERROR, "TSPI_providerEnumDevices - Initialize Critical Section"
|
|
" Failed for csMemoryList"));
|
|
return PHONEERR_NOMEM;
|
|
}
|
|
#endif
|
|
|
|
EnterCriticalSection(&csHidList);
|
|
|
|
// Find Telephony hid Devices
|
|
lResult = FindKnownHidDevices (&pHidDevices,
|
|
&NumHidDevices);
|
|
|
|
if (lResult != ERROR_SUCCESS)
|
|
{
|
|
LOG((PHONESP_ERROR, "TSPI_providerEnumDevices - FindKnownHidDevices failed %d", lResult));
|
|
|
|
LeaveCriticalSection(&csHidList);
|
|
DeleteCriticalSection(&csHidList);
|
|
DeleteCriticalSection(&csAllPhones);
|
|
#if DBG
|
|
DeleteCriticalSection(&csMemoryList);
|
|
#endif
|
|
|
|
if (lResult == ERROR_OUTOFMEMORY)
|
|
{
|
|
return PHONEERR_NOMEM;
|
|
}
|
|
else
|
|
{
|
|
return PHONEERR_OPERATIONFAILED;
|
|
}
|
|
}
|
|
|
|
|
|
LOG((PHONESP_TRACE, "TSPI_providerEnumDevices - number of Hid Devices : %d ", NumHidDevices));
|
|
|
|
// Allocate memory for the array of pointers where each pointer points to
|
|
// one of the phone discovered
|
|
|
|
pPhone = MemAlloc(NumHidDevices * sizeof(PPHONESP_PHONE_INFO));
|
|
|
|
if ( pPhone == NULL )
|
|
{
|
|
LOG((PHONESP_ERROR, "TSPI_providerEnumDevices - OUT OF MEMORY allocating pPhone"));
|
|
|
|
CloseHidDevices();
|
|
LeaveCriticalSection(&csHidList);
|
|
DeleteCriticalSection(&csHidList);
|
|
DeleteCriticalSection(&csAllPhones);
|
|
#if DBG
|
|
DeleteCriticalSection(&csMemoryList);
|
|
#endif
|
|
|
|
return PHONEERR_NOMEM;
|
|
}
|
|
|
|
//
|
|
// for each phone discovered, gather the capabilities of the phone and
|
|
// initialize the phone structure
|
|
//
|
|
dwPhoneCnt = 0;
|
|
|
|
for (pHidDevice = pHidDevices; pHidDevice != NULL; pHidDevice = pHidDevice->Next)
|
|
{
|
|
pHidDevice->bNew = FALSE;
|
|
|
|
// Allocate memory for this phone
|
|
pPhone[dwPhoneCnt] = (PPHONESP_PHONE_INFO)MemAlloc(sizeof(PHONESP_PHONE_INFO));
|
|
|
|
if ( pPhone[dwPhoneCnt] == NULL )
|
|
{
|
|
LOG((PHONESP_ERROR, "TSPI_providerEnumDevices - OUT OF MEMORY allocating PPHONESP_PHONE_INFO"
|
|
" for Phone %d", dwPhoneCnt));
|
|
|
|
// Release memory allocated to other phones
|
|
for(dwCount = 0; dwCount < dwPhoneCnt ; dwCount++)
|
|
{
|
|
FreePhone(pPhone[dwCount]);
|
|
MemFree((LPVOID)pPhone[dwCount]);
|
|
DeleteCriticalSection(&pPhone[dwCount]->csThisPhone);
|
|
}
|
|
MemFree((LPVOID)pPhone);
|
|
|
|
CloseHidDevices();
|
|
|
|
LeaveCriticalSection(&csHidList);
|
|
DeleteCriticalSection(&csHidList);
|
|
DeleteCriticalSection(&csAllPhones);
|
|
#if DBG
|
|
DeleteCriticalSection(&csMemoryList);
|
|
#endif
|
|
|
|
return PHONEERR_NOMEM;
|
|
}
|
|
|
|
LOG((PHONESP_TRACE, "TSPI_ProviderEnumDevices: Initializing Device: %d",dwPhoneCnt+1));
|
|
|
|
ZeroMemory( pPhone[dwPhoneCnt], sizeof(PHONESP_PHONE_INFO));
|
|
|
|
//
|
|
// Initialize the critical section object for this phone. only the
|
|
// thread that owns this object can access the structure for this phone
|
|
//
|
|
__try
|
|
{
|
|
InitializeCriticalSection(&pPhone[dwPhoneCnt]->csThisPhone);
|
|
}
|
|
__except(1)
|
|
{
|
|
// Release memory allocated to the phones
|
|
for(dwCount = 0; dwCount < dwPhoneCnt; dwCount++)
|
|
{
|
|
FreePhone(pPhone[dwCount]);
|
|
MemFree((LPVOID)pPhone[dwCount]);
|
|
DeleteCriticalSection(&pPhone[dwCount]->csThisPhone);
|
|
}
|
|
MemFree((LPVOID)pPhone[dwPhoneCnt]);
|
|
MemFree((LPVOID)pPhone);
|
|
|
|
CloseHidDevices();
|
|
|
|
LeaveCriticalSection(&csHidList);
|
|
DeleteCriticalSection(&csHidList);
|
|
DeleteCriticalSection(&csAllPhones);
|
|
#if DBG
|
|
DeleteCriticalSection(&csMemoryList);
|
|
#endif
|
|
|
|
LOG((PHONESP_ERROR,"TSPI_providerEnumDevices - Initialize Critical Section"
|
|
" Failed for Phone %d", dwPhoneCnt));
|
|
|
|
return PHONEERR_NOMEM;
|
|
}
|
|
|
|
lResult = CreatePhone( pPhone[dwPhoneCnt], pHidDevice, dwPhoneCnt );
|
|
|
|
if ( lResult != ERROR_SUCCESS )
|
|
{
|
|
LOG((PHONESP_ERROR,"TSPI_providerEnumDevices - CreatePhone"
|
|
" Failed for Phone %d: error: %d", dwPhoneCnt, lResult));
|
|
}
|
|
else
|
|
{
|
|
// Phone created successfully, increase phone count
|
|
dwPhoneCnt++;
|
|
}
|
|
}
|
|
|
|
LeaveCriticalSection(&csHidList);
|
|
|
|
*lpdwNumPhones = gdwNumPhones = dwPhoneCnt;
|
|
|
|
//
|
|
// If the space allocated previously was greater than the actual number of
|
|
// supported phones
|
|
//
|
|
if(NumHidDevices != gdwNumPhones)
|
|
{
|
|
gpPhone = MemAlloc(gdwNumPhones * sizeof(PPHONESP_PHONE_INFO));
|
|
|
|
if ( gpPhone == NULL )
|
|
{
|
|
for(dwCount = 0; dwCount < dwPhoneCnt ; dwCount++)
|
|
{
|
|
FreePhone(pPhone[dwCount]);
|
|
MemFree((LPVOID)pPhone[dwCount]);
|
|
DeleteCriticalSection(&pPhone[dwCount]->csThisPhone);
|
|
}
|
|
MemFree(pPhone);
|
|
|
|
CloseHidDevices();
|
|
|
|
DeleteCriticalSection(&csAllPhones);
|
|
#if DBG
|
|
DeleteCriticalSection(&csMemoryList);
|
|
#endif
|
|
DeleteCriticalSection(&csHidList);
|
|
|
|
LOG((PHONESP_ERROR,"TSPI_providerEnumDevices - OUT OF MEMORY allocating gpPhone"));
|
|
|
|
return PHONEERR_NOMEM;
|
|
}
|
|
|
|
CopyMemory(
|
|
gpPhone,
|
|
pPhone,
|
|
sizeof(PPHONESP_PHONE_INFO) * gdwNumPhones
|
|
);
|
|
|
|
MemFree(pPhone);
|
|
}
|
|
else
|
|
{
|
|
gpPhone = pPhone;
|
|
}
|
|
|
|
glpfnPhoneCreateProc = lpfnPhoneCreateProc;
|
|
ghProvider = hProvider;
|
|
|
|
LOG((PHONESP_TRACE, "TSPI_providerEnumDevices - exit"));
|
|
|
|
return 0;
|
|
}
|
|
/*************************TSPI_providerEnumDevices - end*********************/
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
TSPI_providerInit:
|
|
|
|
The TSPI_providerInit function initializes the service provider and gives
|
|
it parameters required for subsequent operation.
|
|
|
|
Arguments:
|
|
|
|
dwTSPIVersion - The version of the TSPI definition under which
|
|
this function must operate.
|
|
dwPermanentProviderID - The permanent identifier, unique within the TSP
|
|
on this system, of the TSP being initialized.
|
|
dwLineDeviceIDBase - Ignored by this TSP
|
|
dwPhoneDeviceIDBase - The lowest device identifier for the phone
|
|
devices supported by this service provider.
|
|
dwNumLines(Ignored) - The number of line devices this TSP supports.
|
|
dwNumPhones - The number of phone devices this TSP supports.
|
|
The value returned is the number of phone
|
|
devices reported in TSPI_providerEnumDevices.
|
|
lpfnCompletionProc - The procedure the TSP calls to report
|
|
completion of all asynchronously operating
|
|
procedures on line and phone devices.
|
|
lpdwTSPIOptions - A pointer to a DWORD-sized memory location,into
|
|
which the TSP can write a value specifying
|
|
LINETSPIOPTIONS_ values. This parameter allows
|
|
the TSP to return bits indicating optional
|
|
behaviors desired of TAPI. TAPI sets the
|
|
options DWORD to 0.
|
|
|
|
Returns LONG:
|
|
Zero if the request succeeds or
|
|
An error number if an error occurs.
|
|
|
|
Comments:
|
|
|
|
******************************************************************************/
|
|
|
|
LONG
|
|
TSPIAPI
|
|
TSPI_providerInit(
|
|
DWORD dwTSPIVersion,
|
|
DWORD dwPermanentProviderID,
|
|
DWORD dwLineDeviceIDBase,
|
|
DWORD dwPhoneDeviceIDBase,
|
|
DWORD_PTR dwNumLines,
|
|
DWORD_PTR dwNumPhones,
|
|
ASYNC_COMPLETION lpfnCompletionProc,
|
|
LPDWORD lpdwTSPIOptions
|
|
)
|
|
{
|
|
DWORD dwThreadID;
|
|
LONG lResult = 0;
|
|
|
|
LOGREGISTERTRACING(_T("hidphone"));
|
|
|
|
LOG((PHONESP_TRACE, "TSPI_providerInit - enter"));
|
|
|
|
|
|
// Load Provider Info From String Table
|
|
gszProviderInfo = PHONESP_LoadString(
|
|
IDS_PROVIDER_INFO,
|
|
&lResult
|
|
);
|
|
|
|
if(lResult != ERROR_SUCCESS)
|
|
{
|
|
DWORD dwPhoneCnt;
|
|
|
|
LOG((PHONESP_ERROR,"TSPI_providerEnumDevices - PHONESP_LoadString failed %d", lResult));
|
|
|
|
for(dwPhoneCnt = 0; dwPhoneCnt < gdwNumPhones; dwPhoneCnt++)
|
|
{
|
|
FreePhone(gpPhone[dwPhoneCnt]);
|
|
|
|
DeleteCriticalSection(&gpPhone[dwPhoneCnt]->csThisPhone);
|
|
|
|
MemFree(gpPhone[dwPhoneCnt]);
|
|
}
|
|
|
|
EnterCriticalSection(&csHidList);
|
|
CloseHidDevices();
|
|
LeaveCriticalSection(&csHidList);
|
|
|
|
DeleteCriticalSection(&csHidList);
|
|
DeleteCriticalSection(&csAllPhones);
|
|
#if DBG
|
|
DeleteCriticalSection(&csMemoryList);
|
|
#endif
|
|
|
|
if(lResult == ERROR_OUTOFMEMORY)
|
|
{
|
|
return PHONEERR_NOMEM;
|
|
}
|
|
else
|
|
{
|
|
return lResult;
|
|
}
|
|
}
|
|
|
|
|
|
glpfnCompletionProc = lpfnCompletionProc;
|
|
gdwPhoneDeviceIDBase = dwPhoneDeviceIDBase;
|
|
gdwPermanentProviderID = dwPermanentProviderID;
|
|
|
|
//
|
|
// Assign device IDs to the phones
|
|
//
|
|
{
|
|
DWORD dwPhoneCnt;
|
|
|
|
for(dwPhoneCnt = 0; dwPhoneCnt < gdwNumPhones; dwPhoneCnt++)
|
|
{
|
|
gpPhone[dwPhoneCnt]->dwDeviceID = gdwPhoneDeviceIDBase + dwPhoneCnt;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Alloc a queue for storing async requests for async completion,
|
|
// and start a thread to service that queue
|
|
//
|
|
|
|
//Initialize critical section for the async queue
|
|
__try
|
|
{
|
|
InitializeCriticalSection(&gAsyncQueue.AsyncEventQueueCritSec);
|
|
}
|
|
__except(1)
|
|
{
|
|
DWORD dwPhoneCnt;
|
|
|
|
LOG((PHONESP_ERROR, "TSPI_providerInit - Initialize Critical Section"
|
|
" Failed for gAsyncQueue.AsyncEventQueueCritSec"));
|
|
|
|
for(dwPhoneCnt = 0; dwPhoneCnt < gdwNumPhones; dwPhoneCnt++)
|
|
{
|
|
FreePhone(gpPhone[dwPhoneCnt]);
|
|
|
|
MemFree(gpPhone[dwPhoneCnt]);
|
|
DeleteCriticalSection(&gpPhone[dwPhoneCnt]->csThisPhone);
|
|
}
|
|
|
|
EnterCriticalSection(&csHidList);
|
|
CloseHidDevices();
|
|
LeaveCriticalSection(&csHidList);
|
|
|
|
MemFree((LPVOID) gszProviderInfo);
|
|
|
|
DeleteCriticalSection(&csHidList);
|
|
DeleteCriticalSection(&csAllPhones);
|
|
#if DBG
|
|
DeleteCriticalSection(&csMemoryList);
|
|
#endif
|
|
|
|
return PHONEERR_NOMEM;
|
|
}
|
|
|
|
gAsyncQueue.dwNumTotalQueueEntries = MAX_QUEUE_ENTRIES;
|
|
gAsyncQueue.dwNumUsedQueueEntries = 0;
|
|
|
|
//
|
|
// Alloc memory for the queue to accomodate dwNumTotalQueueEntries ot begin
|
|
// with. The size of the queue can later be increased as required
|
|
//
|
|
|
|
gAsyncQueue.pAsyncRequestQueue =
|
|
MemAlloc(gAsyncQueue.dwNumTotalQueueEntries * sizeof(PPHONESP_ASYNC_REQ_INFO));
|
|
|
|
if ( gAsyncQueue.pAsyncRequestQueue == NULL )
|
|
{
|
|
DWORD dwPhoneCnt;
|
|
|
|
LOG((PHONESP_ERROR, "TSPI_providerInit - OUT OF MEMORY allocating"
|
|
" gAsyncQueue.pAsyncRequestQueue"));
|
|
|
|
for(dwPhoneCnt = 0; dwPhoneCnt < gdwNumPhones; dwPhoneCnt++)
|
|
{
|
|
FreePhone(gpPhone[dwPhoneCnt]);
|
|
|
|
MemFree(gpPhone[dwPhoneCnt]);
|
|
DeleteCriticalSection(&gpPhone[dwPhoneCnt]->csThisPhone);
|
|
}
|
|
|
|
EnterCriticalSection(&csHidList);
|
|
CloseHidDevices();
|
|
LeaveCriticalSection(&csHidList);
|
|
|
|
MemFree((LPVOID) gszProviderInfo);
|
|
|
|
DeleteCriticalSection(&gAsyncQueue.AsyncEventQueueCritSec);
|
|
|
|
DeleteCriticalSection(&csHidList);
|
|
DeleteCriticalSection(&csAllPhones);
|
|
#if DBG
|
|
DeleteCriticalSection(&csMemoryList);
|
|
#endif
|
|
|
|
return PHONEERR_NOMEM;
|
|
}
|
|
|
|
|
|
gAsyncQueue.pAsyncRequestQueueIn =
|
|
gAsyncQueue.pAsyncRequestQueueOut = gAsyncQueue.pAsyncRequestQueue;
|
|
|
|
//
|
|
// the thread associated waits on this event when there are no requests
|
|
// pending in the queue. This event informs the thread when a request is
|
|
// entered in an empty queue so the thread can exit the wait state and
|
|
// process the request
|
|
//
|
|
|
|
gAsyncQueue.hAsyncEventsPendingEvent = CreateEvent (
|
|
(LPSECURITY_ATTRIBUTES) NULL,
|
|
TRUE, // manual reset
|
|
FALSE, // non-signaled
|
|
NULL // unnamed
|
|
);
|
|
|
|
if ( gAsyncQueue.hAsyncEventsPendingEvent == NULL )
|
|
{
|
|
DWORD dwPhoneCnt;
|
|
|
|
LOG((PHONESP_ERROR, "TSPI_providerInit - CreateEvent failed"
|
|
" for gAsyncQueue.hAsyncEventsPendingEvent"));
|
|
|
|
for(dwPhoneCnt = 0; dwPhoneCnt < gdwNumPhones; dwPhoneCnt++)
|
|
{
|
|
FreePhone(gpPhone[dwPhoneCnt]);
|
|
|
|
MemFree(gpPhone[dwPhoneCnt]);
|
|
DeleteCriticalSection(&gpPhone[dwPhoneCnt]->csThisPhone);
|
|
}
|
|
|
|
EnterCriticalSection(&csHidList);
|
|
CloseHidDevices();
|
|
LeaveCriticalSection(&csHidList);
|
|
|
|
MemFree((LPVOID) gszProviderInfo);
|
|
|
|
DeleteCriticalSection(&gAsyncQueue.AsyncEventQueueCritSec);
|
|
MemFree((LPVOID)gAsyncQueue.pAsyncRequestQueue);
|
|
|
|
DeleteCriticalSection(&csHidList);
|
|
DeleteCriticalSection(&csAllPhones);
|
|
#if DBG
|
|
DeleteCriticalSection(&csMemoryList);
|
|
#endif
|
|
|
|
return PHONEERR_NOMEM;
|
|
}
|
|
|
|
|
|
//
|
|
// Create the thread to service the requests in the queue
|
|
//
|
|
|
|
gAsyncQueue.hAsyncEventQueueServiceThread =
|
|
CreateThread (
|
|
(LPSECURITY_ATTRIBUTES) NULL,
|
|
0, // default stack size
|
|
(LPTHREAD_START_ROUTINE) AsyncEventQueueServiceThread,
|
|
NULL, // thread param
|
|
0, // creation flags
|
|
&dwThreadID // &dwThreadID
|
|
);
|
|
|
|
if ( gAsyncQueue.hAsyncEventQueueServiceThread == NULL )
|
|
{
|
|
DWORD dwPhoneCnt;
|
|
|
|
LOG((PHONESP_ERROR, "TSPI_providerInit - CreateThread failed"
|
|
" for gAsyncQueue.hAsyncEventQueueServiceThread"));
|
|
|
|
for(dwPhoneCnt = 0; dwPhoneCnt < gdwNumPhones; dwPhoneCnt++)
|
|
{
|
|
FreePhone(gpPhone[dwPhoneCnt]);
|
|
|
|
MemFree(gpPhone[dwPhoneCnt]);
|
|
DeleteCriticalSection(&gpPhone[dwPhoneCnt]->csThisPhone);
|
|
}
|
|
|
|
EnterCriticalSection(&csHidList);
|
|
CloseHidDevices();
|
|
LeaveCriticalSection(&csHidList);
|
|
|
|
MemFree((LPVOID) gszProviderInfo);
|
|
|
|
DeleteCriticalSection(&gAsyncQueue.AsyncEventQueueCritSec);
|
|
CloseHandle(gAsyncQueue.hAsyncEventsPendingEvent);
|
|
MemFree((LPVOID)gAsyncQueue.pAsyncRequestQueue);
|
|
|
|
DeleteCriticalSection(&csHidList);
|
|
DeleteCriticalSection(&csAllPhones);
|
|
#if DBG
|
|
DeleteCriticalSection(&csMemoryList);
|
|
#endif
|
|
|
|
return PHONEERR_NOMEM;
|
|
}
|
|
|
|
LOG((PHONESP_TRACE, "TSPI_providerInit - exit"));
|
|
return 0;
|
|
}
|
|
/***************************TSPI_providerInit - end***************************/
|
|
|
|
|
|
/******************************************************************************
|
|
TSPI_providerInstall:
|
|
|
|
This function is obsolete. However due to a bug in TAPI, the TSP must
|
|
provide a do-nothing implementation of this function and export it (along
|
|
with the superseding function TUISPI_providerInstall)
|
|
|
|
*******************************************************************************/
|
|
LONG
|
|
TSPIAPI
|
|
TSPI_providerInstall(
|
|
HWND hwndOwner,
|
|
DWORD dwPermanentProviderID
|
|
)
|
|
{
|
|
LOG((PHONESP_TRACE, "TSPI_providerInstall - enter"));
|
|
LOG((PHONESP_TRACE, "TSPI_providerInstall - exit"));
|
|
return 0;
|
|
}
|
|
/*********************TSPI_providerInstall - end******************************/
|
|
|
|
|
|
/******************************************************************************
|
|
TSPI_providerRemove:
|
|
|
|
This function is obsolete. However due to a bug in TAPI, the TSP must
|
|
provide a do-nothing implementation of this function and export it (along
|
|
with the superseding function TUISPI_providerRemove)
|
|
|
|
*******************************************************************************/
|
|
|
|
LONG
|
|
TSPIAPI
|
|
TSPI_providerRemove (
|
|
HWND hwndOwner,
|
|
DWORD dwPermanentProviderId
|
|
)
|
|
{
|
|
LOG((PHONESP_TRACE, "TSPI_providerRemove - enter"));
|
|
LOG((PHONESP_TRACE, "TSPI_providerRemove - exit"));
|
|
return 0;
|
|
}
|
|
|
|
/*********************TSPI_providerRemove - end******************************/
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
TSPI_providerShutdown:
|
|
|
|
This function shuts down the TSP. The TSP terminates any activities it has
|
|
in progress and releases any resources it has allocated.
|
|
|
|
Arguments:
|
|
dwTSPIVersion - The version of the TSPI definition under which
|
|
this function must operate.
|
|
dwPermanentProviderID - This parameter allows the TSP to determine which
|
|
among multiple possible instances of the TSP is
|
|
being shut down. The value of the parameter is
|
|
identical to that passed in the parameter of
|
|
the same name in TSPI_providerInit.
|
|
|
|
Returns LONG:
|
|
Zero if the request succeeds or
|
|
An error number if an error occurs. Possible return values are as follows:
|
|
LINEERR_INCOMPATIBLEAPIVERSION, LINEERR_NOMEM.
|
|
|
|
Comments: Whenever TAPI API call PhoneShutdown is called , it first shuts
|
|
down all the phones that are currently open using TSPI_phoneClose
|
|
and then calls TSPI_providerShutdown
|
|
|
|
|
|
******************************************************************************/
|
|
|
|
|
|
LONG
|
|
TSPIAPI
|
|
TSPI_providerShutdown(
|
|
DWORD dwTSPIVersion,
|
|
DWORD dwPermanentProviderID
|
|
)
|
|
{
|
|
DWORD dwPhoneCnt = 0;
|
|
|
|
LOG((PHONESP_TRACE, "TSPI_providerShutdown - enter"));
|
|
|
|
|
|
// this will terminate the queue service thread once all the operations
|
|
// pending in the queue are serviced
|
|
gbProviderShutdown = TRUE;
|
|
|
|
// the queue service waits for this event when the queue. By setting
|
|
// this event,the thread wakes up and realises that the queue is empty and
|
|
// hence exists since gbProviderShutdown is true
|
|
SetEvent(gAsyncQueue.hAsyncEventsPendingEvent);
|
|
|
|
// Wait for the queue thread to terminate.
|
|
WaitForSingleObject(gAsyncQueue.hAsyncEventQueueServiceThread, INFINITE);
|
|
|
|
|
|
// Free all the associated memory with the providerinfo
|
|
MemFree((LPVOID) gszProviderInfo);
|
|
|
|
EnterCriticalSection(&csAllPhones);
|
|
|
|
// Free all memory associated with the phones
|
|
for(dwPhoneCnt = 0; dwPhoneCnt < gdwNumPhones; dwPhoneCnt++)
|
|
{
|
|
EnterCriticalSection(&gpPhone[dwPhoneCnt]->csThisPhone);
|
|
FreePhone(gpPhone[dwPhoneCnt]);
|
|
LeaveCriticalSection(&gpPhone[dwPhoneCnt]->csThisPhone);
|
|
|
|
DeleteCriticalSection(&gpPhone[dwPhoneCnt]->csThisPhone);
|
|
|
|
MemFree(gpPhone[dwPhoneCnt]);
|
|
}
|
|
|
|
gdwNumPhones = 0;
|
|
|
|
LeaveCriticalSection(&csAllPhones);
|
|
|
|
CloseHandle (gAsyncQueue.hAsyncEventQueueServiceThread);
|
|
CloseHandle (gAsyncQueue.hAsyncEventsPendingEvent);
|
|
|
|
EnterCriticalSection(&csHidList);
|
|
CloseHidDevices();
|
|
LeaveCriticalSection(&csHidList);
|
|
|
|
LOG((PHONESP_TRACE, "Free Heap taken by phone"));
|
|
MemFree (gpPhone);
|
|
|
|
LOG((PHONESP_TRACE, "Free Heap taken by queue"));
|
|
MemFree (gAsyncQueue.pAsyncRequestQueue);
|
|
|
|
#if DBG
|
|
LOG((PHONESP_TRACE, "Dumping Memory Trace"));
|
|
DumpMemoryList();
|
|
|
|
DeleteCriticalSection (&csMemoryList);
|
|
#endif
|
|
|
|
DeleteCriticalSection (&gAsyncQueue.AsyncEventQueueCritSec);
|
|
DeleteCriticalSection (&csHidList);
|
|
DeleteCriticalSection (&csAllPhones);
|
|
|
|
LOG((PHONESP_TRACE, "TSPI_providerShutdown - exit"));
|
|
|
|
LOGDEREGISTERTRACING();
|
|
|
|
return 0;
|
|
}
|
|
/***************TSPI_providerShutdown*****************************************/
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
TSPI_providerUIIdentify:
|
|
|
|
This function extracts from the TSP, the fully qualified path to load
|
|
the TSP's UI DLL component.
|
|
|
|
Arguments:
|
|
lpszUIDLLName - Pointer to a block of memory at least MAX_PATH in length,
|
|
into which the TSP must copy a NULL-terminated string
|
|
specifying the fully-qualified path for the DLL
|
|
containing the TSP functions which must execute in the
|
|
process of the calling application.
|
|
|
|
Return LONG:
|
|
Returns zero if successful.
|
|
Shouldn't ever fail, but if it does returns one of these negative
|
|
error values: LINEERR_NOMEM, LINEERR_OPERATIONFAILED.
|
|
|
|
******************************************************************************/
|
|
LONG
|
|
TSPIAPI
|
|
TSPI_providerUIIdentify(
|
|
LPWSTR lpszUIDLLName
|
|
)
|
|
{
|
|
LOG((PHONESP_TRACE, "TSPI_providerUIIdentify - enter"));
|
|
|
|
//
|
|
// If we ever want to specify some other dll to handle ui, we
|
|
// would do it here.
|
|
//
|
|
GetModuleFileName(ghInst,
|
|
lpszUIDLLName,
|
|
MAX_PATH);
|
|
|
|
LOG((PHONESP_TRACE, "TSPI_providerUIIdentify - exit"));
|
|
|
|
return 0;
|
|
}
|
|
/***********************TSPI_providerUIIdentify - end ************************/
|
|
|
|
/******************************************************************************
|
|
TUISPI_providerInstall:
|
|
|
|
The TSP exports this function and provides a do-nothing implementation.
|
|
The Advanced tab of the Phone and Modem Options control panel will call
|
|
this function when the provider is to be installed, to give the TSP a
|
|
chance to do custom UI. There is no requirement for custom configuration
|
|
UI. The only requirement is that the control panel be able to
|
|
automatically install the TSP.
|
|
|
|
*******************************************************************************/
|
|
LONG
|
|
TSPIAPI
|
|
TUISPI_providerInstall(
|
|
TUISPIDLLCALLBACK lpfnUIDLLCallback,
|
|
HWND hwndOwner,
|
|
DWORD dwPermanentProviderID
|
|
)
|
|
{
|
|
LOG((PHONESP_TRACE, "TUISPI_providerInstall - enter"));
|
|
|
|
// check for previous instance
|
|
if (IsTSPAlreadyInstalled())
|
|
{
|
|
// cannot be installed twice
|
|
LOG((PHONESP_TRACE, "TUISPI_providerInstall - cannot be installed twice"));
|
|
return LINEERR_NOMULTIPLEINSTANCE;
|
|
}
|
|
|
|
LOG((PHONESP_TRACE, "TUISPI_providerInstall - exit"));
|
|
return 0;
|
|
}
|
|
/***********************TUISPI_providerInstall - end ************************/
|
|
|
|
/******************************************************************************
|
|
TUISPI_providerRemove:
|
|
|
|
The TSP exports this function and provides a do-nothing implementation.
|
|
The Advanced tab of the Phone and Modem Options control panel will call
|
|
this function when the provider is to be removed, to give the TSP a
|
|
chance to do custom UI. There is no requirement for custom configuration
|
|
UI. The only requirement is that the control panel be able to
|
|
automatically remove the TSP.
|
|
|
|
*******************************************************************************/
|
|
LONG
|
|
TSPIAPI
|
|
TUISPI_providerRemove(
|
|
TUISPIDLLCALLBACK lpfnUIDLLCallback,
|
|
HWND hwndOwner,
|
|
DWORD dwPermanentProviderID
|
|
)
|
|
{
|
|
LOG((PHONESP_TRACE, "TUISPI_providerRemove - enter"));
|
|
LOG((PHONESP_TRACE, "TUISPI_providerRemove - exit"));
|
|
return 0;
|
|
}
|
|
/***********************TUISPI_providerRemove - end ************************/
|
|
|
|
//----------------------------PRIVATE FUNCTIONS-------------------------------
|
|
|
|
|
|
/******************************************************************************
|
|
AsyncRequestQueueIn:
|
|
|
|
This function adds the new incoming request from the tapisrv to the async
|
|
queue.
|
|
|
|
Arguments:
|
|
IN PPHONESP_ASYNC_REQ_INFO pAsyncReqInfo - Pointer to the request info.
|
|
|
|
Returns BOOL:
|
|
TRUE if the function is successful
|
|
FALSE if it is not
|
|
|
|
******************************************************************************/
|
|
BOOL
|
|
AsyncRequestQueueIn (
|
|
PPHONESP_ASYNC_REQ_INFO pAsyncReqInfo
|
|
)
|
|
{
|
|
|
|
//LOG((PHONESP_TRACE, "AsyncRequestQueueIn - enter "));
|
|
|
|
EnterCriticalSection (&gAsyncQueue.AsyncEventQueueCritSec);
|
|
|
|
if (gAsyncQueue.dwNumUsedQueueEntries == gAsyncQueue.dwNumTotalQueueEntries)
|
|
{
|
|
|
|
//
|
|
// We've max'd out our ring buffer, so try to grow it
|
|
//
|
|
|
|
DWORD dwMoveSize;
|
|
PPHONESP_ASYNC_REQ_INFO *pNewAsyncRequestQueue;
|
|
|
|
if ( ! ( pNewAsyncRequestQueue =
|
|
MemAlloc(2 * gAsyncQueue.dwNumTotalQueueEntries
|
|
* sizeof (PPHONESP_ASYNC_REQ_INFO)) ) )
|
|
{
|
|
LeaveCriticalSection( &gAsyncQueue.AsyncEventQueueCritSec);
|
|
LOG((PHONESP_ERROR,"AsyncRequestQueueIn - Not enough memory to"
|
|
" queue request"));
|
|
return FALSE;
|
|
}
|
|
|
|
dwMoveSize = (DWORD) ((gAsyncQueue.pAsyncRequestQueue +
|
|
gAsyncQueue.dwNumTotalQueueEntries) -
|
|
gAsyncQueue.pAsyncRequestQueueOut) *
|
|
sizeof (PPHONESP_ASYNC_REQ_INFO);
|
|
|
|
CopyMemory(
|
|
pNewAsyncRequestQueue,
|
|
gAsyncQueue.pAsyncRequestQueueOut,
|
|
dwMoveSize
|
|
);
|
|
|
|
CopyMemory(
|
|
((LPBYTE) pNewAsyncRequestQueue) + dwMoveSize,
|
|
gAsyncQueue.pAsyncRequestQueue,
|
|
(gAsyncQueue.pAsyncRequestQueueOut -
|
|
gAsyncQueue.pAsyncRequestQueue) *
|
|
sizeof (PPHONESP_ASYNC_REQ_INFO)
|
|
);
|
|
|
|
MemFree (gAsyncQueue.pAsyncRequestQueue);
|
|
|
|
gAsyncQueue.pAsyncRequestQueue =
|
|
gAsyncQueue.pAsyncRequestQueueOut = pNewAsyncRequestQueue;
|
|
gAsyncQueue.pAsyncRequestQueueIn = pNewAsyncRequestQueue +
|
|
gAsyncQueue.dwNumTotalQueueEntries;
|
|
gAsyncQueue.dwNumTotalQueueEntries *= 2;
|
|
}
|
|
|
|
*(gAsyncQueue.pAsyncRequestQueueIn) = pAsyncReqInfo;
|
|
|
|
gAsyncQueue.pAsyncRequestQueueIn++;
|
|
|
|
// The queue is maintained as a circular list - if the queue in pointer
|
|
// has reached the bottom of the queue, reset it to point it to the top
|
|
// of the queue
|
|
if (gAsyncQueue.pAsyncRequestQueueIn == (gAsyncQueue.pAsyncRequestQueue +
|
|
gAsyncQueue.dwNumTotalQueueEntries))
|
|
{
|
|
gAsyncQueue.pAsyncRequestQueueIn = gAsyncQueue.pAsyncRequestQueue;
|
|
}
|
|
|
|
// Increment the number of outstanding requests in the queue
|
|
gAsyncQueue.dwNumUsedQueueEntries++;
|
|
|
|
// If this is the first request in the queue - set event to resume the
|
|
// thread to process the queue
|
|
|
|
if (gAsyncQueue.dwNumUsedQueueEntries == 1)
|
|
{
|
|
SetEvent (gAsyncQueue.hAsyncEventsPendingEvent);
|
|
}
|
|
|
|
LeaveCriticalSection (&gAsyncQueue.AsyncEventQueueCritSec);
|
|
|
|
//LOG((PHONESP_TRACE, "AsyncRequestQueueIn - exit"));
|
|
return TRUE;
|
|
}
|
|
/********************AsyncRequestQueueIn - end********************************/
|
|
|
|
/******************************************************************************
|
|
CreateButtonsAndAssignID
|
|
|
|
This function creates button structures for the phone from the capability
|
|
array. It also determines whether the phone has a keypad. It assigns IDs to
|
|
the buttons discovered.
|
|
|
|
Arguments:
|
|
PPHONESP_PHONE_INFO pPhone
|
|
|
|
Returns LONG:
|
|
ERROR_SUCCESS if the function succeeds
|
|
ERROR_OUTOFMEMORY if error occurs while allocating memory
|
|
|
|
******************************************************************************/
|
|
|
|
LONG
|
|
CreateButtonsAndAssignID (
|
|
PPHONESP_PHONE_INFO pPhone
|
|
)
|
|
{
|
|
DWORD i,j, dwNextFreeID = 0;
|
|
BOOL KEYPAD = TRUE;
|
|
BOOL KEYPAD_ABCD = TRUE;
|
|
PPHONESP_BUTTONINFO pButtonInfo;
|
|
DWORD lResult = 0;
|
|
|
|
LOG((PHONESP_TRACE, "CreateButtonsAndAssignID - enter"));
|
|
|
|
// First determine the number of buttons available on this phone
|
|
|
|
// If all the 12 basic key pad buttons are present
|
|
// then phone has a Keypad, else all the key pad buttons are ignored
|
|
for(i = PHONESP_PHONE_KEY_0; i <= PHONESP_PHONE_KEY_POUND; i++)
|
|
{
|
|
if(!pPhone->dwReportTypes[i])
|
|
{
|
|
KEYPAD = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Also determine if phone had ABCD buttons on its keypad
|
|
for(i = PHONESP_PHONE_KEY_A; i <= PHONESP_PHONE_KEY_D; i++)
|
|
{
|
|
if(!pPhone->dwReportTypes[i])
|
|
{
|
|
KEYPAD_ABCD = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (KEYPAD)
|
|
{
|
|
if (KEYPAD_ABCD)
|
|
{
|
|
// keypad with ABCD
|
|
pPhone->dwNumButtons = PHONESP_NUMBER_PHONE_KEYS;
|
|
}
|
|
else
|
|
{
|
|
// basic keypad
|
|
pPhone->dwNumButtons = 12;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pPhone->dwNumButtons = 0;
|
|
}
|
|
|
|
for(i = PHONESP_NUMBER_PHONE_KEYS; i < PHONESP_NUMBER_BUTTONS; i++)
|
|
{
|
|
if(pPhone->dwReportTypes[i])
|
|
{
|
|
pPhone->dwNumButtons++;
|
|
}
|
|
}
|
|
|
|
// Allocate memory for all the buttons
|
|
|
|
if ( ! (pPhone->pButtonInfo = (PPHONESP_BUTTONINFO)
|
|
MemAlloc( pPhone->dwNumButtons *
|
|
sizeof(PHONESP_BUTTONINFO)
|
|
) ) )
|
|
{
|
|
return ERROR_OUTOFMEMORY;
|
|
}
|
|
|
|
pButtonInfo = pPhone->pButtonInfo;
|
|
|
|
// if the phone has a keypad with all the 16 buttons
|
|
if (KEYPAD)
|
|
{
|
|
LOG((PHONESP_TRACE, "Phone Has a Keypad"));
|
|
|
|
for( i = PHONESP_PHONE_KEY_0; i <= (DWORD)(KEYPAD_ABCD ? PHONESP_PHONE_KEY_D : PHONESP_PHONE_KEY_POUND) ; i++, pButtonInfo++)
|
|
{
|
|
|
|
pButtonInfo->dwButtonID = i;
|
|
pButtonInfo->dwButtonMode = PHONEBUTTONMODE_KEYPAD;
|
|
pButtonInfo->dwButtonFunction = PHONEBUTTONFUNCTION_NONE;
|
|
pButtonInfo->dwButtonState = PHONEBUTTONSTATE_UP;
|
|
pPhone->dwButtonIds[i] = pButtonInfo->dwButtonID;
|
|
|
|
pButtonInfo->szButtonText = PHONESP_LoadString(
|
|
gdwButtonText[i],
|
|
&lResult
|
|
);
|
|
|
|
if(lResult != ERROR_SUCCESS)
|
|
{
|
|
DWORD dwCount;
|
|
|
|
for(dwCount =0; dwCount < i; dwCount++)
|
|
{
|
|
MemFree(pPhone->pButtonInfo->szButtonText);
|
|
pPhone->pButtonInfo++;
|
|
}
|
|
|
|
MemFree(pPhone->pButtonInfo);
|
|
return lResult;
|
|
}
|
|
|
|
LOG((PHONESP_TRACE,"Button Found '%ws' at %d", pButtonInfo->szButtonText, i));
|
|
}
|
|
|
|
dwNextFreeID = i;
|
|
pPhone->bKeyPad = TRUE;
|
|
}
|
|
else
|
|
{
|
|
// If phone has no keypad - the button ID for the feature buttons start
|
|
// from 0 else they start from 16
|
|
dwNextFreeID = 0;
|
|
}
|
|
|
|
// assign appropriate button ids for the feature buttons if they exist
|
|
for (i = PHONESP_NUMBER_PHONE_KEYS, j = 0; i < PHONESP_NUMBER_BUTTONS; i++, j++)
|
|
{
|
|
if(pPhone->dwReportTypes[i])
|
|
{
|
|
pButtonInfo->dwButtonID = dwNextFreeID;
|
|
pButtonInfo->dwButtonMode = PHONEBUTTONMODE_FEATURE;
|
|
pButtonInfo->dwButtonFunction = gdwButtonFunction[j];
|
|
pButtonInfo->dwButtonState = PHONEBUTTONSTATE_UP;
|
|
pPhone->dwButtonIds[i] = pButtonInfo->dwButtonID;
|
|
|
|
pButtonInfo->szButtonText = PHONESP_LoadString(
|
|
gdwButtonText[i],
|
|
&lResult
|
|
);
|
|
|
|
if(lResult != ERROR_SUCCESS)
|
|
{
|
|
DWORD dwCount;
|
|
DWORD dwStartID = 0;
|
|
|
|
if(KEYPAD)
|
|
{
|
|
for(dwCount = PHONESP_PHONE_KEY_0;
|
|
dwCount <= (DWORD)(KEYPAD_ABCD ? PHONESP_PHONE_KEY_D : PHONESP_PHONE_KEY_POUND); dwCount++)
|
|
{
|
|
MemFree(pPhone->pButtonInfo->szButtonText);
|
|
pPhone->pButtonInfo++;
|
|
}
|
|
dwStartID = dwCount;
|
|
}
|
|
|
|
for(dwCount = dwStartID; dwCount < dwNextFreeID; dwCount++)
|
|
{
|
|
MemFree(pPhone->pButtonInfo->szButtonText);
|
|
pPhone->pButtonInfo++;
|
|
}
|
|
|
|
MemFree(pPhone->pButtonInfo);
|
|
|
|
return lResult;
|
|
}
|
|
|
|
LOG((PHONESP_TRACE,"Button Found '%ws' at %d", pButtonInfo->szButtonText, dwNextFreeID));
|
|
|
|
dwNextFreeID++;
|
|
pButtonInfo++;
|
|
}
|
|
}
|
|
|
|
LOG((PHONESP_TRACE, "CreateButtonsAndAssignID - exit"));
|
|
return ERROR_SUCCESS;
|
|
}
|
|
/********************CreateButtonsAndAssignID - end****************************/
|
|
|
|
/*****************************************************************************
|
|
GetButtonFromID
|
|
|
|
This function will retrieve the structure for the Button from it's ID
|
|
|
|
Arguments:
|
|
IN PPHONESP_PHONE_INFO pPhone - Pointer to the phone whose button
|
|
structure has to be retrieved.
|
|
IN DWORD dwButtonID - The Button ID
|
|
|
|
|
|
Returns:
|
|
PBUTTONINFO - Pointer to the button structure if successful
|
|
NULL - If Button not found
|
|
******************************************************************************/
|
|
PPHONESP_BUTTONINFO
|
|
GetButtonFromID (
|
|
PPHONESP_PHONE_INFO pPhone,
|
|
DWORD dwButtonID
|
|
)
|
|
{
|
|
PPHONESP_BUTTONINFO pButtonInfo;
|
|
DWORD i;
|
|
|
|
// if the phone has any buttons
|
|
if (pPhone->pButtonInfo)
|
|
{
|
|
pButtonInfo = pPhone->pButtonInfo;
|
|
|
|
// search the list of buttons to find the button corresponding to the
|
|
// button id provided
|
|
for( i = 0; i < pPhone->dwNumButtons; i++)
|
|
{
|
|
if (pButtonInfo->dwButtonID == dwButtonID)
|
|
{
|
|
return pButtonInfo;
|
|
}
|
|
pButtonInfo++;
|
|
}
|
|
}
|
|
|
|
return (PPHONESP_BUTTONINFO) NULL;
|
|
}
|
|
/*************************GetButtonFromID - end*******************************/
|
|
|
|
|
|
/******************************************************************************
|
|
GetPhoneFromID:
|
|
|
|
This function returns the structure that contains the information on the
|
|
phone whose device ID is passed to this function.
|
|
|
|
Arguments:
|
|
dwDeviceID - The device ID of the phone to be retrieved
|
|
pdwPhoneID - The to a DWORD to store the index into gpPhone,
|
|
this parameter can be NULL
|
|
|
|
Returns PPHONESP_PHONE_INFO
|
|
Pointer to the phone structure if successful
|
|
NULL if phone not found
|
|
|
|
******************************************************************************/
|
|
PPHONESP_PHONE_INFO
|
|
GetPhoneFromID(
|
|
DWORD dwDeviceID,
|
|
DWORD * pdwPhoneID
|
|
)
|
|
{
|
|
DWORD dwPhone;
|
|
PPHONESP_PHONE_INFO pPhone;
|
|
|
|
LOG((PHONESP_TRACE, " GetPhoneFromID - enter"));
|
|
|
|
for (dwPhone = 0; dwPhone < gdwNumPhones; dwPhone++)
|
|
{
|
|
pPhone = (PPHONESP_PHONE_INFO) gpPhone[ dwPhone ];
|
|
|
|
EnterCriticalSection(&pPhone->csThisPhone);
|
|
|
|
if ( pPhone->bAllocated )
|
|
{
|
|
if ( pPhone->dwDeviceID == dwDeviceID )
|
|
{
|
|
// check pdwPhoneID, NULL is valid if the caller doesn't
|
|
// want us to return the phone index
|
|
if (pdwPhoneID != NULL)
|
|
{
|
|
*pdwPhoneID = dwPhone;
|
|
}
|
|
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
return pPhone;
|
|
}
|
|
}
|
|
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
}
|
|
|
|
LOG((PHONESP_TRACE, " GetPhoneFromID - exit"));
|
|
|
|
return NULL;
|
|
}
|
|
/*****************************GetPhoneFromID - end****************************/
|
|
|
|
/******************************************************************************
|
|
GetPhoneFromHid:
|
|
|
|
This function returns the structure that contains the information on the
|
|
phone whose HidDevice is passed to this function.
|
|
|
|
Arguments:
|
|
HidDevice - Pointer to a hid device
|
|
|
|
|
|
Returns PPHONESP_PHONE_INFO
|
|
Pointer to the phone structure if successful
|
|
NULL if phone not found
|
|
|
|
******************************************************************************/
|
|
PPHONESP_PHONE_INFO
|
|
GetPhoneFromHid (
|
|
PHID_DEVICE HidDevice
|
|
)
|
|
{
|
|
DWORD dwPhone;
|
|
PPHONESP_PHONE_INFO pPhone;
|
|
|
|
LOG((PHONESP_TRACE, " GetPhoneFromHid - enter"));
|
|
|
|
for (dwPhone = 0; dwPhone < gdwNumPhones; dwPhone++)
|
|
{
|
|
pPhone = (PPHONESP_PHONE_INFO) gpPhone[ dwPhone ];
|
|
|
|
EnterCriticalSection(&pPhone->csThisPhone);
|
|
|
|
if ( pPhone->bAllocated )
|
|
{
|
|
if ( pPhone->pHidDevice == HidDevice )
|
|
{
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
return pPhone;
|
|
}
|
|
}
|
|
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
}
|
|
|
|
LOG((PHONESP_TRACE, " GetPhoneFromHid - exit"));
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/******************************************************************************
|
|
GetButtonUsages:
|
|
|
|
This function parses the PHIDP_BUTTON_CAPS structure to retrieve the usages
|
|
present for the phone and records them in the capabilities array of the
|
|
phone structure.
|
|
|
|
Arguments:
|
|
PPHONESP_PHONE_INFO pPhone - The phone structure to be updated
|
|
PHIDP_BUTTON_CAPS pButtonCaps - The Button Caps structure to be parsed
|
|
DWORD dwNumberCaps - The number of Button Caps structure of the Report
|
|
Type
|
|
DWORD ReportType - Whether the usage within the Button Caps structure is
|
|
associated with an INPUT, OUTPUT or FEATURE Report.
|
|
|
|
Returns VOID.
|
|
|
|
******************************************************************************/
|
|
VOID
|
|
GetButtonUsages(
|
|
PPHONESP_PHONE_INFO pPhone,
|
|
PHIDP_BUTTON_CAPS pButtonCaps,
|
|
DWORD dwNumberCaps,
|
|
DWORD ReportType
|
|
)
|
|
{
|
|
DWORD cNumCaps;
|
|
USAGE Usage;
|
|
|
|
for (cNumCaps = 0; cNumCaps < dwNumberCaps; pButtonCaps++,cNumCaps++)
|
|
{ // if the button caps structure has a list of usages
|
|
if(pButtonCaps->IsRange)
|
|
{
|
|
for(Usage = (USAGE) pButtonCaps->Range.UsageMin;
|
|
Usage <= (USAGE) pButtonCaps->Range.UsageMax; Usage++)
|
|
{
|
|
InitPhoneAttribFromUsage(
|
|
ReportType,
|
|
pButtonCaps->UsagePage,
|
|
Usage,
|
|
pPhone,
|
|
0,
|
|
0
|
|
);
|
|
}
|
|
}
|
|
else // if the button caps structure has a single usage
|
|
{
|
|
InitPhoneAttribFromUsage(
|
|
ReportType,
|
|
pButtonCaps->UsagePage,
|
|
pButtonCaps->NotRange.Usage,
|
|
pPhone,
|
|
0,
|
|
0
|
|
);
|
|
}
|
|
}
|
|
}
|
|
/*****************************GetUsages - end********************************/
|
|
|
|
/******************************************************************************
|
|
GetReportID
|
|
|
|
This function returns the HidData structure that contains the usage
|
|
provided. The HidData structure contains the report ID for this usage
|
|
|
|
Arguments:
|
|
IN PHID_DEVICE pHidDevice - the device whose usage is provided
|
|
IN USAGE Usage - The usage whose report Id is to be discovered
|
|
OUT PHID_DATA pHidData - If the function succeeds, this structure
|
|
contains the report id for the usage, else
|
|
it is NULL
|
|
|
|
Returns LONG:
|
|
ERROR_SUCCESS - if the functions succeeds
|
|
MY_RESOURCENOTFOUND - if the usage was not found in the pHidDevice
|
|
structure provided
|
|
******************************************************************************/
|
|
LONG
|
|
GetReportID (
|
|
IN PHID_DEVICE pHidDevice,
|
|
IN USAGE Usage,
|
|
OUT PHID_DATA pHidData
|
|
)
|
|
{
|
|
PHID_DATA pData;
|
|
USAGE ButtonUsage;
|
|
|
|
pData = pHidDevice->OutputData;
|
|
|
|
while (pData)
|
|
{
|
|
// if the hid data structure has button data
|
|
if (pData->IsButtonData)
|
|
{
|
|
for(ButtonUsage = (USAGE) pData->ButtonData.UsageMin;
|
|
ButtonUsage <= (USAGE) pData->ButtonData.UsageMax; ButtonUsage++)
|
|
{
|
|
if (Usage == ButtonUsage)
|
|
{
|
|
pHidData = pData;
|
|
return ERROR_SUCCESS;
|
|
}
|
|
}
|
|
|
|
}
|
|
else
|
|
{ // if the hid data structure has value data
|
|
if (Usage == pData->ValueData.Usage)
|
|
{
|
|
pHidData = pData;
|
|
return ERROR_SUCCESS;
|
|
}
|
|
}
|
|
pData++;
|
|
}
|
|
|
|
pHidData = NULL;
|
|
|
|
return ERROR_INVALID_DATA;
|
|
}
|
|
/*************************GetReportID - end **********************************/
|
|
|
|
|
|
/******************************************************************************
|
|
GetValueUsages:
|
|
|
|
This function parses the PHIDP_VALUE_CAPS structure to retrieve the usages
|
|
present for the phone and records them in the capabilities array of the
|
|
phone structure.
|
|
|
|
Arguments:
|
|
PPHONESP_PHONE_INFO pPhone - The phone structure to be updated
|
|
PHIDP_VALUE_CAPS pValueCaps - The Value Caps structure to be parsed
|
|
DWORD dwNumberCaps - The number of Button Caps structure of the Report
|
|
Type
|
|
DWORD ReportType - Whether the usage within the Button Caps structure is
|
|
associated with an INPUT, OUTPUT or FEATURE Report.
|
|
|
|
Returns VOID.
|
|
|
|
******************************************************************************/
|
|
|
|
VOID
|
|
GetValueUsages(
|
|
PPHONESP_PHONE_INFO pPhone,
|
|
PHIDP_VALUE_CAPS pValueCaps,
|
|
DWORD dwNumberCaps,
|
|
DWORD ReportType
|
|
)
|
|
{
|
|
DWORD cNumCaps;
|
|
USAGE Usage;
|
|
|
|
for (cNumCaps=0; cNumCaps < dwNumberCaps; pValueCaps++, cNumCaps++)
|
|
{
|
|
if(pValueCaps->IsRange)
|
|
{
|
|
for(Usage = (USAGE) pValueCaps->Range.UsageMin;
|
|
Usage <= (USAGE) pValueCaps->Range.UsageMax; Usage++)
|
|
{
|
|
InitPhoneAttribFromUsage(
|
|
ReportType,
|
|
pValueCaps->UsagePage,
|
|
Usage,
|
|
pPhone,
|
|
pValueCaps->LogicalMin,
|
|
pValueCaps->LogicalMax
|
|
);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
InitPhoneAttribFromUsage(
|
|
ReportType,
|
|
pValueCaps->UsagePage,
|
|
pValueCaps->NotRange.Usage,
|
|
pPhone,
|
|
pValueCaps->LogicalMin,
|
|
pValueCaps->LogicalMax
|
|
);
|
|
}
|
|
|
|
}
|
|
}
|
|
/**********************GetValueUsages - end***********************************/
|
|
|
|
/******************************************************************************
|
|
InitPhoneAttribFromUsages:
|
|
|
|
This function is called by providerInit to determine the capabilities of
|
|
the device
|
|
|
|
Arguments:
|
|
IN DWORD ReportType - Whether the usage is a input/feature/output
|
|
IN USAGE Usage - A Usage of the device
|
|
IN OUT PPHONESP_PHONE_INFO pPhone - The pointer to the phone whose
|
|
capabilities are being determined.
|
|
|
|
Returns VOID
|
|
|
|
******************************************************************************/
|
|
VOID
|
|
InitPhoneAttribFromUsage (
|
|
DWORD ReportType,
|
|
USAGE UsagePage,
|
|
USAGE Usage,
|
|
PPHONESP_PHONE_INFO pPhone,
|
|
LONG Min,
|
|
LONG Max
|
|
)
|
|
{
|
|
|
|
PPHONESP_BUTTONINFO pButtonInfo;
|
|
|
|
//LOG((PHONESP_TRACE, "InitPhoneAttribFromUsage - enter"));
|
|
|
|
switch (UsagePage)
|
|
{
|
|
case HID_USAGE_PAGE_TELEPHONY:
|
|
{
|
|
switch (Usage)
|
|
{
|
|
case HID_USAGE_TELEPHONY_HOOKSWITCH:
|
|
pPhone->dwHandset |= ReportType;
|
|
pPhone->dwHookSwitchDevs |= PHONEHOOKSWITCHDEV_HANDSET;
|
|
pPhone->dwHandsetHookSwitchMode = PHONEHOOKSWITCHMODE_ONHOOK; //Assume handset is on hook
|
|
|
|
LOG((PHONESP_TRACE,"HOOKSWITCH USAGE, ReportType 0x%04x", ReportType));
|
|
break;
|
|
|
|
case HID_USAGE_TELEPHONY_RINGER:
|
|
pPhone->dwRing |= ReportType;
|
|
pPhone->dwRingMode = 0; //Assume the phone is not ringing
|
|
|
|
LOG((PHONESP_TRACE,"RINGER USAGE, ReportType: %d", ReportType));
|
|
break;
|
|
|
|
case HID_USAGE_TELEPHONY_SPEAKER_PHONE:
|
|
pPhone->dwSpeaker |= ReportType;
|
|
pPhone->dwHookSwitchDevs |= PHONEHOOKSWITCHDEV_SPEAKER;
|
|
pPhone->dwSpeakerHookSwitchMode = PHONEHOOKSWITCHMODE_ONHOOK; //Assume speaker is on hook
|
|
LOG((PHONESP_TRACE,"SPEAKERPHONE USAGE, ReportType 0x%04x", ReportType));
|
|
break;
|
|
|
|
|
|
default:
|
|
// Key Pad buttons
|
|
if ( (Usage >= HID_USAGE_TELEPHONY_PHONE_KEY_0) &&
|
|
(Usage <= HID_USAGE_TELEPHONY_PHONE_KEY_D) )
|
|
{
|
|
pPhone->dwReportTypes[Usage - HID_USAGE_TELEPHONY_PHONE_KEY_0] |= ReportType;
|
|
LOG((PHONESP_TRACE,"PHONE_KEY_%d USAGE, ReportType 0x%04x",
|
|
Usage - HID_USAGE_TELEPHONY_PHONE_KEY_0, ReportType));
|
|
}
|
|
else
|
|
{ // Feature Buttons
|
|
DWORD Index;
|
|
if (LookupIndexForUsage(Usage, &Index) == ERROR_SUCCESS)
|
|
{
|
|
pPhone->dwReportTypes[Index] |= ReportType;
|
|
LOG((PHONESP_TRACE,"PHONE USAGE: 0x%04x, ReportType 0x%04x",
|
|
Usage, ReportType));
|
|
}
|
|
else
|
|
{
|
|
LOG((PHONESP_TRACE, "Unsupported PHONE USAGE: 0x%04x", Usage ));
|
|
}
|
|
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
case HID_USAGE_PAGE_CONSUMER:
|
|
{
|
|
switch (Usage)
|
|
{
|
|
case HID_USAGE_CONSUMER_VOLUME:
|
|
if ((Min == -1) && (Max == 1))
|
|
{
|
|
// Phone has volume controls
|
|
pPhone->dwReportTypes[PHONESP_FEATURE_VOLUMEUP] |= ReportType;
|
|
pPhone->dwReportTypes[PHONESP_FEATURE_VOLUMEDOWN] |= ReportType;
|
|
pPhone->dwVolume |= ReportType;
|
|
LOG((PHONESP_TRACE,"VOLUME USAGE, ReportType 0x%04x", ReportType));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//LOG((PHONESP_TRACE, "InitPhoneAttribFromUsage - exit"));
|
|
}
|
|
|
|
/**************************InitPhoneAttribFromUsage - end ********************/
|
|
|
|
/******************************************************************************
|
|
InitUsage
|
|
|
|
This function takes the usage retrieved in the input report and updates the
|
|
device status and sends an appropriate Phone event
|
|
|
|
Arguments:
|
|
PPHONESP_PHONE_INFO pPhone - Pointer to phone whose input report is
|
|
received
|
|
USAGE Usage - The usage whose value is recieved
|
|
BOOL bON - The status of the usage Received
|
|
|
|
Returns VOID
|
|
******************************************************************************/
|
|
|
|
VOID
|
|
InitUsage (
|
|
PPHONESP_PHONE_INFO pPhone,
|
|
USAGE Usage,
|
|
BOOL bON
|
|
)
|
|
{
|
|
|
|
DWORD Index;
|
|
DWORD dwMode;
|
|
|
|
LOG((PHONESP_TRACE, "InitUsage - enter"));
|
|
|
|
switch (Usage)
|
|
{
|
|
case HID_USAGE_TELEPHONY_HOOKSWITCH:
|
|
if (bON)
|
|
{
|
|
LOG((PHONESP_TRACE, "HANDSET OFFHOOK"));
|
|
pPhone->dwHandsetHookSwitchMode = PHONEHOOKSWITCHMODE_MICSPEAKER;
|
|
}
|
|
else
|
|
{
|
|
LOG((PHONESP_TRACE, "HANDSET ONHOOK"));
|
|
pPhone->dwHandsetHookSwitchMode = PHONEHOOKSWITCHMODE_ONHOOK;
|
|
}
|
|
break;
|
|
|
|
case HID_USAGE_TELEPHONY_SPEAKER_PHONE:
|
|
if (bON == TRUE)
|
|
{
|
|
pPhone->bSpeakerHookSwitchButton = TRUE;
|
|
}
|
|
else
|
|
{
|
|
pPhone->bSpeakerHookSwitchButton = FALSE;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
// Feature & Phone Key Buttons
|
|
|
|
// Find the index of the usage
|
|
if (LookupIndexForUsage(Usage, &Index) == ERROR_SUCCESS)
|
|
{
|
|
PPHONESP_BUTTONINFO pButtonInfo;
|
|
|
|
//
|
|
// The index retrieved when indexed in the dwButtonIds array of the
|
|
// phone structure gives the Button ID. With this ID get the Button
|
|
// Info for that button id
|
|
//
|
|
pButtonInfo = GetButtonFromID(pPhone,pPhone->dwButtonIds[Index]);
|
|
|
|
if(pButtonInfo != NULL)
|
|
{
|
|
if(bON == TRUE)
|
|
{
|
|
// This feature button is currently on
|
|
LOG((PHONESP_TRACE, "BUTTON '%ws' DOWN", pButtonInfo->szButtonText ));
|
|
pButtonInfo->dwButtonState = PHONEBUTTONSTATE_DOWN;
|
|
}
|
|
else
|
|
{
|
|
// This feature button is currently off
|
|
LOG((PHONESP_TRACE, "BUTTON '%ws' UP", pButtonInfo->szButtonText ));
|
|
pButtonInfo->dwButtonState = PHONEBUTTONSTATE_UP;
|
|
}
|
|
}
|
|
|
|
}
|
|
break;
|
|
}
|
|
|
|
LOG((PHONESP_TRACE, "InitUsage - exit"));
|
|
|
|
}
|
|
/*************************InitUsage - end*************************************/
|
|
|
|
/******************************************************************************
|
|
LookupIndexForUsage
|
|
|
|
This function retrieves the index of the usage provided. Only the Feature
|
|
Button usages are present in this Lookup Table. Therefore only the index
|
|
for the feature buttons can be retrieved.
|
|
|
|
Arguments:
|
|
DWORD Usage - THe usage whose index is to be retrieved
|
|
DWORD *Index - The Index of the Usage retrieved
|
|
|
|
Returns LONG:
|
|
ERROR_SUCCESS - if the usage was found in the table
|
|
ERROR_INVALID_DATA - if the usage was not found in the Lookup Table
|
|
|
|
******************************************************************************/
|
|
LONG
|
|
LookupIndexForUsage(
|
|
IN DWORD Usage,
|
|
OUT DWORD *Index
|
|
)
|
|
{
|
|
DWORD cnt;
|
|
|
|
for(cnt = 0; cnt < PHONESP_NUMBER_FEATURE_BUTTONS; cnt++)
|
|
{
|
|
if(gdwLookupFeatureIndex[cnt].Usage == Usage)
|
|
{
|
|
*Index = gdwLookupFeatureIndex[cnt].Index;
|
|
return ERROR_SUCCESS;
|
|
}
|
|
}
|
|
return ERROR_INVALID_DATA;
|
|
}
|
|
/***************LookupIndexForUsage - end*************************************/
|
|
|
|
/******************************************************************************
|
|
PHONESP_LoadString:
|
|
|
|
This function loads the string from the String Table.
|
|
|
|
|
|
Arguments:
|
|
IN UINT ResourceID - Specifies the integer identifier of the string to
|
|
be loaded from the resource table
|
|
OUT WCHAR *szBuffer- The pointer to the Buffer that contains the string
|
|
|
|
Returns LONG
|
|
ERROR_SUCCESS if operation successful else
|
|
MY_NOMEM if operation failed because of not enough memory.
|
|
MY_RESOURCENOTFOUND - if the resource was not found in the string table
|
|
******************************************************************************/
|
|
|
|
LPWSTR
|
|
PHONESP_LoadString(
|
|
IN UINT ResourceID,
|
|
PLONG lResult
|
|
)
|
|
|
|
{
|
|
DWORD dwNumBytes;
|
|
DWORD dwNumChars;
|
|
DWORD dwBufferChars = 100;
|
|
|
|
WCHAR *wszBuffer;
|
|
WCHAR *szBuffer;
|
|
|
|
while (1)
|
|
{
|
|
if (! ( wszBuffer = (WCHAR *) MemAlloc(dwBufferChars * sizeof(WCHAR))))
|
|
{
|
|
LOG((PHONESP_ERROR,"PHONESP_LoadString - Not enough Memory"));
|
|
*lResult = ERROR_OUTOFMEMORY;
|
|
return (LPWSTR) NULL;
|
|
}
|
|
|
|
// load string into buffer
|
|
dwNumChars = LoadString(
|
|
ghInst,
|
|
ResourceID,
|
|
wszBuffer,
|
|
dwBufferChars
|
|
);
|
|
|
|
if( dwNumChars < dwBufferChars)
|
|
{
|
|
break;
|
|
}
|
|
|
|
// LoadString returns 0 in the dwNumChars if string resource does not exist
|
|
if (dwNumChars == 0)
|
|
{
|
|
MemFree(wszBuffer);
|
|
*lResult = ERROR_INVALID_DATA;
|
|
return (LPWSTR) NULL;
|
|
}
|
|
|
|
dwBufferChars *= 2;
|
|
MemFree(wszBuffer);
|
|
}
|
|
|
|
// determine memory needed
|
|
dwNumBytes = (dwNumChars + 1) * sizeof(WCHAR);
|
|
|
|
// allocate memory for unicode string
|
|
if ( ! ( szBuffer = (WCHAR *) MemAlloc(dwNumBytes) ) )
|
|
{
|
|
MemFree(wszBuffer);
|
|
LOG((PHONESP_ERROR,"PHONESP_LoadString - Not enough Memory"));
|
|
*lResult = ERROR_OUTOFMEMORY;
|
|
return (LPWSTR) NULL;
|
|
}
|
|
|
|
// copy loaded string into buffer
|
|
CopyMemory (
|
|
szBuffer,
|
|
wszBuffer,
|
|
dwNumBytes
|
|
);
|
|
|
|
MemFree(wszBuffer);
|
|
*lResult = ERROR_SUCCESS;
|
|
|
|
return (LPWSTR) szBuffer;
|
|
}
|
|
/*******************MyLoadString - end ***************************************/
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
ReportUsage
|
|
|
|
This function takes the usage retrieved in the input report and updates the
|
|
device status and sends an appropriate Phone event
|
|
|
|
Arguments:
|
|
PPHONESP_PHONE_INFO pPhone - Pointer to phone whose input report is
|
|
received
|
|
USAGE Usage - The usage whose value is recieved
|
|
BOOL bON - The status of the usage Received
|
|
|
|
Returns VOID
|
|
******************************************************************************/
|
|
|
|
VOID
|
|
ReportUsage (
|
|
PPHONESP_PHONE_INFO pPhone,
|
|
USAGE UsagePage,
|
|
USAGE Usage,
|
|
ULONG Value
|
|
)
|
|
{
|
|
|
|
DWORD Index;
|
|
|
|
//LOG((PHONESP_TRACE, "ReportUsage - enter"));
|
|
|
|
EnterCriticalSection(&csAllPhones);
|
|
|
|
if ( ! ( pPhone && pPhone->htPhone ) )
|
|
{
|
|
LeaveCriticalSection(&csAllPhones);
|
|
return; // exception handling
|
|
}
|
|
|
|
EnterCriticalSection(&pPhone->csThisPhone);
|
|
LeaveCriticalSection(&csAllPhones);
|
|
|
|
switch (UsagePage)
|
|
{
|
|
case HID_USAGE_PAGE_TELEPHONY:
|
|
{
|
|
switch (Usage)
|
|
{
|
|
case HID_USAGE_TELEPHONY_HOOKSWITCH:
|
|
if (Value == TRUE)
|
|
{
|
|
if (pPhone->dwHandsetHookSwitchMode != PHONEHOOKSWITCHMODE_MICSPEAKER)
|
|
{
|
|
LOG((PHONESP_TRACE, "HANDSET OFFHOOK "));
|
|
pPhone->dwHandsetHookSwitchMode = PHONEHOOKSWITCHMODE_MICSPEAKER;
|
|
|
|
SendPhoneEvent(
|
|
pPhone,
|
|
PHONE_STATE,
|
|
PHONESTATE_HANDSETHOOKSWITCH,
|
|
PHONEHOOKSWITCHMODE_MICSPEAKER,
|
|
0
|
|
);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pPhone->dwHandsetHookSwitchMode != PHONEHOOKSWITCHMODE_ONHOOK)
|
|
{
|
|
LOG((PHONESP_TRACE, "HANDSET ONHOOK"));
|
|
pPhone->dwHandsetHookSwitchMode = PHONEHOOKSWITCHMODE_ONHOOK;
|
|
|
|
SendPhoneEvent(
|
|
pPhone,
|
|
PHONE_STATE,
|
|
PHONESTATE_HANDSETHOOKSWITCH,
|
|
PHONEHOOKSWITCHMODE_ONHOOK,
|
|
0
|
|
);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case HID_USAGE_TELEPHONY_SPEAKER_PHONE:
|
|
if (Value == TRUE)
|
|
{
|
|
if (pPhone->bSpeakerHookSwitchButton == FALSE)
|
|
{
|
|
pPhone->bSpeakerHookSwitchButton = TRUE;
|
|
|
|
if (pPhone->dwSpeakerHookSwitchMode != PHONEHOOKSWITCHMODE_ONHOOK)
|
|
{
|
|
LOG((PHONESP_TRACE, "SPEAKER ONHOOK"));
|
|
pPhone->dwSpeakerHookSwitchMode = PHONEHOOKSWITCHMODE_ONHOOK;
|
|
|
|
SendPhoneEvent(
|
|
pPhone,
|
|
PHONE_STATE,
|
|
PHONESTATE_SPEAKERHOOKSWITCH,
|
|
PHONEHOOKSWITCHMODE_ONHOOK,
|
|
0
|
|
);
|
|
}
|
|
else
|
|
{
|
|
LOG((PHONESP_TRACE, "SPEAKER OFFHOOK "));
|
|
pPhone->dwSpeakerHookSwitchMode = PHONEHOOKSWITCHMODE_MICSPEAKER;
|
|
|
|
SendPhoneEvent(
|
|
pPhone,
|
|
PHONE_STATE,
|
|
PHONESTATE_SPEAKERHOOKSWITCH,
|
|
PHONEHOOKSWITCHMODE_MICSPEAKER,
|
|
0
|
|
);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pPhone->bSpeakerHookSwitchButton = FALSE;
|
|
}
|
|
break;
|
|
|
|
// Feature Buttons with on-off control
|
|
case HID_USAGE_TELEPHONY_HOLD:
|
|
case HID_USAGE_TELEPHONY_PARK:
|
|
case HID_USAGE_TELEPHONY_FORWARD_CALLS:
|
|
case HID_USAGE_TELEPHONY_CONFERENCE:
|
|
case HID_USAGE_TELEPHONY_PHONE_MUTE:
|
|
case HID_USAGE_TELEPHONY_DONOTDISTURB:
|
|
case HID_USAGE_TELEPHONY_SEND:
|
|
|
|
if (LookupIndexForUsage(Usage, &Index) == ERROR_SUCCESS)
|
|
{
|
|
PPHONESP_BUTTONINFO pButtonInfo;
|
|
|
|
pButtonInfo = GetButtonFromID(pPhone,pPhone->dwButtonIds[Index]);
|
|
|
|
if (pButtonInfo != NULL)
|
|
{
|
|
if (Value == TRUE)
|
|
{
|
|
if (pButtonInfo->dwButtonState != PHONEBUTTONSTATE_DOWN)
|
|
{
|
|
LOG((PHONESP_TRACE, "BUTTON '%ws' DOWN", pButtonInfo->szButtonText));
|
|
pButtonInfo->dwButtonState = PHONEBUTTONSTATE_DOWN;
|
|
|
|
SendPhoneEvent(
|
|
pPhone,
|
|
PHONE_BUTTON,
|
|
pPhone->dwButtonIds[Index],
|
|
PHONEBUTTONMODE_FEATURE,
|
|
PHONEBUTTONSTATE_DOWN
|
|
);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pButtonInfo->dwButtonState != PHONEBUTTONSTATE_UP)
|
|
{
|
|
LOG((PHONESP_TRACE, "BUTTON '%ws' UP", pButtonInfo->szButtonText));
|
|
pButtonInfo->dwButtonState = PHONEBUTTONSTATE_UP;
|
|
|
|
SendPhoneEvent(
|
|
pPhone,
|
|
PHONE_BUTTON,
|
|
pPhone->dwButtonIds[Index],
|
|
PHONEBUTTONMODE_FEATURE,
|
|
PHONEBUTTONSTATE_UP
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
|
|
// Key Pad buttons
|
|
if ( (pPhone->bKeyPad) &&
|
|
(Usage >= HID_USAGE_TELEPHONY_PHONE_KEY_0) &&
|
|
(Usage <= HID_USAGE_TELEPHONY_PHONE_KEY_D) )
|
|
{
|
|
PPHONESP_BUTTONINFO pButtonInfo;
|
|
|
|
pButtonInfo = GetButtonFromID(pPhone,pPhone->dwButtonIds[Usage - HID_USAGE_TELEPHONY_PHONE_KEY_0]);
|
|
|
|
if (pButtonInfo != NULL)
|
|
{
|
|
if (Value == TRUE)
|
|
{
|
|
if (pButtonInfo->dwButtonState != PHONEBUTTONSTATE_DOWN)
|
|
{
|
|
if (pButtonInfo->dwButtonState != PHONEBUTTONSTATE_DOWN)
|
|
{
|
|
LOG((PHONESP_TRACE, "BUTTON '%ws' DOWN", pButtonInfo->szButtonText));
|
|
pButtonInfo->dwButtonState = PHONEBUTTONSTATE_DOWN;
|
|
|
|
SendPhoneEvent(
|
|
pPhone,
|
|
PHONE_BUTTON,
|
|
Usage - HID_USAGE_TELEPHONY_PHONE_KEY_0,
|
|
PHONEBUTTONMODE_KEYPAD,
|
|
PHONEBUTTONSTATE_DOWN
|
|
);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pButtonInfo->dwButtonState != PHONEBUTTONSTATE_UP)
|
|
{
|
|
LOG((PHONESP_TRACE, "BUTTON '%ws' UP", pButtonInfo->szButtonText));
|
|
pButtonInfo->dwButtonState = PHONEBUTTONSTATE_UP;
|
|
|
|
SendPhoneEvent(
|
|
pPhone,
|
|
PHONE_BUTTON,
|
|
Usage - HID_USAGE_TELEPHONY_PHONE_KEY_0,
|
|
PHONEBUTTONMODE_KEYPAD,
|
|
PHONEBUTTONSTATE_UP
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{ // Feature Buttons - with one-shot control
|
|
if (LookupIndexForUsage(Usage, &Index) == ERROR_SUCCESS)
|
|
{
|
|
if (Value == TRUE)
|
|
{
|
|
PPHONESP_BUTTONINFO pButtonInfo;
|
|
|
|
pButtonInfo = GetButtonFromID(pPhone,pPhone->dwButtonIds[Index]);
|
|
|
|
if ( pButtonInfo != NULL )
|
|
{
|
|
LOG((PHONESP_TRACE, "BUTTON '%ws' DOWN", pButtonInfo->szButtonText));
|
|
|
|
SendPhoneEvent(
|
|
pPhone,
|
|
PHONE_BUTTON,
|
|
pPhone->dwButtonIds[Index],
|
|
PHONEBUTTONMODE_FEATURE,
|
|
PHONEBUTTONSTATE_DOWN
|
|
);
|
|
|
|
LOG((PHONESP_TRACE, "BUTTON '%ws' UP", pButtonInfo->szButtonText));
|
|
|
|
SendPhoneEvent(
|
|
pPhone,
|
|
PHONE_BUTTON,
|
|
pPhone->dwButtonIds[Index],
|
|
PHONEBUTTONMODE_FEATURE,
|
|
PHONEBUTTONSTATE_UP
|
|
);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOG((PHONESP_TRACE, "Unsupported PHONE USAGE: 0x%04x",Usage));
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case HID_USAGE_PAGE_CONSUMER:
|
|
{
|
|
switch (Usage)
|
|
{
|
|
case HID_USAGE_CONSUMER_VOLUME:
|
|
{
|
|
if (pPhone->dwVolume)
|
|
{
|
|
PPHONESP_BUTTONINFO pUpButtonInfo;
|
|
PPHONESP_BUTTONINFO pDownButtonInfo;
|
|
|
|
pUpButtonInfo = GetButtonFromID(pPhone,pPhone->dwButtonIds[PHONESP_FEATURE_VOLUMEUP]);
|
|
pDownButtonInfo = GetButtonFromID(pPhone,pPhone->dwButtonIds[PHONESP_FEATURE_VOLUMEDOWN]);
|
|
|
|
if ((pUpButtonInfo != NULL) && (pDownButtonInfo != NULL))
|
|
{
|
|
switch (Value) // 2-bit signed
|
|
{
|
|
case 0x0:
|
|
if (pUpButtonInfo->dwButtonState != PHONEBUTTONSTATE_UP)
|
|
{
|
|
LOG((PHONESP_TRACE, "BUTTON '%ws' UP", pUpButtonInfo->szButtonText));
|
|
pUpButtonInfo->dwButtonState = PHONEBUTTONSTATE_UP;
|
|
|
|
SendPhoneEvent(
|
|
pPhone,
|
|
PHONE_BUTTON,
|
|
pPhone->dwButtonIds[PHONESP_FEATURE_VOLUMEUP],
|
|
PHONEBUTTONMODE_FEATURE,
|
|
PHONEBUTTONSTATE_UP
|
|
);
|
|
}
|
|
|
|
if (pDownButtonInfo->dwButtonState != PHONEBUTTONSTATE_UP)
|
|
{
|
|
LOG((PHONESP_TRACE, "BUTTON '%ws' UP", pDownButtonInfo->szButtonText));
|
|
pDownButtonInfo->dwButtonState = PHONEBUTTONSTATE_UP;
|
|
|
|
SendPhoneEvent(
|
|
pPhone,
|
|
PHONE_BUTTON,
|
|
pPhone->dwButtonIds[PHONESP_FEATURE_VOLUMEDOWN],
|
|
PHONEBUTTONMODE_FEATURE,
|
|
PHONEBUTTONSTATE_UP
|
|
);
|
|
}
|
|
break;
|
|
case 0x1:
|
|
if (pUpButtonInfo->dwButtonState != PHONEBUTTONSTATE_DOWN)
|
|
{
|
|
LOG((PHONESP_TRACE, "BUTTON '%ws' DOWN", pUpButtonInfo->szButtonText));
|
|
pUpButtonInfo->dwButtonState = PHONEBUTTONSTATE_DOWN;
|
|
|
|
SendPhoneEvent(
|
|
pPhone,
|
|
PHONE_BUTTON,
|
|
pPhone->dwButtonIds[PHONESP_FEATURE_VOLUMEUP],
|
|
PHONEBUTTONMODE_FEATURE,
|
|
PHONEBUTTONSTATE_DOWN
|
|
);
|
|
}
|
|
|
|
if (pDownButtonInfo->dwButtonState != PHONEBUTTONSTATE_UP)
|
|
{
|
|
LOG((PHONESP_TRACE, "BUTTON '%ws' UP", pDownButtonInfo->szButtonText));
|
|
pDownButtonInfo->dwButtonState = PHONEBUTTONSTATE_UP;
|
|
|
|
SendPhoneEvent(
|
|
pPhone,
|
|
PHONE_BUTTON,
|
|
pPhone->dwButtonIds[PHONESP_FEATURE_VOLUMEDOWN],
|
|
PHONEBUTTONMODE_FEATURE,
|
|
PHONEBUTTONSTATE_UP
|
|
);
|
|
}
|
|
break;
|
|
case 0x3:
|
|
if (pUpButtonInfo->dwButtonState != PHONEBUTTONSTATE_UP)
|
|
{
|
|
LOG((PHONESP_TRACE, "BUTTON '%ws' UP", pUpButtonInfo->szButtonText));
|
|
pUpButtonInfo->dwButtonState = PHONEBUTTONSTATE_UP;
|
|
|
|
SendPhoneEvent(
|
|
pPhone,
|
|
PHONE_BUTTON,
|
|
pPhone->dwButtonIds[PHONESP_FEATURE_VOLUMEUP],
|
|
PHONEBUTTONMODE_FEATURE,
|
|
PHONEBUTTONSTATE_UP
|
|
);
|
|
}
|
|
|
|
if (pDownButtonInfo->dwButtonState != PHONEBUTTONSTATE_DOWN)
|
|
{
|
|
LOG((PHONESP_TRACE, "BUTTON '%ws' DOWN", pDownButtonInfo->szButtonText));
|
|
pDownButtonInfo->dwButtonState = PHONEBUTTONSTATE_DOWN;
|
|
|
|
SendPhoneEvent(
|
|
pPhone,
|
|
PHONE_BUTTON,
|
|
pPhone->dwButtonIds[PHONESP_FEATURE_VOLUMEDOWN],
|
|
PHONEBUTTONMODE_FEATURE,
|
|
PHONEBUTTONSTATE_DOWN
|
|
);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
LeaveCriticalSection(&pPhone->csThisPhone);
|
|
|
|
//LOG((PHONESP_TRACE, "ReportUsage - exit"));
|
|
|
|
}
|
|
/**********************ReportUsage - end**************************************/
|
|
|
|
|
|
/******************************************************************************
|
|
SendPhoneEvent:
|
|
|
|
This function determines whether TAPI had requested the receipt of this
|
|
message and if requested, then sends the phone device message .
|
|
|
|
Arguments:
|
|
PMYPHONE pPhone - The pointer to the phone
|
|
DWORD dwMsg - Type of Phone Event such as PHONE_BUTTON, etc
|
|
ULONG_PTR Param1 - Details relating to the Phone Event
|
|
ULONG_PTR Param2 - "
|
|
ULONG_PTR Param3 - "
|
|
|
|
Returns VOID
|
|
|
|
******************************************************************************/
|
|
VOID
|
|
SendPhoneEvent(
|
|
PPHONESP_PHONE_INFO pPhone,
|
|
DWORD dwMsg,
|
|
ULONG_PTR Param1,
|
|
ULONG_PTR Param2,
|
|
ULONG_PTR Param3
|
|
)
|
|
{
|
|
LOG((PHONESP_TRACE, "SendPhoneEvent - enter"));
|
|
|
|
switch (dwMsg)
|
|
{
|
|
case PHONE_BUTTON:
|
|
|
|
if ( ((Param2) & pPhone->dwButtonModesMsgs ) &&
|
|
((Param3) & pPhone->dwButtonStateMsgs) )
|
|
{
|
|
(*(pPhone->lpfnPhoneEventProc))(
|
|
pPhone->htPhone,
|
|
dwMsg,
|
|
Param1,
|
|
Param2,
|
|
Param3
|
|
);
|
|
}
|
|
break;
|
|
|
|
case PHONE_REMOVE:
|
|
(*(glpfnPhoneCreateProc))(
|
|
0,
|
|
dwMsg,
|
|
Param1,
|
|
Param2,
|
|
Param3
|
|
);
|
|
break;
|
|
|
|
case PHONE_CREATE:
|
|
(*(glpfnPhoneCreateProc))(
|
|
0,
|
|
dwMsg,
|
|
Param1,
|
|
Param2,
|
|
Param3
|
|
);
|
|
|
|
break;
|
|
|
|
case PHONE_STATE:
|
|
if (Param1 & pPhone->dwPhoneStateMsgs)
|
|
{
|
|
(*(pPhone->lpfnPhoneEventProc))(
|
|
pPhone->htPhone,
|
|
dwMsg,
|
|
Param1,
|
|
Param2,
|
|
Param3
|
|
);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
LOG((PHONESP_TRACE, "SendPhoneEvent - exit"));
|
|
}
|
|
/****************************SendPhoneEvent - end*****************************/
|
|
|
|
/******************************************************************************
|
|
SendOutputReport
|
|
|
|
This function forms an output report for the usage provided and sends it to
|
|
the device
|
|
|
|
Arguments:
|
|
PHID_DEVICE pHidDevice - The hid device to which the output report is
|
|
be sent
|
|
USAGE Usage - The Usage for which the output report is to be
|
|
sent
|
|
BOOL bSet - Whether the usage has to be set or reset
|
|
|
|
Returns LONG:
|
|
ERROR_SUCCESS if the function succeeded
|
|
ERROR_INVALID_DATA on error
|
|
|
|
******************************************************************************/
|
|
|
|
LONG
|
|
SendOutputReport(
|
|
PHID_DEVICE pHidDevice,
|
|
USAGE Usage,
|
|
BOOL bSet
|
|
)
|
|
{
|
|
HID_DATA HidData;
|
|
PUSAGE UsageList = &Usage;
|
|
LONG NumUsages = 1;
|
|
|
|
if ( GetReportID(pHidDevice, Usage, &HidData) == ERROR_SUCCESS)
|
|
{
|
|
NTSTATUS Result;
|
|
|
|
memset ( pHidDevice->OutputReportBuffer,
|
|
(UCHAR) 0,
|
|
pHidDevice->Caps.OutputReportByteLength
|
|
);
|
|
|
|
if (HidData.IsButtonData)
|
|
{
|
|
if (bSet)
|
|
{
|
|
Result = HidP_SetUsages (
|
|
HidP_Output,
|
|
HidData.UsagePage,
|
|
0,
|
|
UsageList,
|
|
&NumUsages,
|
|
pHidDevice->Ppd,
|
|
pHidDevice->OutputReportBuffer,
|
|
pHidDevice->Caps.OutputReportByteLength
|
|
);
|
|
|
|
if(Result != HIDP_STATUS_SUCCESS)
|
|
{
|
|
return ERROR_INVALID_DATA;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Result = HidP_UnsetUsages (
|
|
HidP_Output,
|
|
HidData.UsagePage,
|
|
0,
|
|
UsageList,
|
|
&NumUsages,
|
|
pHidDevice->Ppd,
|
|
pHidDevice->OutputReportBuffer,
|
|
pHidDevice->Caps.OutputReportByteLength
|
|
);
|
|
if(Result != HIDP_STATUS_SUCCESS)
|
|
{
|
|
return ERROR_INVALID_DATA;
|
|
}
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Result = HidP_SetUsageValue (
|
|
HidP_Output,
|
|
HidData.UsagePage,
|
|
0,
|
|
Usage,
|
|
HidData.ValueData.Value,
|
|
pHidDevice->Ppd,
|
|
pHidDevice->OutputReportBuffer,
|
|
pHidDevice->Caps.OutputReportByteLength
|
|
);
|
|
if(Result != HIDP_STATUS_SUCCESS)
|
|
{
|
|
return ERROR_INVALID_DATA;
|
|
}
|
|
|
|
}
|
|
|
|
Write(pHidDevice);
|
|
}
|
|
else
|
|
{
|
|
return ERROR_INVALID_DATA;
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
/************************SendOutputReport - end*******************************/
|
|
|
|
|
|
/******************************************************************************
|
|
ShowData
|
|
|
|
This function is called by the queue service thread when the request queued
|
|
is an input report. This function retrieves the Usages present in this
|
|
structure and passes them on to ReportUsage which performs appropriate
|
|
actions.
|
|
|
|
******************************************************************************/
|
|
VOID
|
|
CALLBACK
|
|
ShowData(
|
|
PPHONESP_FUNC_INFO pAsyncFuncInfo
|
|
)
|
|
{
|
|
|
|
PPHONESP_PHONE_INFO pPhone = (PPHONESP_PHONE_INFO) pAsyncFuncInfo->dwParam1;
|
|
BOOL bButton;
|
|
|
|
if( (DWORD) pAsyncFuncInfo->dwParam2 == PHONESP_BUTTON)
|
|
{
|
|
USAGE UsagePage = (USAGE) pAsyncFuncInfo->dwParam3;
|
|
USAGE UsageMin = (USAGE) pAsyncFuncInfo->dwParam4;
|
|
USAGE UsageMax = (USAGE) pAsyncFuncInfo->dwParam5;
|
|
DWORD MaxUsageLength = (DWORD) pAsyncFuncInfo->dwParam6;
|
|
PUSAGE Usages = (PUSAGE) pAsyncFuncInfo->dwParam7;
|
|
USAGE Usage;
|
|
|
|
for ( Usage = UsageMin; Usage <= UsageMax; Usage++ )
|
|
{
|
|
DWORD i;
|
|
|
|
for ( i = 0; i < MaxUsageLength; i++ )
|
|
{
|
|
if ( Usage == Usages[i] )
|
|
{
|
|
//LOG((PHONESP_TRACE,"ShowData - UsagePage 0x%04x Usage 0x%04x BUTTON DOWN", UsagePage, Usage));
|
|
ReportUsage(pPhone, UsagePage, Usage, TRUE);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( i == MaxUsageLength )
|
|
{
|
|
//LOG((PHONESP_TRACE,"ShowData - UsagePage 0x%04x Usage 0x%04x BUTTON UP", UsagePage, Usage));
|
|
ReportUsage(pPhone, UsagePage, Usage, FALSE);
|
|
}
|
|
}
|
|
MemFree(Usages);
|
|
}
|
|
else
|
|
{
|
|
USAGE UsagePage = (USAGE) pAsyncFuncInfo->dwParam3;
|
|
USAGE Usage = (USAGE) pAsyncFuncInfo->dwParam4;
|
|
ULONG Value = (ULONG) pAsyncFuncInfo->dwParam5;
|
|
|
|
//LOG((PHONESP_TRACE,"ShowData - UsagePage 0x%04x Usage 0x%04x VALUE %d", UsagePage, Usage, Value));
|
|
ReportUsage(pPhone, UsagePage, Usage, Value);
|
|
}
|
|
}
|
|
/*******************ShowData - end********************************************/
|
|
|
|
|