/**************************************************************************** NAME slcConnectionSetup DESCRIPTION Perform link setup for a given SLC RETURNS void */ static void slcConnectionSetup(hfp_link_priority priority, Sink sink, bdaddr* bd_addr) { uint16 priorityIdx = PROFILE_INDEX(priority); /* Set timeout to 5 seconds */ ConnectionSetLinkSupervisionTimeout(sink, SINK_LINK_SUPERVISION_TIMEOUT); /* Send our link policy settings */ linkPolicyUseHfpSettings(priority, sink); /* Send a delayed message to request a role indication and make necessary changes as appropriate */ MessageCancelFirst(&theSink.task , EventSysCheckRole); MessageSendConditionally (&theSink.task , EventSysCheckRole , NULL , &theSink.rundata->connection_in_progress ); #ifdef ENABLE_PBAP /* Connect the PBAP link of this device */ pbapConnect(priority); #endif /* Sync volume level and mute settings with AG */ VolumeSendAndSetHeadsetVolume(theSink.profile_data[priorityIdx].audio.gSMVolumeLevel ,FALSE , priority) ; /* Enable +CLIP from AG if using Audio Prompt numbers/names or display, always request if using display */ #if !defined(ENABLE_DISPLAY) if(theSink.features.VoicePromptNumbers) #endif HfpCallerIdEnableRequest(priority, TRUE); /* HS uses call waiting indications when AG SCO is not routed */ HfpCallWaitingEnableRequest(priority, TRUE); /* Attempt to pull the audio across if not already present, delay by 5 seconds to prevent a race condition occuring with certain phones */ MessageSendLater ( &theSink.task , EventSysCheckForAudioTransfer , 0 , 5000 ) ; /* Send different event if first connection since power on - allows different LED pattern */ if((theSink.features.UseDiffConnectedEventAtPowerOn)&&(theSink.powerup_no_connection)) MessageSend (&theSink.task , EventSysSLCConnectedAfterPowerOn , NULL ); else MessageSend (&theSink.task , EventSysSLCConnected , NULL ); /* Reset the flag - first connection indicated */ theSink.powerup_no_connection = FALSE; }
/**************************************************************************** NAME handleClRole DESCRIPTION Checks the current role. */ static void handleClRole(devInstanceTaskData *inst, hci_role role, hci_status status, bool role_switch) { Sink sink = 0; if (inst->a2dp) sink = inst->a2dp_sig_sink; else if (inst->aghfp_sink) sink = inst->aghfp_sink; /* store the current role */ if ((status == hci_success) && (inst->role != role)) { inst->role = role; DEBUG_CL((" Current role: inst[0x%x] role:[%d]\n", (uint16)inst, inst->role)); if (sink) { if (inst->role == hci_role_master) { /* Set link supervision timeout 5 seconds */ ConnectionSetLinkSupervisionTimeout(sink, 0x1F80); } } } }
/**************************************************************************** NAME linkPolicyHandleRoleCfm DESCRIPTION this is a function checks the returned role of the device and makes the decision of whether to change it or not, if it needs changing it sends a role change reuest RETURNS void */ void linkPolicyHandleRoleCfm(CL_DM_ROLE_CFM_T *cfm) { hci_role requiredRole = hci_role_dont_care; LP_DEBUG(("RoleConfirm, status=%u sink=0x%X role=%s\n", cfm->status, (unsigned int)cfm->sink, (cfm->role == hci_role_master) ? "master" : "slave")); /* ensure role read successfully */ if ((cfm->status == hci_success)&&(!theSink.features.DisableRoleSwitching)) { /* when multipoint enabled connect as master, this can be switched to slave later on if required when only 1 ag is connected */ if((theSink.MultipointEnable) && (linkPolicyNumberPhysicalConnections() > 1)) { #if defined ENABLE_PEER uint16 priority; if (getA2dpIndexFromSink(cfm->sink, &priority) && (theSink.a2dp_link_data->peer_device[priority] == remote_device_peer)) { if (A2dpMediaGetRole(theSink.a2dp_link_data->device_id[priority], theSink.a2dp_link_data->stream_id[priority]) == a2dp_source) { LP_DEBUG(("LP: Multipoint: Peer (relaying), require Master role\n")) ; requiredRole = hci_role_master; /* Set the link supervision timeout as the role switch will have reset it back to firmware defaults */ ConnectionSetLinkSupervisionTimeout(cfm->sink, SINK_LINK_SUPERVISION_TIMEOUT); } else { LP_DEBUG(("LP: Multipoint: Peer, don't change role\n")) ; requiredRole = hci_role_dont_care; } } else #endif { #if defined ENABLE_PEER && defined PEER_SCATTERNET_DEBUG /* Scatternet debugging only */ if (getA2dpIndexFromSink(cfm->sink, &priority) && theSink.a2dp_link_data->invert_ag_role[priority]) { LP_DEBUG(("LP: Multipoint: Non-peer, require Slave role (inverted)\n")) ; requiredRole = hci_role_slave; } else #endif { LP_DEBUG(("LP: Multipoint: Non-peer, require Master role\n")) ; requiredRole = hci_role_master; /* Set the link supervision timeout as the role switch will have reset it back to firmware defaults */ ConnectionSetLinkSupervisionTimeout(cfm->sink, SINK_LINK_SUPERVISION_TIMEOUT); } } } #ifdef ENABLE_SUBWOOFER /* when a sub woofer is in use the sink app needs to be master of all links to maintain stable connections */ else if((cfm->status == hci_success)&&(SwatGetSignallingSink(theSink.rundata->subwoofer.dev_id))) { LP_DEBUG(("LP: Subwoofer, require Master role\n")) ; requiredRole = hci_role_master; /* Restore the Set link supervision timeout to 1 second as this is reset after a role change */ ConnectionSetLinkSupervisionTimeout(SwatGetSignallingSink(theSink.rundata->subwoofer.dev_id), SUBWOOFER_LINK_SUPERVISION_TIMEOUT); } #endif /* non multipoint case, device needs to be slave */ else { /* Set required role to slave as only one AG connected */ if((theSink.user_power_table)&&(theSink.user_power_table->normalEntries)) { /* if user supplied role request then use that */ LP_DEBUG(("LP: Singlepoint, require Master role\n")) ; requiredRole = theSink.user_power_table->normalRole; } else { #if defined ENABLE_PEER uint16 priority; if (getA2dpIndexFromSink(cfm->sink, &priority) && (theSink.a2dp_link_data->peer_device[priority] == remote_device_peer)) { if (A2dpMediaGetRole(theSink.a2dp_link_data->device_id[priority], theSink.a2dp_link_data->stream_id[priority]) == a2dp_source) { LP_DEBUG(("LP: Singlepoint: Peer (relaying), require Master role\n")) ; requiredRole = hci_role_master; /* Set the link supervision timeout as the role switch will have reset it back to firmware defaults */ ConnectionSetLinkSupervisionTimeout(cfm->sink, SINK_LINK_SUPERVISION_TIMEOUT); } else { LP_DEBUG(("LP: Singlepoint: Peer, don't change role\n")) ; requiredRole = hci_role_dont_care; } } else #endif { /* otherwise default to slave */ LP_DEBUG(("LP: Singlepoint, require Slave role\n")) ; requiredRole = hci_role_slave; } } } } /* check for failure of role switch due to AG having a sco open, if this is the case then reschedule the role switch until it is successfull or fails completely */ else if((cfm->status == hci_error_role_change_not_allowed)&&(!theSink.features.DisableRoleSwitching)) { LP_DEBUG(("LP: hci_error_role_change_not_allowed on sink = %x\n",(uint16)cfm->sink)); } /* automatic role switching is disabled, use the hfp_power_table pskey role requirements instead */ else if(cfm->status == hci_success) { LP_DEBUG(("LP: Bypass Automatic role sw, use hfp_power_table role requirements\n")) ; /* check for the prescence of a user configured role requirement */ if(theSink.user_power_table) { /* determine device state, if stream a2dp check for power table entry and use that role if available */ if((stateManagerGetState() == deviceA2DPStreaming)&&(theSink.user_power_table->A2DPStreamEntries)) { LP_DEBUG(("LP: Bypass: use A2dp role\n")) ; requiredRole = theSink.user_power_table->A2DPStreamRole; } /* or if in call and sco is present check for sco power table entry and use role from that */ else if((stateManagerGetState() > deviceConnected)&&(theSink.routed_audio)&&(theSink.user_power_table->SCOEntries)) { LP_DEBUG(("LP: Bypass: use SCO role\n")) ; requiredRole = theSink.user_power_table->SCORole; } /* or default to normal role power table entry and use role from that */ else if(theSink.user_power_table->normalEntries) { LP_DEBUG(("LP: Bypass: use Normal role\n")) ; requiredRole = theSink.user_power_table->normalRole; } /* if no suitable power table entries available then default to slave role */ else { LP_DEBUG(("LP: Bypass: use default slave role\n")) ; requiredRole = hci_role_slave; } } } /* Request a role change if required */ if (requiredRole != hci_role_dont_care) { if (cfm->role != requiredRole) { LP_DEBUG(("LP: Set dev as %s %x\n",(requiredRole == hci_role_master) ? "master" : "slave", (unsigned int)cfm->sink)) ; /* Set role for this connection */ ConnectionSetRole(&theSink.task, cfm->sink, requiredRole); } else { LP_DEBUG(("LP: role not set, already %s\n",(requiredRole == hci_role_master) ? "master" : "slave")) ; sinkA2dpSetLinkRole(cfm->sink, cfm->role); } } else { sinkA2dpSetLinkRole(cfm->sink, cfm->role); LP_DEBUG(("LP: role change not required\n")) ; } }