/*++ 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********************************************/