/*---------------------------------------------------------------------------*/ /* plantronics_headset.cpp */ /* copyright (c) innovaphone 2012 */ /* */ /*---------------------------------------------------------------------------*/ #ifdef _WINDOWS #include #include #include #include #include #include #include #include #include "windows.h" #else #include #include #define FreeLibrary(hmodule) dlclose(hmodule) #define GetProcAddress(hmodule,name) dlsym(hmodule,name) extern "C" void ns_log(const char *format, ...); #endif #include "platform/platform.h" #include "common/ilib/str.h" #include "../interface/headset_provider_if.h" #include "plantronics_headset.h" NAMESPACE_BEGIN #define PlantronicsDll_InitSpokesRuntime(plantronicsDll) ((int (*)()) GetProcAddress(plantronicsDll, "InitSpokesRuntime"))() #define PlantronicsDll_ShutDownSpokesRuntime(plantronicsDll) ((void (*)()) GetProcAddress(plantronicsDll, "ShutDownSpokesRuntime"))() #define PlantronicsDll_getSessionManager(plantronicsDll,ppSessionMgr) ((SMResult (*)(ppSessionManager PpSessionMgr)) GetProcAddress(plantronicsDll, "getSessionManager"))(ppSessionMgr) #define PlantronicsDll_InitLogger(plantronicsDll,cfgFilename,logDir,logFilename,logger,initWithModuleLogs) ((void (*)(char const *CfgFilename, char const *LogDir, char const *LogFilename, char const *Logger, bool InitWithModuleLogs)) GetProcAddress(plantronicsDll, "InitLogger"))(cfgFilename,logDir,logFilename,logger,initWithModuleLogs) #define PlantronicsDll_StopLogger(plantronicsDll) ((void (*)()) GetProcAddress(plantronicsDll, "StopLogger"))() #define PlantronicsDll_SetLogLevel(plantronicsDll,logLevel) ((int (*)(eSpokesLogLevel LogLevel)) GetProcAddress(plantronicsDll, "SetLogLevel"))(logLevel) #define CS_NONE 0 #define CS_INCALL_INIT 1 #define CS_UP 2 #define CS_HOLD 3 static const char *CallStateToString(byte callState) { switch (callState) { case CS_NONE: return "CS_NONE"; case CS_INCALL_INIT: return "CS_INCALL_INIT"; case CS_UP: return "CS_UP"; case CS_HOLD: return "CS_HOLD"; } return NULL; } static const char *KeyToString(byte key) { switch (key) { case KEY_EHS_TALK: return "KEY_EHS_TALK"; case KEY_EHS_FLASH: return "KEY_EHS_FLASH"; case KEY_EHS_REDIAL: return "KEY_EHS_REDIAL"; case KEY_OFFHOOK: return "KEY_OFFHOOK"; case KEY_ONHOOK: return "KEY_ONHOOK"; case KEY_DISC: return "KEY_DISC"; case KEY_MIC: return "KEY_MIC"; case KEY_INCALL: return "KEY_INCALL"; case KEY_HOLD: return "KEY_HOLD"; case KEY_RETRIEVE: return "KEY_RETRIEVE"; } return NULL; } /* static const char *PlantronicsHeadsetButtonToString(eHeadsetButton headsetButton) { switch (headsetButton) { case HEADSET_BUTTON_UNKNOWN: return "Unknown"; case HEADSET_BUTTON_VOLUME_UP: return "VolumeUp"; case HEADSET_BUTTON_VOLUME_DOWN: return "VolumeDown"; case HEADSET_BUTTON_VOLUME_UP_HELD: return "VolumeUpHeld"; case HEADSET_BUTTON_VOLUME_DOWN_HELD: return "VolumeDownHeld"; case HEADSET_BUTTON_MUTE: return "Mute"; case HEADSET_BUTTON_MUTE_HELD: return "MuteHeld"; case HEADSET_BUTTON_TALK: return "Talk"; case HEADSET_BUTTON_AUDIO: return "Audio"; case HEADSET_BUTTON_PLAY: return "Play"; case HEADSET_BUTTON_PAUSE: return "Pause"; case HEADSET_BUTTON_NEXT: return "Next"; case HEADSET_BUTTON_PREVIOUS: return "Previous"; case HEADSET_BUTTON_FAST_FORWARD: return "FastForward"; case HEADSET_BUTTON_REWIND: return "Rewind"; case HEADSET_BUTTON_STOP: return "Stop"; case HEADSET_BUTTON_FLASH: return "Flash"; case HEADSET_BUTTON_SMART: return "Smart"; //Handset buttons case HEADSET_BUTTON_OFF_HOOK: return "OffHook"; case HEADSET_BUTTON_ON_HOOK: return "OnHook"; case HEADSET_BUTTON_KEY_0: return "Key0"; case HEADSET_BUTTON_KEY_1: return "Key1"; case HEADSET_BUTTON_KEY_2: return "Key2"; case HEADSET_BUTTON_KEY_3: return "Key3"; case HEADSET_BUTTON_KEY_4: return "Key4"; case HEADSET_BUTTON_KEY_5: return "Key5"; case HEADSET_BUTTON_KEY_6: return "Key6"; case HEADSET_BUTTON_KEY_7: return "Key7"; case HEADSET_BUTTON_KEY_8: return "Key8"; case HEADSET_BUTTON_KEY_9: return "Key9"; case HEADSET_BUTTON_KEY_STAR: return "KeyStar"; case HEADSET_BUTTON_KEY_POUND: return "KeyPound"; case HEADSET_BUTTON_SPEAKER: return "Speaker"; case HEADSET_BUTTON_REJECT: return "Reject"; case HEADSET_BUTTON_REDIAL: return "Redial"; } return NULL; } */ static const char *PlantronicsDeviceEventKindToString(eDeviceEventKind deviceEventKind) { switch (deviceEventKind) { case DEVICE_EVENT_KIND_DOCKED: return "Docked"; case DEVICE_EVENT_KIND_UNDOCKED: return "Undocked"; case DEVICE_EVENT_KIND_TALKPRESS: return "Talkpress"; case DEVICE_EVENT_KIND_UNKNOWN: return "-"; case DEVICE_EVENT_KIND_NO_DEVICE: return "NoDevice"; case DEVICE_EVENT_KIND_BASEPRESS: return "Basepress"; case DEVICE_EVENT_KIND_IDLE_TALKPRESS: return "IdleTalkpress"; } return NULL; } static const char *PlantronicsCallStateToString(eCallState callState) { switch (callState) { case CALL_STATE_UNKNOWN: return "-"; case CALL_STATE_ACCEPT_CALL: return "AcceptCall"; case CALL_STATE_TERMINATE_CALL: return "TerminateCall"; case CALL_STATE_HOLD_CALL: return "HoldCall"; case CALL_STATE_RESUME_CALL: return "ResumeCall"; case CALL_STATE_FLASH: return "Flash"; case CALL_STATE_CALL_IN_PROGRESS: return "CallInProgress"; case CALL_STATE_CALL_RINGING: return "CallRinging"; case CALL_STATE_CALL_ENDED: return "CallEnded"; case CALL_STATE_TRANSFER_TO_HEADSET: return "TransferToHeadset"; case CALL_STATE_TRANSFER_TO_SPEAKER: return "TransferToSpeaker"; case CALL_STATE_MUTEON: return "MuteOn"; case CALL_STATE_MUTEOFF: return "MuteOff"; case CALL_STATE_MOBILE_CALL_RINGING: return "MobileCallRinging"; case CALL_STATE_MOBILE_CALL_IN_PROGRESS: return "MobileCallInProgress"; case CALL_STATE_MOBILE_CALL_ENDED: return "MobileCallEnded"; case CALL_STATE_DON: return "DOn"; case CALL_STATE_DOFF: return "DOff"; case CALL_STATE_CALL_IDLE: return "CallIdle"; case CALL_STATE_PLAY: return "Play"; case CALL_STATE_PAUSE: return "Pause"; case CALL_STATE_STOP: return "Stop"; case CALL_STATE_DTMF_KEY: return "DTMFKey"; case CALL_STATE_REJECT_CALL: return "RejectCall"; case CALL_STATE_MAKE_CALL: return "MakeCall"; case CALL_STATE_HOOK: return "Hook"; case CALL_STATE_HOOK_IDLE: return "HookIdle"; case CALL_STATE_HOOK_DOCKED: return "HookDocked"; case CALL_STATE_HOOK_UNDOCKED: return "HookUndocked"; case CALL_STATE_BASE_EVENT: return "BaseEvent"; case CALL_STATE_CALL_ANSWERED_AND_ENDED: return "CallANsweredAndEnded"; case CALL_STATE_CALL_UNANSWERED_AND_ENDED: return "CallUnansweredAndEnded"; case CALL_STATE_DEVICE_CHANGE: return "DeviceChange"; case CALL_STATE_DEVICE_ARRIVED: return "DeviceArrived"; case CALL_STATE_DEVICE_REMOVED: return "DeviceRemoved"; } return NULL; } static const char *PlantronicsSMResultToString(SMResult smResult) { switch (smResult) { case SM_RESULT_SUCCESS: return "Success"; case SM_RESULT_FAIL: return "Fail"; case SM_RESULT_INVALID_ARGUMENT: return "Invalid argument"; case SM_RESULT_ALREADY_REGISTERED: return "Already registered"; case SM_RESULT_NOT_FOUND: return "Not found"; case SM_RESULT_NOT_SUPPORTED: return "Not supported"; case SM_RESULT_NOT_INITIALIZED: return "Not initialized"; } return NULL; } static const char *PlantronicsCMResultToString(CMResult cmResult) { switch (cmResult) { case CM_RESULT_SUCCESS: return "Success"; case CM_RESULT_FAIL: return "Fail"; case CM_RESULT_INVALID_ARGUMENT: return "Invalid argument"; case CM_RESULT_INVALID_STATE: return "Invalid state"; case CM_RESULT_NO_DEVICE: return "No device"; case CM_RESULT_A2DP_DEVICE: return "A2DP device"; case CM_RESULT_INVALID_LINETYPE: return "Invalid line type"; case CM_RESULT_NOT_SUPPORTED: return "Not supported"; case CM_RESULT_CALL_CONTROL_NO_DEVICE: return "Call control no device"; } return NULL; } static const char *PlantronicsDMResultToString(DMResult dmResult) { switch (dmResult) { case DM_RESULT_SUCCESS: return "Success"; case DM_RESULT_FAIL: return "Fail"; case DM_RESULT_BUFFER_TOO_SMALL: return "Buffer too small"; case DM_RESULT_ILLEGAL_ARGUMENT: return "Illegal argument"; case DM_RESULT_NOT_FOUND: return "Not found"; case DM_RESULT_NOT_SUPPORTED: return "Not supported"; case DM_RESULT_DATA_NOT_AVAILABLE: return "Data not available"; case DM_RESULT_BUSY: return "Busy"; case DM_RESULT_USAGE_NOT_FOUND: return "Usage not found"; case DM_RESULT_NOT_IMPLEMENTED: return "Not implemented"; case DMRESULT_INVALID_PRODUCT_SERIAL_NO: return "Invalid product serial number"; case DMRESULT_INVALID_PRODUCT_BUILD_NO: return "Invalid product build number"; case DMRESULT_INVALID_REFERENCE: return "Invalid reference"; case DM_RESULT_USB_RESET_FAILED: return "USB reset failed"; } return NULL; } static dword from_wchar_t(const wchar_t *in, char *out, dword cnt) { #if (__WCHAR_MAX__ <= 0x7f) dword l; l = strlen(in); if (l < cnt) { memcpy(out, in, l + 1); return l; } else { memcpy(out, in, cnt - 1); out[cnt - 1] = '\0'; return cnt - 1; } #elif (__WCHAR_MAX__ <= 0x7fff) return str::from_ucs2((const word *) in, out, cnt); #else word buf[128]; dword i, j, n, l, len; l = 0; while (in[l] != 0) { l++; } len = 0; i = 0; while ((i < l) && (len + 8 < cnt)) { n = l - i; if (n >= sizeof(buf) / sizeof(buf[0])) { n = (sizeof(buf) / sizeof(buf[0])) - 1; } for (j = 0; j < n; j++) { buf[j] = (word)(in[i + j]); } len += str::from_ucs2_n(buf, n, &out[len], cnt - len - 1); i += n; } out[len] = '\0'; return len; #endif } static dword to_wchar_t(const char *in, wchar_t *out, dword cnt) { #if (__WCHAR_MAX__ <= 0x7f) dword l; l = strlen(in); if (l < cnt) { memcpy(out, in, l + 1); return l; } else { memcpy(out, in, cnt - 1); out[cnt - 1] = '\0'; return cnt - 1; } #elif (__WCHAR_MAX__ <= 0x7fff) return str::to_ucs2(in, (word *) out, cnt); #else dword i, l; l = str::to_ucs2(in, (word *) out, cnt); i = l + 1; do { i--; ((wchar_t *) out)[i] = ((word *) out)[i]; } while (i != 0); return l; #endif } PlantronicsHeadsetProvider *plantronicsHeadsetProvider; PlantronicsHeadsetProvider::PlantronicsHeadsetProvider() { unsigned i; strcpy(tag, "PLANTRONICS"); user = NULL; plantronicsDll = NULL; sessionManager = NULL; session = NULL; callCommand = NULL; for (i = 0; i < sizeof(calls) / sizeof(calls[0]); i++) { calls[i].state = CS_NONE; } mute = false; deviceCount = 0; activeDeviceId[0] = '\0'; serviceStarted = false; eventQueueFill = 0; eventQueuePos = 0; eventQueueAck = 0; logDeviceEventPos = 0; logDeviceEventAck = 0; for (i = 0; i < MAX_PLANTRONICS_LOG_DEVICE_EVENTS; i++) { logDeviceEvents[i][PLANTRONICS_LOG_DEVICE_EVENT_SIZE - 1] = '\0'; } plantronicsHeadsetProvider = this; Connect(); } PlantronicsHeadsetProvider::~PlantronicsHeadsetProvider() { } /* static void PlantronicsLogDeviceEvent(unsigned short deviceID, char *eventStr) { unsigned i, l; l = strlen(eventStr); if (l >= PLANTRONICS_LOG_DEVICE_EVENT_SIZE - 1) { l = PLANTRONICS_LOG_DEVICE_EVENT_SIZE - 2; } i = plantronicsHeadsetProvider->logDeviceEventPos; plantronicsHeadsetProvider->logDeviceEventIds[i] = deviceID; memcpy(plantronicsHeadsetProvider->logDeviceEvents[i], eventStr, l + 1); i = (i < MAX_PLANTRONICS_LOG_DEVICE_EVENTS - 1) ? i + 1 : 0; plantronicsHeadsetProvider->logDeviceEventPos = i; plantronicsHeadsetProvider->user->HeadsetDispatch(plantronicsHeadsetProvider); } */ void PlantronicsHeadsetProvider::eventPut(const void *p, unsigned l) { unsigned n; if (l == 0) { l = strlen((const char *) p) + 1; } n = PLANTRONICS_EVENT_QUEUE_SIZE - eventQueueFill; if (l < n) { memcpy(&eventQueue[eventQueueFill], p, l); eventQueueFill += l; } else { memcpy(&eventQueue[eventQueueFill], p, n); if (l != n) { memcpy(eventQueue, &((const byte *) p)[n], l - n); } eventQueueFill = l - n; } } void PlantronicsHeadsetProvider::eventSend(const void *p, unsigned l) { eventPut(p, l); eventQueuePos = eventQueueFill; user->HeadsetDispatch(this); } void PlantronicsHeadsetProvider::eventGet(void *p, unsigned l) { unsigned n; if (l == 0) { while (eventQueue[eventQueueAck + ((l < PLANTRONICS_EVENT_QUEUE_SIZE - eventQueueAck) ? l : l - PLANTRONICS_EVENT_QUEUE_SIZE)] != '\0') { l++; } l++; } n = PLANTRONICS_EVENT_QUEUE_SIZE - eventQueueAck; if (l < n) { memcpy(p, &eventQueue[eventQueueAck], l); eventQueueAck += l; } else { memcpy(p, &eventQueue[eventQueueAck], n); if (l != n) { memcpy(&((byte *) p)[n], eventQueue, l - n); } eventQueueAck = l - n; } } #define PLANTRONICS_CALL_STATE_CHANGED 0 bool PlantronicsSessionManagerEvents::OnDeviceStateChanged(DeviceStateEventArgs const &args) { char devicePath[PLANTRONICS_DEVICE_PATH_SIZE]; int32_t callId; from_wchar_t(args.devicePath, devicePath, PLANTRONICS_DEVICE_PATH_SIZE); byte b = PLANTRONICS_CALL_STATE_CHANGED; plantronicsHeadsetProvider->eventPut(&b, sizeof(b)); b = DEVICE_EVENT_KIND_UNKNOWN; plantronicsHeadsetProvider->eventPut(&b, sizeof(b)); b = (args.deviceState == DEV_STATE_ADDED) ? CALL_STATE_DEVICE_ARRIVED : CALL_STATE_DEVICE_REMOVED; plantronicsHeadsetProvider->eventPut(&b, sizeof(b)); callId = 0; plantronicsHeadsetProvider->eventPut(&callId, sizeof(callId)); plantronicsHeadsetProvider->eventSend(devicePath, 0); return true; } bool PlantronicsSessionManagerEvents::OnCallStateChanged(CallStateEventArgs const &args) { char devicePath[PLANTRONICS_DEVICE_PATH_SIZE]; from_wchar_t(args.devicePath, devicePath, PLANTRONICS_DEVICE_PATH_SIZE); byte b = PLANTRONICS_CALL_STATE_CHANGED; plantronicsHeadsetProvider->eventPut(&b, sizeof(b)); b = (byte)(args.deviceEventkind); plantronicsHeadsetProvider->eventPut(&b, sizeof(b)); b = (byte)(args.callState); plantronicsHeadsetProvider->eventPut(&b, sizeof(b)); plantronicsHeadsetProvider->eventPut(&args.callId.id, sizeof(args.callId.id)); plantronicsHeadsetProvider->eventSend(devicePath, 0); return true; } bool PlantronicsCallEvents::OnCallStateChanged(CallStateEventArgs const &args) { char devicePath[PLANTRONICS_DEVICE_PATH_SIZE]; from_wchar_t(args.devicePath, devicePath, PLANTRONICS_DEVICE_PATH_SIZE); byte b = PLANTRONICS_CALL_STATE_CHANGED; plantronicsHeadsetProvider->eventPut(&b, sizeof(b)); b = (byte)(args.deviceEventkind); plantronicsHeadsetProvider->eventPut(&b, sizeof(b)); b = (byte)(args.callState); plantronicsHeadsetProvider->eventPut(&b, sizeof(b)); plantronicsHeadsetProvider->eventPut(&args.callId.id, sizeof(args.callId.id)); plantronicsHeadsetProvider->eventSend(devicePath, 0); return true; } void PlantronicsHeadsetProvider::OnDispatch() { char devicePath[PLANTRONICS_DEVICE_PATH_SIZE]; int32_t callId; byte cmd, deviceEventKind, callState; unsigned pos; pos = eventQueuePos; while (eventQueueAck != pos) { eventGet(&cmd, sizeof(cmd)); switch (cmd) { case PLANTRONICS_CALL_STATE_CHANGED: eventGet(&deviceEventKind, sizeof(deviceEventKind)); eventGet(&callState, sizeof(callState)); eventGet(&callId, sizeof(callId)); eventGet(devicePath, 0); CallStateChanged(devicePath, deviceEventKind, callState, callId); break; } } pos = logDeviceEventPos; while (logDeviceEventAck != pos) { ERR(("%s.%u %s", tag, logDeviceEventIds[logDeviceEventAck], logDeviceEvents[logDeviceEventAck])); logDeviceEventAck = (logDeviceEventAck < MAX_PLANTRONICS_LOG_DEVICE_EVENTS - 1) ? logDeviceEventAck + 1 : 0; } } void PlantronicsHeadsetProvider::Initialize(UHeadsetProvider * const user) { this->user = user; } void PlantronicsHeadsetProvider::Connect() { SMResult smRes; int res; #ifdef _WINDOWS plantronicsDll = ::LoadLibrary(L"Spokes.dll"); #else char libraryFilePath[PATH_MAX]; CFURLRef appUrlRef = CFBundleCopyResourceURL(CFBundleGetMainBundle(), CFSTR("Spokes3GSDK"), CFSTR("framework"), NULL); CFStringRef macPath = CFURLCopyFileSystemPath(appUrlRef, kCFURLPOSIXPathStyle); const char *libraryPath = CFStringGetCStringPtr(macPath, CFStringGetSystemEncoding()); _sprintf(libraryFilePath, "%s%s", libraryPath, "/Versions/Current/Spokes3GSDK"); ERR(("%s Library path %s", tag, libraryFilePath)); plantronicsDll = dlopen(libraryPath, RTLD_LOCAL | RTLD_LAZY); CFRelease(appUrlRef); CFRelease(macPath); #endif serviceStarted = true; if (plantronicsDll) { PlantronicsDll_InitLogger(plantronicsDll, NULL, NULL, NULL, NULL, false); res = PlantronicsDll_SetLogLevel(plantronicsDll, LOW); if (res != 0) { ERR(("%s SetLogLevel failed %i", tag, res)); } res = PlantronicsDll_InitSpokesRuntime(plantronicsDll); if (res != 0) { ERR(("%s InitSpokesRuntime failed %i", tag, res)); } else { smRes = PlantronicsDll_getSessionManager(plantronicsDll, &sessionManager); if (smRes != SM_RESULT_SUCCESS) { ERR(("%s getSessionManager failed %s", tag, PlantronicsSMResultToString(smRes))); } else { smRes = sessionManager->registerCallback(&sessionManagerEvents); if (smRes != SM_RESULT_SUCCESS) { ERR(("%s registerCallback for session manager events failed %s", tag, PlantronicsSMResultToString(smRes))); } smRes = sessionManager->registerSession(L"innoPlugin", &session); if (smRes != SM_RESULT_SUCCESS) { ERR(("%s registerSession failed %s", tag, PlantronicsSMResultToString(smRes))); } else { smRes = session->registerCallback(&callEvents); if (smRes != SM_RESULT_SUCCESS) { ERR(("%s registerCallback for call events failed %s", tag, PlantronicsSMResultToString(smRes))); } IUserPreference *userPreference; smRes = sessionManager->getUserPreference(&userPreference); if (smRes != SM_RESULT_SUCCESS) { ERR(("%s getUserPreference failed %s", tag, PlantronicsSMResultToString(smRes))); } else { // set this session as default one for receiving CallRequest events int32_t pluginId; session->getPluginId(&pluginId); userPreference->setDefaultSoftphone(pluginId); } smRes = session->getCallCommand(&callCommand); if (smRes != SM_RESULT_SUCCESS) { ERR(("%s getCallCommand failed %s", tag, PlantronicsSMResultToString(smRes))); } ERR(("%s Library initialized", tag)); } } } } else { ERR(("%s DLL Open failed", tag)); } } void PlantronicsHeadsetProvider::Close() { if (serviceStarted) { if (sessionManager) { if (session) { if (callCommand) { UpdateDevice(activeDeviceId, false); activeDeviceId[0] = '\0'; callCommand->Release(); } session->unregisterCallback(&callEvents); sessionManager->unregisterSession(session); session->Release(); } sessionManager->unregisterCallback(&sessionManagerEvents); } if (plantronicsDll) { PlantronicsDll_ShutDownSpokesRuntime(plantronicsDll); PlantronicsDll_StopLogger(plantronicsDll); FreeLibrary(plantronicsDll); plantronicsDll = NULL; } } user->HeadsetCloseComplete(this); } unsigned PlantronicsHeadsetProvider::GetCurrentCall(int32_t callId, bool originated, const char * number, const char * name) { unsigned i; i = 0; while ((i < sizeof(calls) / sizeof(calls[0])) && ((calls[i].state == CS_NONE) || (calls[i].id != callId))) { i++; } if (i == sizeof(calls) / sizeof(calls[0])) { i = 0; while ((i < sizeof(calls) / sizeof(calls[0])) && (calls[i].state != CS_NONE)) { i++; } if (i == sizeof(calls) / sizeof(calls[0])) { ERR(("%s %i GetCurrentCall failed, no free call slot %s %s", tag, callId, CallStateToString(calls[0].state), CallStateToString(calls[1].state))); for (i = 0; i < sizeof(calls) / sizeof(calls[0]); i++) { calls[i].state = CS_NONE; } i = 0; } calls[i].id = callId; calls[i].originated = originated; strcpy(calls[i].number, number ? number : ""); strcpy(calls[i].name, name ? name : ""); } return i; } void PlantronicsHeadsetProvider::CallStateChanged(const char *devicePath, byte deviceEventKind, byte callState, int32_t callId) { struct PlantronicsDevice device; wchar_t devicePathW[PLANTRONICS_DEVICE_PATH_SIZE]; wchar_t deviceNameW[PLANTRONICS_DEVICE_NAME_SIZE]; IDevice *dev; SMResult smRes; DMResult dmRes; CMResult cmRes; unsigned i, j, l, currentCall; i = 0; while ((i < deviceCount) && (strcmp(devices[i].devicePath, devicePath) != 0)) { i++; } device.deviceName[0] = '\0'; smRes = session->getActiveDevice(&dev); if (smRes == SM_RESULT_SUCCESS) { dmRes = dev->getDevicePath(devicePathW, PLANTRONICS_DEVICE_PATH_SIZE); if (dmRes != DM_RESULT_SUCCESS) { ERR(("%s CallStateChanged getDevicePath failed %s", tag, PlantronicsDMResultToString(dmRes))); } else { from_wchar_t(devicePathW, device.devicePath, PLANTRONICS_DEVICE_PATH_SIZE); if (strcmp(device.devicePath, devicePath) == 0) { smRes = session->getActiveDeviceName(deviceNameW, sizeof(deviceNameW)); if (smRes != SM_RESULT_SUCCESS) { ERR(("%s CallStateChanged getActiveDeviceName failed %s", tag, PlantronicsSMResultToString(smRes))); } else { from_wchar_t(deviceNameW, (i < deviceCount) ? devices[i].deviceName : device.deviceName, PLANTRONICS_DEVICE_NAME_SIZE); ERR(("%s CallStateChanged getActiveDeviceName %s", tag, (i < deviceCount) ? devices[i].deviceName : device.deviceName)); } } } dev->Release(); } to_wchar_t(devicePath, devicePathW, PLANTRONICS_DEVICE_PATH_SIZE); smRes = sessionManager->getDeviceForPath(devicePathW, &dev); if (smRes != SM_RESULT_SUCCESS) { dev = NULL; ERR(("%s CallStateChanged getDeviceForPath failed %s %s", tag, devicePath, PlantronicsSMResultToString(smRes))); } ERR(("%s %i CallStateChanged %s %s", tag, callId, PlantronicsDeviceEventKindToString((eDeviceEventKind) deviceEventKind), PlantronicsCallStateToString((eCallState) callState))); switch (deviceEventKind) { case DEVICE_EVENT_KIND_DOCKED: break; case DEVICE_EVENT_KIND_UNDOCKED: break; case DEVICE_EVENT_KIND_TALKPRESS: break; case DEVICE_EVENT_KIND_BASEPRESS: break; case DEVICE_EVENT_KIND_IDLE_TALKPRESS: if (i < deviceCount) { ERR(("%s CallStateChanged %s %s", tag, PlantronicsDeviceEventKindToString((eDeviceEventKind) deviceEventKind), KeyToString(KEY_OFFHOOK))); user->HookKeyReceived(devices[i].deviceName, KEY_OFFHOOK); } break; } switch (callState) { case CALL_STATE_DEVICE_ARRIVED: if (i == deviceCount) { device.productID = 0; device.vendorID = VENDOR_ID_PLANTRONICS; if (dev) { device.productID = (unsigned short)(dev->getProductID()); device.vendorID = (unsigned short)(dev->getVendorID()); if (device.deviceName[0] == '\0') { dmRes = dev->getProductName(deviceNameW, sizeof(deviceNameW)); if (dmRes == DM_RESULT_SUCCESS) { from_wchar_t(deviceNameW, device.deviceName, PLANTRONICS_DEVICE_NAME_SIZE); } } } strcpy(device.devicePath, devicePath); ERR(("%s CallStateChanged %s %s %s", tag, PlantronicsCallStateToString((eCallState) callState), device.devicePath, device.deviceName)); l = strlen(device.deviceName); i = deviceCount; while ((i != 0) && (strlen(devices[i - 1].deviceName) < l)) { memcpy(&devices[i], &devices[i - 1], sizeof(struct PlantronicsDevice)); i--; } memcpy(&devices[i], &device, sizeof(struct PlantronicsDevice)); deviceCount++; if (dev && !dev->isAttached()) { if (!dev->attach()) { ERR(("%s attach failed", tag)); } } UpdateDevice(activeDeviceId, true); } break; case CALL_STATE_DEVICE_REMOVED: if (i < deviceCount) { ERR(("%s CallStateChanged %s %s %s", tag, PlantronicsCallStateToString((eCallState) callState), devices[i].devicePath, devices[i].deviceName)); deviceCount--; while (i < deviceCount) { memcpy(&devices[i], &devices[i + 1], sizeof(struct PlantronicsDevice)); i++; } } break; case CALL_STATE_CALL_IN_PROGRESS: currentCall = GetCurrentCall(callId, false, NULL, NULL); calls[currentCall].state = CS_UP; call.setID(callId); if (callCommand) { cmRes = callCommand->answeredCall(&call); if (cmRes != CM_RESULT_SUCCESS) { ERR(("%s answeredCall failed %s", tag, PlantronicsCMResultToString(cmRes))); } } break; case CALL_STATE_ACCEPT_CALL: currentCall = GetCurrentCall(callId, false, NULL, NULL); calls[currentCall].state = CS_UP; if ((i < deviceCount) && calls[currentCall].originated) { ERR(("%s CallStateChanged %s %s", tag, PlantronicsCallStateToString((eCallState) callState), KeyToString(KEY_OFFHOOK))); user->HookKeyReceived(devices[i].deviceName, KEY_OFFHOOK); } break; case CALL_STATE_TERMINATE_CALL: currentCall = GetCurrentCall(callId, false, NULL, NULL); if (calls[currentCall].state != CS_NONE) { calls[currentCall].state = CS_NONE; if ((i < deviceCount) && calls[currentCall].originated) { ERR(("%s CallStateChanged %s %s", tag, PlantronicsCallStateToString((eCallState) callState), KeyToString(KEY_DISC))); user->HookKeyReceived(devices[i].deviceName, KEY_DISC); } } break; case CALL_STATE_HOLD_CALL: currentCall = GetCurrentCall(callId, false, NULL, NULL); calls[currentCall].state = CS_HOLD; if ((i < deviceCount) && calls[currentCall].originated) { j = 0; while ((j < currentCall) && (calls[j].state != CS_UP) && (calls[j].state != CS_HOLD)) { j++; } if (j == currentCall) { ERR(("%s CallStateChanged %s %s", tag, PlantronicsCallStateToString((eCallState) callState), KeyToString(KEY_EHS_FLASH))); user->HookKeyReceived(devices[i].deviceName, KEY_EHS_FLASH); } } break; case CALL_STATE_RESUME_CALL: currentCall = GetCurrentCall(callId, false, NULL, NULL); calls[currentCall].state = CS_UP; if ((i < deviceCount) && calls[currentCall].originated) { j = 0; while ((j < currentCall) && (calls[j].state != CS_UP) && (calls[j].state != CS_HOLD)) { j++; } if (j == currentCall) { ERR(("%s CallStateChanged %s %s", tag, PlantronicsCallStateToString((eCallState) callState), KeyToString(KEY_EHS_FLASH))); user->HookKeyReceived(devices[i].deviceName, KEY_EHS_FLASH); } } break; case CALL_STATE_FLASH: currentCall = GetCurrentCall(callId, false, NULL, NULL); if ((i < deviceCount) && calls[currentCall].originated) { ERR(("%s CallStateChanged %s %s", tag, PlantronicsCallStateToString((eCallState) callState), KeyToString(KEY_EHS_FLASH))); user->HookKeyReceived(devices[i].deviceName, KEY_EHS_FLASH); } break; case CALL_STATE_CALL_ENDED: currentCall = GetCurrentCall(callId, false, NULL, NULL); calls[currentCall].state = CS_NONE; break; case CALL_STATE_REJECT_CALL: currentCall = GetCurrentCall(callId, false, NULL, NULL); if (calls[currentCall].state != CS_NONE) { calls[currentCall].state = CS_NONE; if ((i < deviceCount) && calls[currentCall].originated) { ERR(("%s CallStateChanged %s %s", tag, PlantronicsCallStateToString((eCallState) callState), KeyToString(KEY_DISC))); user->HookKeyReceived(devices[i].deviceName, KEY_DISC); } } break; case CALL_STATE_MUTEON: if (!mute) { mute = true; currentCall = GetCurrentCall(callId, false, NULL, NULL); if ((i < deviceCount) && calls[currentCall].originated) { ERR(("%s CallStateChanged %s %s", tag, PlantronicsCallStateToString((eCallState) callState), KeyToString(KEY_MIC))); user->HookKeyReceived(devices[i].deviceName, KEY_MIC); } } break; case CALL_STATE_MUTEOFF: if (mute) { mute = false; currentCall = GetCurrentCall(callId, false, NULL, NULL); if ((i < deviceCount) && calls[currentCall].originated) { ERR(("%s CallStateChanged %s %s", tag, PlantronicsCallStateToString((eCallState) callState), KeyToString(KEY_MIC))); user->HookKeyReceived(devices[i].deviceName, KEY_MIC); } } break; } if (dev) { dev->Release(); } } void PlantronicsHeadsetProvider::DevicesDetected(unsigned detectedDevices, const char * const *deviceIds, word *vendorIds, word *productIds) { unsigned i; if (!serviceStarted) { i = 0; while ((i < detectedDevices) && (vendorIds[i] != VENDOR_ID_PLANTRONICS) && (vendorIds[i] != VENDOR_ID_POLYCOM) && (vendorIds[i] != VENDOR_ID_PHILLIPS)) { i++; } if (i < detectedDevices) { Connect(); } } } word PlantronicsHeadsetProvider::GetVendorId(const char *deviceId, word *productId) { unsigned i; i = 0; while ((i < deviceCount) && !strstr(deviceId, devices[i].deviceName)) { i++; } if (i == deviceCount) { return 0; } *productId = devices[i].productID; return (devices[i].vendorID); } void PlantronicsHeadsetProvider::SetMicrophoneMute(bool mute) { if (mute != this->mute) { this->mute = mute; UpdateDevice(activeDeviceId, true); } } void PlantronicsHeadsetProvider::StartHookDevice(const char *deviceId) { unsigned l; int j; UpdateDevice(activeDeviceId, false); l = strlen(deviceId); if (l < PLANTRONICS_DEVICE_ID_SIZE) { memcpy(activeDeviceId, deviceId, l + 1); } else { memcpy(activeDeviceId, deviceId, PLANTRONICS_DEVICE_ID_SIZE - 1); activeDeviceId[PLANTRONICS_DEVICE_ID_SIZE - 1] = '\0'; } j = 0; while ((j < deviceCount) && !strstr(deviceId, devices[j].deviceName)) { j++; } if (j < deviceCount) { ERR(("%s StartHookDevice %s", tag, deviceId)); UpdateDevice(activeDeviceId, true); } } void PlantronicsHeadsetProvider::StopHookDevice(const char *deviceId) { UpdateDevice(activeDeviceId, false); activeDeviceId[0] = '\0'; } void PlantronicsHeadsetProvider::UpdateDevice(const char *deviceId, bool active) { CMResult cmRes; int i; i = 0; while ((i < deviceCount) && !strstr(deviceId, devices[i].deviceName)) { i++; } if ((i < deviceCount) && (deviceId[0] != '\0')) { if (callCommand) { cmRes = callCommand->muteCall(active && mute); if (cmRes != CM_RESULT_SUCCESS) { ERR(("%s muteCall failed %s", tag, PlantronicsCMResultToString(cmRes))); } } } } void PlantronicsHeadsetProvider::SendHookKey(const char *deviceId, byte key, byte callId, const char * number, const char * name) { static const char xdigits[] = "0123456789ABCDEF"; char nameX[PLANTRONICS_CALL_NAME_SIZE]; wchar_t nameW[PLANTRONICS_CALL_NAME_SIZE]; CMResult cmRes; unsigned i, j, currentCall; currentCall = GetCurrentCall(callId, true, number, name); ERR(("%s %i SendHookKey %s %s", tag, callId, KeyToString(key), CallStateToString(calls[currentCall].state))); call.setID(callId); contact.setID(0); if (name) { i = 0; j = 0; while (name[i] != '\0') { if (((name[i] >= '0') && (name[i] <= '9')) || ((name[i] >= 'A') && (name[i] <= 'Z')) || ((name[i] >= 'a') && (name[i] <= 'z'))) { nameX[j++] = name[i]; } else { nameX[j++] = '%'; nameX[j++] = xdigits[((byte)(name[i])) >> 4]; nameX[j++] = xdigits[name[i] & 0x0f]; } i++; } nameX[j] = '\0'; to_wchar_t(nameX, nameW, PLANTRONICS_CALL_NAME_SIZE); contact.setName(nameW); } else { contact.setName(NULL); } if (number) { to_wchar_t(number, nameW, PLANTRONICS_CALL_NAME_SIZE); if (strchr(number, '@')) { contact.setPhone(NULL); contact.setSipUri(nameW); } else { contact.setPhone(nameW); contact.setSipUri(NULL); } } else { contact.setPhone(NULL); contact.setSipUri(NULL); } switch (key) { case KEY_ONHOOK: switch (calls[currentCall].state) { case CS_UP: case CS_HOLD: case CS_INCALL_INIT: // (serial close) incall reject by swphone or onhook ack from core calls[currentCall].state = CS_NONE; if (callCommand) { cmRes = callCommand->terminateCall(&call); if (cmRes != CM_RESULT_SUCCESS) { ERR(("%s terminateCall failed %s", tag, PlantronicsCMResultToString(cmRes))); } } break; } break; case KEY_OFFHOOK: switch (calls[currentCall].state) { case CS_INCALL_INIT: // incall answered by swphone calls[currentCall].state = CS_UP; if (callCommand) { cmRes = callCommand->answeredCall(&call); if (cmRes != CM_RESULT_SUCCESS) { ERR(("%s answeredCall failed %s", tag, PlantronicsCMResultToString(cmRes))); } } break; case CS_NONE: calls[currentCall].state = CS_UP; // outcall initiated by swphone. Inform Plantronics my app has an outgoing call if (callCommand) { cmRes = callCommand->outgoingCall(&call, &contact, eAudioRoute::AUDIO_ROUTE_TO_HEADSET); if (cmRes != CM_RESULT_SUCCESS) { ERR(("%s outgoingCall failed %s", tag, PlantronicsCMResultToString(cmRes))); } } break; } break; case KEY_INCALL: switch (calls[currentCall].state) { case CS_NONE: calls[currentCall].state = CS_INCALL_INIT; // inform Plantronics my app has an incoming (ringing) call if (callCommand) { cmRes = callCommand->incomingCall(&call, &contact, RING_TONE_UNKNOWN, eAudioRoute::AUDIO_ROUTE_TO_HEADSET); if (cmRes != CM_RESULT_SUCCESS) { ERR(("%s incomingCall failed %s", tag, PlantronicsCMResultToString(cmRes))); } } break; } break; case KEY_HOLD: switch (calls[currentCall].state) { case CS_UP: calls[currentCall].state = CS_HOLD; if (callCommand) { cmRes = callCommand->holdCall(&call); if (cmRes != CM_RESULT_SUCCESS) { ERR(("%s holdCall failed %s", tag, PlantronicsCMResultToString(cmRes))); } } break; } break; case KEY_RETRIEVE: switch (calls[currentCall].state) { case CS_HOLD: calls[currentCall].state = CS_UP; if (callCommand) { cmRes = callCommand->resumeCall(&call); if (cmRes != CM_RESULT_SUCCESS) { ERR(("%s resumeCall failed %s", tag, PlantronicsCMResultToString(cmRes))); } } break; } break; } } NAMESPACE_END