/**************************************************************************** NAME linkPolicyCheckRoles DESCRIPTION this function obtains the sinks of any connection and performs a role check on them RETURNS void */ void linkPolicyCheckRoles(void) { Sink sink_pri, sink_sec, sink_a2dp_pri, sink_a2dp_sec; /* obtain any hfp link sinks */ HfpLinkGetSlcSink(hfp_primary_link, &sink_pri); HfpLinkGetSlcSink(hfp_secondary_link, &sink_sec); /* obtain sinks for any a2dp links */ sink_a2dp_pri = A2dpSignallingGetSink(theSink.a2dp_link_data->device_id[a2dp_primary]); sink_a2dp_sec = A2dpSignallingGetSink(theSink.a2dp_link_data->device_id[a2dp_secondary]); LP_DEBUG(("LP: linkPolicyCheckRoles: Hfp pri = %x, sec = %x, A2dp pri = %x, sec = %x\n",(uint16)sink_pri ,(uint16)sink_sec ,(uint16)sink_a2dp_pri ,(uint16)sink_a2dp_sec)) ; /* if primary hfp exists then check its role */ if(sink_pri) linkPolicyGetRole(&sink_pri); /* if secondary hfp connection then check its role */ if(sink_sec) linkPolicyGetRole(&sink_sec); /* if primary a2dp exists and it is not the same device as pri or sec hfp connections */ if((sink_a2dp_pri)&&(!deviceManagerIsSameDevice(a2dp_primary, hfp_primary_link))&&(!deviceManagerIsSameDevice(a2dp_primary, hfp_secondary_link))) linkPolicyGetRole(&sink_a2dp_pri); /* if secondary a2dp exists and it is not the same device as pri or sec hfp connections */ if((sink_a2dp_sec)&&(!deviceManagerIsSameDevice(a2dp_secondary, hfp_primary_link))&&(!deviceManagerIsSameDevice(a2dp_secondary, hfp_secondary_link))) linkPolicyGetRole(&sink_a2dp_sec); #ifdef ENABLE_SUBWOOFER /* check the subwoofer signalling sink if connected, this will have an impact of the role required for AG connections to prevent a scatternet scenario */ if(SwatGetSignallingSink(theSink.rundata->subwoofer.dev_id)) { Sink sink = SwatGetSignallingSink(theSink.rundata->subwoofer.dev_id); linkPolicyGetRole(&sink); } #endif }
/**************************************************************************** NAME slcHandleLinkLossInd DESCRIPTION Indication of change in link loss status. RETURNS void */ void slcHandleLinkLossInd( const HFP_SLC_LINK_LOSS_IND_T *ind ) { typed_bdaddr ag_addr; Sink sink; /* Are we recovering or have we recovered? */ if(ind->status == hfp_link_loss_recovery) { /* Send an event to notify the user */ MessageCancelAll(&theSink.task , EventSysLinkLoss ); MessageSend(&theSink.task , EventSysLinkLoss , 0); /* Go connectable if feature enabled */ if(theSink.features.GoConnectableDuringLinkLoss) sinkEnableConnectable(); } else if(ind->status == hfp_link_loss_none) { sink_attributes attributes; /* Get Sink and bdaddr for the link */ HfpLinkGetSlcSink(ind->priority, &sink); SinkGetBdAddr(sink, &ag_addr); /* Carry out link setup */ slcConnectionSetup(ind->priority, sink, &ag_addr.addr); /* Link loss recovered - disable connectable */ if(theSink.features.GoConnectableDuringLinkLoss) { #ifdef ENABLE_SUBWOOFER if(SwatGetSignallingSink(theSink.rundata->subwoofer.dev_id)) { sinkDisableConnectable(); } #else sinkDisableConnectable(); #endif } /* Reconnect A2DP if appropriate */ if( theSink.features.EnableA2dpStreaming && deviceManagerGetAttributes(&attributes, (const bdaddr *)&ag_addr.addr) && (attributes.profiles & sink_a2dp) ) { SLC_DEBUG(("SLC: Reconnecting A2DP\n")) ; /* attempt reconnection to device supporting A2DP */ A2dpSignallingConnectRequest((bdaddr *)&ag_addr.addr); } } }
/**************************************************************************** NAME linkPolicyUpdateSwatLink DESCRIPTION this function checks the current swat connection state and updates the link policy of the link RETURNS void */ void linkPolicyUpdateSwatLink(void) { #ifdef ENABLE_SUBWOOFER /* attempt to get subwoofer signalling sink */ Sink sink = SwatGetSignallingSink(theSink.rundata->subwoofer.dev_id); /* determine if subwoofer is available */ if(sink) { LP_DEBUG(("LP: SetLinkPolicy Swat\n" )); ConnectionSetLinkPolicy(sink, 2 , lp_powertable_subwoofer); } else { /* no swat link */ LP_DEBUG(("LP: SetLinkPolicy Swat - no link\n" )); } #endif }
/**************************************************************************** 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, sink = %x role=%s\n", (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, require Master role\n")) ; requiredRole = hci_role_master; } else { LP_DEBUG(("LP: Multipoint: Peer, require Slave role\n")) ; requiredRole = hci_role_slave; } } 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; } } } #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; } #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 { /* 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")) ; } } else { LP_DEBUG(("LP: role change not required\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 ; }