/**************************************************************************** NAME slcConnectionComplete DESCRIPTION SLC connection has completed RETURNS void */ static void slcConnectionComplete(hfp_link_priority priority, Sink sink, bdaddr* bd_addr) { /* mark as connected */ theSink.profile_data[PROFILE_INDEX(priority)].status.connected = TRUE; /* Another connection made, update number of current connections */ SLC_DEBUG(("SLC: Pro Connected[%x], NoOfDev=%x\n", (int)sink,deviceManagerNumConnectedDevs())) ; /* Enter connected state if applicable */ if(!stateManagerIsConnected()) stateManagerEnterConnectedState(); /* Ensure the underlying ACL is encrypted */ if(theSink.features.EncryptOnSLCEstablishment) ConnectionSmEncrypt( &theSink.task , sink , TRUE ); /* send message to do indicate a stop of the paging process when in connectable state */ if(theSink.paging_in_progress) MessageSend(&theSink.task, EventSysStopPagingInConnState ,0); /* if device initiated connection */ if(!gSlcData.gSlcConnectRemote) { /* If no event is queued send button press */ if(!theSink.gEventQueuedOnConnection) HfpHsButtonPressRequest(priority); /* Continue trying to connect to next device in 0.1 seconds time to allow current device to finish connecting */ MessageSendLater(&theSink.task,EventSysContinueSlcConnectRequest,0,theSink.conf1->timeouts.SecondAGConnectDelayTime_s); } /* Initialise call transfer flag */ gSlcData.gCallTransferInProgress = FALSE ; }
/**************************************************************************** DESCRIPTION sends the current microphone volume to the AG on connection */ void VolumeSendMicrophoneVolume( uint16 pVolume ) { uint8 i; /*if there is a hfp attached - then send the vol change*/ if ( stateManagerIsConnected() ) { /* scan all available profiles and send mic volume if connected */ for(i=0;i<MAX_PROFILES;i++) { /* send mic volume to AG */ HfpVolumeSyncMicrophoneGainRequest ( (i + hfp_primary_link) , (uint8*)&pVolume ) ; } } }
/**************************************************************************** NAME sinkInitiateLNR DESCRIPTION If HFP and connected - issues command If HFP and not connected - connects and issues if not in call If HSP sends button press RETURNS void */ void sinkInitiateLNR ( hfp_link_priority priority ) { CM_DEBUG(("CM: LNR\n")) ; /* if device not connected to any AG initiate a connection */ if (!stateManagerIsConnected() ) { #ifdef ENABLE_AVRCP sinkAvrcpCheckManualConnectReset(NULL); #endif MessageSend ( &theSink.task , EventUsrEstablishSLC , 0 ) ; sinkQueueEvent(EventUsrLastNumberRedial) ; } /* have at least one connection */ else { uint8 Option = hfp_primary_link; CM_DEBUG(("CM: LNR Connected\n")) ; /* for multipoint use there are two options */ if(theSink.MultipointEnable) { /* these being to use separate LNR buttons, one for AG1 and one for AG2 or use one LNR event and dial using the AG that last made an outgoing call */ if(theSink.features.SeparateLNRButtons) { CM_DEBUG(("CM: LNR use separate LNR buttons\n")) ; Option = priority; } /* dial using AG that made last outgoing call */ else { CM_DEBUG(("CM: LNR use last outgoing AG = %d\n",theSink.last_outgoing_ag)) ; Option = theSink.last_outgoing_ag; } } CM_DEBUG(("CM: LNR on AG %d\n",Option)) ; /* perform last number redial */ HfpDialLastNumberRequest(Option); } }
/**************************************************************************** NAME sinkInitiateVoiceDial DESCRIPTION If HFP and connected - issues command If HFP and not connected - connects and issues if not in call If HSP sends button press RETURNS void */ void sinkInitiateVoiceDial ( hfp_link_priority priority ) { CM_DEBUG(("CM: VD\n")) ; if (!stateManagerIsConnected() ) { #ifdef ENABLE_AVRCP sinkAvrcpCheckManualConnectReset(NULL); #endif MessageSend ( &theSink.task , EventUsrEstablishSLC , 0 ) ; sinkQueueEvent( EventUsrInitateVoiceDial ) ; theSink.VoiceRecognitionIsActive = hfp_invalid_link ; } else { uint8 Option = hfp_primary_link; CM_DEBUG(("CM: VD Connected\n")) ; /* for multipoint use there are two options */ if(theSink.MultipointEnable) { /* these being to use separate VD buttons, one for AG1 and one for AG2 or use one VD event and dial using the AG that last made an outgoing call */ if(theSink.features.SeparateVDButtons) { Option = priority; } /* voice dial using AG that made last outgoing call */ else { Option = theSink.last_outgoing_ag; } } CM_DEBUG(("CM: VoiceDial on %d\n",Option)) ; HfpVoiceRecognitionEnableRequest(Option, TRUE); theSink.VoiceRecognitionIsActive = Option; } }
void VolumeSendAndSetHeadsetVolume( uint16 pNewVolume , bool pPlayTone, hfp_link_priority priority ) { bool lOverideMute = theHeadset.features.OverideMute ; /* ensure profile is connected before changing volume */ if(priority) { /*if there is a hfp attached - then send the vol change, but only if not muted and mute overide is not in effect*/ if ( (stateManagerIsConnected() && (!theHeadset.gMuted ))|| ( lOverideMute )) { HfpVolumeSyncSpeakerGainRequest ( priority , (uint8*)&pNewVolume ) ; } VOL_DEBUG(("VOL: SEND and %x",(unsigned int) priority)) ; #if 1 VolumeSetHeadsetVolume( pNewVolume , pPlayTone, priority, TRUE, dir ); #else VolumeSetHeadsetVolume( pNewVolume , pPlayTone, priority, TRUE ); #endif } }
/* Dial the first entry in the phonebook */ void pbapDialPhoneBook( uint8 phonebook ) { if(theHeadset.pbap_dial_state != pbapc_dialling) { if (!stateManagerIsConnected() ) { #ifdef ENABLE_AVRCP headsetAvrcpCheckManualConnectReset(NULL); #endif PBAP_DEBUG(("Pbap dial, Connect the HFP profile first\n")); MessageSend ( &theHeadset.task , EventEstablishSLC , 0 ) ; switch(phonebook) { case pbap_ich: headsetQueueEvent( EventPbapDialIch ) ; break; case pbap_mch: headsetQueueEvent( EventPbapDialMch ) ; break; default: break; } } else { #ifdef ActiveRubiASR #if 0 if(strcmp(TTS_text , "")) { PBAP_DEBUG(("Pbap : Rubi > UnloadRubiEngine***\n")); TTSTerminate(); AudioDisconnect(); /* clear sco_sink value to indicate no routed audio */ theHeadset.sco_sink = 0; UnloadRubidiumEngine(); memset(TTS_text, 0, sizeof(TTS_text)); } #endif if(theHeadset.TTS_ASR_Playing) { PBAP_DEBUG(("Pbap : Rubi > UnloadRubiEngine***\n")); TTSTerminate(); AudioDisconnect(); /* clear sco_sink value to indicate no routed audio */ theHeadset.sco_sink = 0; UnloadRubidiumEngine(); theHeadset.TTS_ASR_Playing = false; } #endif pbapDial(phonebook); } theHeadset.pbap_dial_state = pbapc_dialling; } }
void slcContinueEstablishSLCRequest( void ) { SLC_DEBUG(("SLC: ContSLCReq, listId = %d\n",gSlcData.gListID)) ; /* is multipoint enabled ?, if so see if necessary to connect further devices, also check for non multipoint and no devices, otherwise exit */ if((!gSlcData.gSlcConnectRemote) && (deviceManagerCanConnect()) && #ifdef ENABLE_PARTYMODE (!((theSink.PartyModeEnabled)&&(theSink.features.PartyMode)))&& #endif (stateManagerGetState() != deviceLimbo)) { if(slcDetermineConnectAction() & AR_Rssi) { /* Connect next result*/ inquiryConnectNext(); } else { /* if there are more devices to connect to then continue */ if(slcGetNextListID()) { SLC_DEBUG(("SLC: PDL entry available for connection , listId = %d\n",gSlcData.gListID)) ; /* attempt to connect to next item in list */ slcAttemptConnection(); } /* otherwise check whether any connection attempt was successful, if not check feature to automatically enter pairing mode */ else if(theSink.features.EnterPairingModeOnFailureToConnect && !stateManagerIsConnected()) { SLC_DEBUG(("SLC: Failed to Connect to anything, enter pairing mode\n")) ; /* enter pairing mode */ MessageSend(&theSink.task, EventUsrEnterPairing, 0); /* now allow role switches */ theSink.rundata->connection_in_progress = FALSE; } /* all connection attempts now complete, allow role switching */ else { /* now allow role switches */ theSink.rundata->connection_in_progress = FALSE; } } } /* Need to handle this case to stop RSSI when all devs connected */ else if(slcDetermineConnectAction() & AR_Rssi) { /* All done, stop inquiring */ if(theSink.inquiry.action != rssi_subwoofer) inquiryStop(); /* set flag to block role switch requests until all connections are complete to avoid unneccesary switching */ theSink.rundata->connection_in_progress = FALSE; } /* connections complete, allow role switches to commence if necessary */ else { /* set flag to block role switch requests until all connections are complete to avoid unneccesary switching */ theSink.rundata->connection_in_progress = FALSE; } /* reset connection via remote ag instead of device flag */ gSlcData.gSlcConnectRemote = FALSE; SLC_DEBUG(("SLC: StopReq\n")) ; }
/**************************************************************************** NAME sinkHandleSlcConnectCfm DESCRIPTION Confirmation that the SLC has been established (or not). RETURNS void */ bool sinkHandleSlcConnectCfm( const HFP_SLC_CONNECT_CFM_T *cfm ) { sink_attributes attributes; bool lResult = FALSE; #ifdef ENABLE_PEER inquiry_result_t* connecting_device = inquiryGetConnectingDevice(); #endif deviceManagerGetDefaultAttributes(&attributes, FALSE); (void)deviceManagerGetAttributes(&attributes, &cfm->bd_addr); /* cancel any link loss reminders */ MessageCancelAll(&theSink.task , EventSysLinkLoss ); /* Check the status of the SLC attempt */ if (cfm->status == hfp_connect_success) { SLC_DEBUG(("SLC: ConnCfm - Success\n")) ; lResult = TRUE ; /* update the profile volume level */ theSink.profile_data[PROFILE_INDEX(cfm->priority)].audio.gSMVolumeLevel = attributes.hfp.volume; /* Handle new connection setup */ slcConnectionComplete(cfm->priority, cfm->sink, (bdaddr *)&cfm->bd_addr); /* Handle common setup for new SLC/link loss */ slcConnectionSetup(cfm->priority, cfm->sink, (bdaddr *)&cfm->bd_addr); /* Record the position of the device in the PDL - prevents reconnection later */ theSink.profile_data[PROFILE_INDEX(cfm->priority)].status.list_id = deviceManagerSetPriority((bdaddr *)&cfm->bd_addr); #ifdef ENABLE_PEER /* If RSSI pairing, check inquiry results for A2DP support */ if (theSink.inquiry.action == rssi_pairing) { if ((connecting_device != NULL) && BdaddrIsSame(&connecting_device->bd_addr, &cfm->bd_addr) && (connecting_device->remote_profiles & profile_a2dp)) { attributes.profiles |= sink_a2dp; } } #endif /* Make sure we store this device */ attributes.profiles |= sink_hfp; deviceManagerStoreAttributes(&attributes, &cfm->bd_addr); /* if rssi pairing check to see if need to cancel rssi pairing or not */ if(theSink.inquiry.action == rssi_pairing) { /* if rssi pairing has completed and the device being connected currently doesn't support A2DP, then stop it progressing further */ if(!((theSink.features.PairIfPDLLessThan) && ( ConnectionTrustedDeviceListSize() < theSink.features.PairIfPDLLessThan ))) { #ifdef ENABLE_PEER if(!((connecting_device != NULL) && BdaddrIsSame(&connecting_device->bd_addr, &cfm->bd_addr) && (connecting_device->remote_profiles & profile_a2dp))) #endif { inquiryStop(); } } } /* Disable A2dp link loss management if connected on remote device */ if( theSink.a2dp_link_data && (theSink.a2dp_link_data->connected[a2dp_primary]) && BdaddrIsSame(&cfm->bd_addr, &theSink.a2dp_link_data->bd_addr[a2dp_primary]) ) { A2dpDeviceManageLinkloss(theSink.a2dp_link_data->device_id[a2dp_primary], FALSE); } else if( theSink.a2dp_link_data && (theSink.a2dp_link_data->connected[a2dp_secondary]) && BdaddrIsSame(&cfm->bd_addr, &theSink.a2dp_link_data->bd_addr[a2dp_secondary]) ) { A2dpDeviceManageLinkloss(theSink.a2dp_link_data->device_id[a2dp_secondary], FALSE); } /* Auto answer call if ringing - only answer the incoming call if its on the connecting AG */ if ( (theSink.features.AutoAnswerOnConnect) && (HfpLinkPriorityFromCallState(hfp_call_state_incoming) == cfm->priority) && (stateManagerGetState() < deviceActiveCallSCO) ) { MessageSend (&theSink.task , EventUsrAnswer , 0 ) ; SLC_DEBUG(("SLC: AutoAnswer triggered\n")) ; } } else { SLC_DEBUG(("SLC: ConnCfm - Fail\n")) ; /* a connection timeout will arrive here, need to report fail for multipoint connections also such that a link loss retry will be performed */ if(!stateManagerIsConnected() || theSink.MultipointEnable) { /* Update local state to reflect this */ slcConnectFail(); } } /* if using multipoint and both devices are connected disable connectable */ if((theSink.MultipointEnable) && (deviceManagerNumConnectedDevs() == MAX_MULTIPOINT_CONNECTIONS)) { SLC_DEBUG(("SLC: disable Conn \n" )); MessageCancelAll(&theSink.task, EventSysConnectableTimeout); #ifdef ENABLE_SUBWOOFER if(SwatGetSignallingSink(theSink.rundata->subwoofer.dev_id)) { sinkDisableConnectable(); } #else sinkDisableConnectable(); #endif } SLC_DEBUG(("SLC: Connect A2DP? En=%d att=%d\n",theSink.features.EnableA2dpStreaming,attributes.profiles)) ; /* if the AG supports A2DP profile attempt to connect to it if auto reconnect is enabled */ if ((theSink.features.EnableA2dpStreaming) && ((!cfm->priority)||(cfm->status == hfp_connect_success) || (cfm->status == hfp_connect_sdp_fail) || (cfm->status == hfp_connect_rejected)) && ((slcDetermineConnectAction() & AR_Rssi)||(attributes.profiles & sink_a2dp)) && ((slcDetermineConnectAction() & AR_Rssi)||(stateManagerGetState()!=deviceConnDiscoverable))) { SLC_DEBUG(("SLC: Connecting A2DP Remote %x\n",gSlcData.gSlcConnectRemote)) ; /* attempt connection to device supporting A2DP */ theSink.a2dp_link_data->remote_connection = gSlcData.gSlcConnectRemote; A2dpSignallingConnectRequest((bdaddr *)&cfm->bd_addr); MessageCancelFirst(&theSink.task, EventSysContinueSlcConnectRequest); /* if rssi pairing check to see if need to cancel rssi pairing or not */ if(theSink.inquiry.action == rssi_pairing) { /* if rssi pairing has completed then stop it progressing further */ if(!((theSink.features.PairIfPDLLessThan)&&( ConnectionTrustedDeviceListSize() < theSink.features.PairIfPDLLessThan ))) { #ifdef ENABLE_PEER if(!((connecting_device != NULL) && BdaddrIsSame(&connecting_device->bd_addr, &cfm->bd_addr) && (connecting_device->remote_profiles & profile_a2dp))) #endif { inquiryStop(); } } } } else { /* reset connection via remote ag instead of device flag */ gSlcData.gSlcConnectRemote = FALSE; } #ifdef ENABLE_MAPC mapcMasConnectRequest((bdaddr *)&cfm->bd_addr); #endif return lResult ; }
/**************************************************************************** NAME sinkHandleSlcDisconnectInd DESCRIPTION Indication that the SLC has been released. RETURNS void */ void sinkHandleSlcDisconnectInd( const HFP_SLC_DISCONNECT_IND_T *ind ) { conn_mask mask = deviceManagerProfilesConnected(&ind->bd_addr); SLC_DEBUG(("SLC: slc DiscInd for index %d, status = %d\n",ind->priority, ind->status)) ; if(ind->status == hfp_disconnect_success || ind->status == hfp_disconnect_link_loss || ind->status == hfp_disconnect_abnormally) { /* store volume info */ deviceManagerUpdateAttributes(&ind->bd_addr, sink_hfp, ind->priority, 0); /* Sends the indication to the device manager to send an event out if a device has disconnected*/ deviceManagerDeviceDisconnectedInd(&ind->bd_addr); /*if the device is off then this is disconnect as part of the power off cycle - dont re-enable connectable*/ if ( stateManagerGetState() != deviceLimbo) { /* Enable A2dp link loss management if connected on remote device */ if( theSink.a2dp_link_data && (theSink.a2dp_link_data->connected[a2dp_primary]) && BdaddrIsSame(&ind->bd_addr, &theSink.a2dp_link_data->bd_addr[a2dp_primary]) ) { A2dpDeviceManageLinkloss(theSink.a2dp_link_data->device_id[a2dp_primary], TRUE); } else if( theSink.a2dp_link_data && (theSink.a2dp_link_data->connected[a2dp_secondary]) && BdaddrIsSame(&ind->bd_addr, &theSink.a2dp_link_data->bd_addr[a2dp_secondary]) ) { A2dpDeviceManageLinkloss(theSink.a2dp_link_data->device_id[a2dp_secondary], TRUE); } /* Kick role checking now a device has disconnected */ linkPolicyCheckRoles(); /* at least one device disconnected, re-enable connectable for another 60 seconds */ sinkEnableMultipointConnectable(); } /*a disconnect in active call state is a call transfer*/ if ( (stateManagerGetState() == deviceActiveCallSCO) || (stateManagerGetState() == deviceActiveCallNoSCO) ) { gSlcData.gCallTransferInProgress = TRUE ; } else { gSlcData.gCallTransferInProgress = FALSE ; } /* if not a link loss reset the last outgoing AG as AG1 will no longer exist now */ theSink.last_outgoing_ag = hfp_primary_link; /* reset the list id of the device just dropped */ theSink.profile_data[PROFILE_INDEX(ind->priority)].status.list_id = INVALID_LIST_ID; /* if device has now disconnected all profiles, mark as disconnected */ if((ind->status != hfp_disconnect_link_loss)&&(!(mask & conn_hfp))) theSink.profile_data[PROFILE_INDEX(ind->priority)].status.connected = FALSE; /* If primary disconnected */ if(ind->priority == hfp_primary_link) { /* ...and we have a secondary link it will be promoted to primary */ if(theSink.profile_data[PROFILE_INDEX(hfp_secondary_link)].status.list_id != INVALID_LIST_ID) { /* Block copy secondary data to primary location */ theSink.profile_data[PROFILE_INDEX(hfp_primary_link)] = theSink.profile_data[PROFILE_INDEX(hfp_secondary_link)]; /* Secondary link no longer exists, set it to invalid */ theSink.profile_data[PROFILE_INDEX(hfp_secondary_link)].status.list_id = INVALID_LIST_ID; } } /* send event slc disconnected only if the status of the indication is success or link loss indication */ MessageSend(&theSink.task , ((ind->status == hfp_disconnect_link_loss) ? EventSysReconnectFailed : EventSysSLCDisconnected) , 0) ; } /*if the device is off then this is disconnect as part of the power off cycle, otherwise check whether device needs to be made connectable */ if ( stateManagerGetState() != deviceLimbo) { /* if the device state still shows connected and there are no profiles currently connected then update the device state to reflect the change of connections */ if ((stateManagerIsConnected()) && (!deviceManagerNumConnectedDevs())) { stateManagerEnterConnectableState( FALSE ) ; } } }