/**************************************************************************** NAME hfpHandleCmerAtAck DESCRIPTION Called when we receive OK/ ERROR in response to the AT+CMER cmd. If we're not getting call hold params from the AG then the SLC is complete. RETURNS void */ void hfpHandleCmerAtAck(hfp_link_data* link, hfp_lib_status status) { if (hfpSlcCheckAtAck(link, status)) { /* If TWC not enabled this is the last AT command in the SLC establishment */ if (!hfFeatureEnabled(HFP_THREE_WAY_CALLING) || !agFeatureEnabled(link, AG_THREE_WAY_CALLING)) hfpSendSlcConnectCfmToApp(link, NULL, hfp_connect_success); } }
/**************************************************************************** NAME hfpHandleRfcommConnectCfm DESCRIPTION Outcome of the RFCOMM connect request or response. RETURNS void */ void hfpHandleRfcommConnectCfm(HFP *hfp, const CL_RFCOMM_CONNECT_CFM_T *cfm) { /* Check the status of the rfcomm connect cfm */ if (cfm->status == rfcomm_connect_success) { /* Store the sink */ hfp->sink = cfm->sink; /* RFCOMM connection is up! Check which profile is supported by this task */ if (supportedProfileIsHsp(hfp->hfpSupportedProfile)) { hfp->agSupportedProfile = hfp_headset_profile; /* HSP supported - SLC is up so tell the app */ hfpSendSlcConnectCfmToApp(hfp_connect_success, hfp); } else if (supportedProfileIsHfp(hfp->hfpSupportedProfile)) { /* Initiate SLC establishment */ MessageSend(&hfp->task, HFP_INTERNAL_AT_BRSF_REQ, 0); } else { /* This should never happen */ HFP_DEBUG(("Unhandled profile type 0x%x\n", hfp->hfpSupportedProfile)); } /* Check for data in the buffer */ hfpHandleReceivedData(hfp, StreamSourceFromSink(cfm->sink)); } else { /* RFCOMM connect failed - Tell the app. */ hfpSendSlcConnectCfmToApp(convertRfcommConnectStatus(cfm->status), hfp); } /* Free the rfcomm lock */ hfp->rfcomm_lock = FALSE; }
/**************************************************************************** NAME hfpHandleRfcommDisconnectInd DESCRIPTION Indication that the RFCOMM connection has been disconnected. RETURNS void */ void hfpHandleRfcommDisconnectInd(HFP *hfp, const CL_RFCOMM_DISCONNECT_IND_T *ind) { if (hfp->state == hfpSlcConnecting) { /* Rfcomm connection has been shutdown during the SLC connection process */ /* Cancel the AT response timeout message because we'll have no more AT cmds being sent */ (void) MessageCancelAll(&hfp->task, HFP_INTERNAL_WAIT_AT_TIMEOUT_IND); /* Report a failed connect attempt to app */ hfpSendSlcConnectCfmToApp(hfp_connect_slc_failed, hfp); } else { /* Convert the rfc disconnect status into its hfp counterpart and inform library */ MAKE_HFP_MESSAGE(HFP_INTERNAL_SLC_DISCONNECT_IND); message->status = convertRfcommDisconnectStatus(ind->status); MessageSend(&hfp->task, HFP_INTERNAL_SLC_DISCONNECT_IND, message); } }
/**************************************************************************** NAME hfpHandleDisconnectRequest DESCRIPTION We're in the right state and have received a disconnect request, handle it here. RETURNS void */ void hfpHandleDisconnectRequest(hfp_link_data* link) { if (link->audio_sink) { MAKE_HFP_MESSAGE(HFP_INTERNAL_SLC_DISCONNECT_REQ); message->link = link; /* If we have a SCO/eSCO active need to tear that down first */ hfpHandleAudioDisconnectReq(link); /* Queue up the SLC disconnect message */ MessageSendConditionally(&theHfp->task, HFP_INTERNAL_SLC_DISCONNECT_REQ, message, (uint16 *) &link->audio_sink); /*lint !e740 */ } else { /* If recovering from link loss or timed out we need to be sure to force disconnect */ if (link->ag_link_loss_state == hfp_link_loss_recovery || link->ag_link_loss_state == hfp_link_loss_timeout) { /* Link was recovering from link loss, stop the procedure */ MessageId message_id = hfpGetLinkTimeoutMessage(link, HFP_RFCOMM_LINK_LOSS_TIMEOUT_LINK_0_IND); MessageCancelFirst(&theHfp->task, message_id); hfpHandleRfcommLinkLossAbort(link, TRUE); } /* Either no link loss or we aborted link loss recovery. Now bring down the link. */ else { /* Can only get here from searching/outgoing/incoming/connected/complete */ if(link->ag_slc_state == hfp_slc_searching) { /* Notify application of connection failure (link will be reset so SDP results ignored) */ hfpSendSlcConnectCfmToApp(link, NULL, hfp_connect_sdp_fail); } else { /* Request the connection lib aborts/disconnects the RFCOMM connection */ ConnectionRfcommDisconnectRequest(&theHfp->task, hfpGetLinkSink(link)); } } } }
/* Helper function to try an HSP search if HFP failed */ static void hfpSdpRetry(hfp_link_data* link) { hfp_service_data* service; /* Mask out the profile that just failed */ link->ag_profiles_to_try &= ~link->service->profile; /* Get whichever profile is left to try */ service = hfpGetServiceFromProfile(link->ag_profiles_to_try); if(service) { bdaddr bd_addr; /* Ignore the result, link must have a bd_addr */ (void)hfpGetLinkBdaddr(link, &bd_addr); /* Try the next service... */ hfpGetProfileServerChannel(link, service, &bd_addr); return; } /* Nothing more to do, SLC has failed */ hfpSendSlcConnectCfmToApp(link, NULL, hfp_connect_sdp_fail); }
/**************************************************************************** NAME hfpHandleServiceSearchAttributeCfm DESCRIPTION Service search has completed, check it has succeeded and get the required attrubutes from the returned list. RETURNS void */ void hfpHandleServiceSearchAttributeCfm(const CL_SDP_SERVICE_SEARCH_ATTRIBUTE_CFM_T *cfm) { /* Get the link from the bdaddr */ hfp_link_data* link = hfpGetLinkFromBdaddr(&cfm->bd_addr); /* The link may have been pulled from under us by an early disconnect request. If this has happened ignore SDP result */ if(link) { /* Check the outcome of the service search */ if (cfm->status == sdp_response_success) { uint16 sdp_data = 0; if(link->ag_slc_state == hfp_slc_searching) { if (getRfcommChannelNumber(cfm->attributes, cfm->attributes+cfm->size_attributes, &sdp_data)) { /* We have an rfcomm channel we can proceed with the connection establishment */ hfpSetLinkSlcState(link, hfp_slc_outgoing); /* Use the local channel to specify the security requirements for this connection */ ConnectionRfcommConnectRequest(&theHfp->task, &cfm->bd_addr, link->service->rfc_server_channel, sdp_data, 0); } else { /* Received unexpected data. Should never happen since we're issuing the search and should know what we're looking. However, if it does, treat as SDP failure. */ hfpSdpRetry(link); } } else if(link->ag_slc_state == hfp_slc_connected) { /* If SLC is up then this must be a features request */ if (getHfpAgSupportedFeatures(cfm->attributes, cfm->attributes+cfm->size_attributes, &sdp_data)) { /* Send an internal message with the supported features. */ hfpHandleSupportedFeaturesNotification(link, sdp_data); } /* else { Received unexpected data. Should never happen since we're issuing the search and should know what we're looking. However, if it does, just ignore since SLC setup is already being continued. } */ } } else { /* RFCOMM channel search failed, tell the application */ if(link->ag_slc_state == hfp_slc_searching) { if (cfm->status == sdp_no_response_data) { /* Retry next profile if possible... */ hfpSdpRetry(link); } else { /* Generic fail by default */ hfp_connect_status status = hfp_connect_failed; /* Was it a page timeout? */ if (cfm->status == sdp_connection_error) status = hfp_connect_timeout; /* Tell the app */ hfpSendSlcConnectCfmToApp(link, NULL, status); } } } } }
/**************************************************************************** NAME hfpHandleCallHoldSupportInd DESCRIPTION Call hold parameters received and parsed. RETURNS void */ static void hfpHandleCallHoldSupportInd(Task link_ptr) { hfp_link_data* link = (hfp_link_data*)link_ptr; /* Inform the app the SLC has been established */ hfpSendSlcConnectCfmToApp(link, NULL, hfp_connect_success); }
/**************************************************************************** NAME hfpHandleServiceSearchAttributeCfm DESCRIPTION Service search has completed, check it has succeeded and get the required attrubutes from the returned list. RETURNS void */ void hfpHandleServiceSearchAttributeCfm(HFP *hfp, const CL_SDP_SERVICE_SEARCH_ATTRIBUTE_CFM_T *cfm) { /* Check the outcome of the service search */ if (cfm->status == sdp_response_success) { uint16 sdp_data = 0; switch ( hfp->sdp_search_mode ) { case hfp_sdp_search_rfcomm_channel: if (getRfcommChannelNumber(cfm->attributes, cfm->attributes+cfm->size_attributes, &sdp_data)) { /* We have an rfcomm channel we can proceed with the connection establishment */ HFP_INTERNAL_RFCOMM_CONNECT_REQ_T message ; message.addr = cfm->bd_addr; message.rfc_channel = sdp_data; if (hfp->state == hfpSlcConnecting) hfpHandleRfcommConnectRequest(hfp, &message); } else { /* Received unexpected data. Should never happen since we're issuing the search and should know what we're looking. However, if it does, assume a failure has occurred as below. */ /* Tell the app the connection attempt has failed. */ hfpSendSlcConnectCfmToApp(hfp_connect_sdp_fail, hfp); } break; case hfp_sdp_search_profile_version: if (getHfpAgProfileVersion(cfm->attributes, cfm->attributes+cfm->size_attributes, &sdp_data)) { /* Obtained version of HFP running on AG */ hfp->agSupportedProfile = (hfp_profile)sdp_data; if( supportedProfileIsHfp15(hfp->agSupportedProfile) ) { hfpSendRemoteAGProfileVerIndToApp(hfp->agSupportedProfile, hfp); } } else { /* Received unexpected data. Should never happen since we're issuing the search and should know what we're looking. However, if it does, assume a failure has occurred as below. */ /* Incorrect response from AG, so assume it's running HFP v1.0 */ hfp->agSupportedProfile = hfp_handsfree_profile; } break; case hfp_sdp_search_supported_features: if (getHfpAgSupportedFeatures(cfm->attributes, cfm->attributes+cfm->size_attributes, &sdp_data)) { /* Send an internal message with the supported features. */ hfpHandleSupportedFeaturesNotification(hfp, sdp_data); } else { /* Received unexpected data. Should never happen since we're issuing the search and should know what we're looking. However, if it does, just ignore since SLC setup is already being continued. */ } break; default: /* else ... We have received data we don't know what to do with. This shouldn't happen since we're issuing the search and should know what we're looking for so for the moment just ignore. */ break; } } else { switch ( hfp->sdp_search_mode ) { case hfp_sdp_search_rfcomm_channel: /* A RFCOMM connection will not exist at this point */ if (cfm->status == sdp_no_response_data) { /* Tell the app the connection attempt has failed. */ hfpSendSlcConnectCfmToApp(hfp_connect_sdp_fail, hfp); } else if (cfm->status == sdp_connection_error) { /* If it was a page timeout the client would like to know */ hfpSendSlcConnectCfmToApp(hfp_connect_timeout, hfp); } else { /* All other sdp fails */ hfpSendSlcConnectCfmToApp(hfp_connect_failed, hfp); } break; case hfp_sdp_search_profile_version: /* No response from AG, so assume it's running HFP v1.0 */ hfp->agSupportedProfile = hfp_handsfree_profile; break; case hfp_sdp_search_supported_features: /* Just ignore. SLC setup is already being continued */ break; default: /* else ... We have received data we don't know what to do with. This shouldn't happen since we're issuing the search and should know what we're looking for so for the moment just ignore. */ break; } } /* Reset sdp search mode */ hfp->sdp_search_mode = hfp_sdp_search_none; }