/**************************************************************************** NAME handleBatteryLow DESCRIPTION Called when the battery voltage is detected to be in Battery Low state */ static void handleBatteryLow( void ) { bool last_state = theHeadset.battery_low_state; PM_DEBUG(("PM: Battery Low\n")); /*we only want low battery reminders if the headset is ON and not charging */ if(stateManagerGetState() != headsetLimbo) { MessageSend(&theHeadset.task, EventLowBattery, 0); #if 0 theHeadset.battery_low_state = TRUE; #endif PM_DEBUG(("battery_low_state = true\n")); } if(ChargerIsChargerConnected() || stateManagerGetState() == headsetLimbo) { /* printf("the battery is charging or the headset is in limbo state\n");*/ theHeadset.battery_low_state = FALSE; PM_DEBUG(("battery_low_state = false\n")); } if(!last_state || !theHeadset.battery_low_state) { #ifdef ROM_LEDS /* update state indication to indicate low batt state change */ PM_DEBUG(("Low battery state\n")); LEDManagerIndicateState( stateManagerGetState() ); #endif } }
void VolumeSetHeadsetVolume( uint16 pNewVolume , bool pPlayTone, hfp_link_priority priority, bool set_gain, volume_direction dir ) { bool lPlayTone = FALSE ; bool lOverideMute = theHeadset.features.OverideMute ; bool lMuteLocalVolAction = theHeadset.features.MuteLocalVolAction ; VOL_DEBUG(("SetVol [%x] [%d][%d][%d]\n " ,pNewVolume, theHeadset.gMuted , lOverideMute , lMuteLocalVolAction)) ; /* this should only occur if we are not muted or if we are muted but wish to overide */ if ( (!theHeadset.gMuted ) || ( lOverideMute ) || (lMuteLocalVolAction)) { /*set the local volume only*/ if ((theHeadset.gMuted) && (!lMuteLocalVolAction)) MessageSend( &theHeadset.task , EventMuteOff , 0 ) ; /* the tone needs to be played so set flag */ lPlayTone = TRUE ; /* set new volume */ theHeadset.profile_data[PROFILE_INDEX(priority)].audio.gSMVolumeLevel = pNewVolume ; if(set_gain) AudioSetVolume ( theHeadset.audioData.gVolMaps[ pNewVolume ].VolGain , theHeadset.codec_task ) ; } /* ensure there is a valid tone (non zero) to be played */ if( pPlayTone && lPlayTone && theHeadset.audioData.gVolMaps[pNewVolume].Tone ) { /*only attempt to play the tone if it has not yet been played*/ /*VOL_DEBUG(("VOL: VolTone[%x]\n" , (int)theHeadset.audioData.gVolMaps[pNewVolume].Tone)) ;*/ #if 1 if(theHeadset.TTS_ASR_Playing == false) { VOL_DEBUG(("VOL: VolTone[%x]\n" , (int)theHeadset.audioData.gVolMaps[pNewVolume].Tone)) ; #ifdef DifferentToneForVolumeButton if((stateManagerGetState() == headsetActiveCallSCO || stateManagerGetState() == headsetActiveCallNoSCO) && (theHeadset.sco_sink != 0)) { if(dir) { TonesPlayTone(0x42 ,theHeadset.features.QueueVolumeTones, FALSE); } else { TonesPlayTone(theHeadset.audioData.gVolMaps[pNewVolume].Tone ,theHeadset.features.QueueVolumeTones, FALSE); } } #else /*TonesPlayTone(theHeadset.audioData.gVolMaps[pNewVolume].Tone ,theHeadset.features.QueueVolumeTones, FALSE);*/ #endif } #endif } }
/**************************************************************************** NAME sinkEnableMultipointConnectable DESCRIPTION when in multi point mode check to see if device can be made connectable, this will be when only one AG is currently connected. this function will be called upon certain button presses which will reset the 60 second timer and allow a second AG to connect should the device have become non discoverable RETURNS none */ void sinkEnableMultipointConnectable( void ) { /* only applicable to multipoint devices and don't go connectable when taking or making an active call, allow connectable in streaming music state */ if((theSink.MultipointEnable)&&(stateManagerGetState() != deviceLimbo)) { /* if only one hfp instance is connected then set connectable to active */ if(deviceManagerNumConnectedDevs() < 2) { MAIN_DEBUG(("MP Go Conn \n" )); /* make device connectable */ sinkEnableConnectable(); /* cancel any currently running timers that would disable connectable mode */ MessageCancelAll( &theSink.task, EventSysConnectableTimeout ); /* remain connectable for a further 'x' seconds to allow a second AG to be connected if non-zero, otherwise stay connecatable forever */ if(theSink.conf1->timeouts.ConnectableTimeout_s) { MessageSendLater(&theSink.task, EventSysConnectableTimeout, 0, D_SEC(theSink.conf1->timeouts.ConnectableTimeout_s)); } } /* otherwise do nothing */ } }
/**************************************************************************** NAME powerManagerHandleVbatLow DESCRIPTION Called when the battery voltage is detected to be in Battery Low state */ static void powerManagerHandleVbatLow( void ) { sinkState lSinkState = stateManagerGetState (); bool batt_was_low = powerManagerIsVbatLow(); PM_DEBUG(("PM: Battery Low\n")); if(powerManagerIsChargerConnected() || lSinkState == deviceLimbo) { theSink.battery_state = POWER_BATT_LEVEL0; } else { theSink.battery_state = POWER_BATT_LOW; } if(!batt_was_low || !powerManagerIsVbatLow()) { /* update state indication to indicate low batt state change */ #ifndef NO_LED LEDManagerIndicateState( lSinkState ); #endif } AudioSetPower(powerManagerGetLBIPM()); }
/**************************************************************************** NAME powerManagerPowerOff DESCRIPTION Power off due to critical battery or temperature outside operational limits RETURNS void */ static void powerManagerPowerOff(void) { if(stateManagerGetState() == deviceLimbo) stateManagerUpdateLimboState(); else MessageSend(&theSink.task, EventUsrPowerOff, 0); }
/**************************************************************************** DESCRIPTION sets the current A2dp volume */ void VolumeSetA2dp(uint16 index, uint16 oldVolume) { if(theHeadset.audioData.gVolMaps[ theHeadset.a2dp_link_data->gAvVolumeLevel[index] ].A2dpGain == VOLUME_A2DP_MUTE_GAIN) { /* if actual mute enabled, activate it now */ if(theHeadset.audioData.gVolMaps[ oldVolume ].A2dpGain != VOLUME_A2DP_MUTE_GAIN) { VOL_DEBUG(("VOL: A2dp mute\n")); AudioSetMode(AUDIO_MODE_MUTE_SPEAKER, NULL); } } else { VOL_DEBUG(("VOL: A2dp set vol [%d][%d]\n", theHeadset.a2dp_link_data->gAvVolumeLevel[index], theHeadset.audioData.gVolMaps[ theHeadset.a2dp_link_data->gAvVolumeLevel[index] ].A2dpGain - 1)); AudioSetVolume ( theHeadset.audioData.gVolMaps[ theHeadset.a2dp_link_data->gAvVolumeLevel[index] ].A2dpGain - 1 , theHeadset.codec_task ) ; } /* set volume level in audio plugin */ if ((theHeadset.audioData.gVolMaps[ theHeadset.a2dp_link_data->gAvVolumeLevel[index] ].A2dpGain != VOLUME_A2DP_MUTE_GAIN) && (theHeadset.audioData.gVolMaps[ oldVolume ].A2dpGain == VOLUME_A2DP_MUTE_GAIN)) { /* the audio was muted but now should be un-muted as above minimum volume */ VOL_DEBUG(("VOL: A2dp unmute\n")); AudioSetMode(AUDIO_MODE_CONNECTED, NULL); } /* play tone if applicable */ #if 1 if(theHeadset.audioData.gVolMaps[theHeadset.a2dp_link_data->gAvVolumeLevel[index]].Tone) { if(stateManagerGetState() == headsetA2DPStreaming) { TonesPlayTone(theHeadset.audioData.gVolMaps[theHeadset.a2dp_link_data->gAvVolumeLevel[index]].Tone ,theHeadset.features.QueueVolumeTones, FALSE); } } #endif }
/**************************************************************************** NAME sinkRecallQueuedEvent DESCRIPTION Checks to see if an event was Queued and issues it RETURNS void */ void sinkRecallQueuedEvent ( void ) { /*this is currently only applicable to LNR and voice Dial but does not care */ if ((theSink.gEventQueuedOnConnection != (EventInvalid - EVENTS_MESSAGE_BASE))) { switch (stateManagerGetState() ) { case deviceIncomingCallEstablish: case deviceOutgoingCallEstablish: case deviceActiveCallSCO: case deviceActiveCallNoSCO: case deviceThreeWayCallWaiting: case deviceThreeWayCallOnHold: case deviceThreeWayMulticall: /* Do Nothing Message Gets ignored*/ break ; default: /* delay sending of queued message by 1 second as some phones are ignoring the AT command when using native firmware as the connection time is much quicker using that firmware */ MessageSendLater ( &theSink.task , (theSink.gEventQueuedOnConnection + EVENTS_MESSAGE_BASE), 0 , 1500) ; break; } } /*reset the queued event*/ sinkClearQueueudEvent(); }
/**************************************************************************** NAME batteryNormal DESCRIPTION Called when the battery voltage is detected to be in a Normal state */ static void handleBatteryNormal(power_battery_level pLevel) { uint16 level = (uint16)(pLevel - POWER_BATT_LEVEL0); PM_DEBUG(("PM: Battery Normal,level %d\n",level)); #ifndef BHC612 MessageSend(&theHeadset.task, EventOkBattery, 0); /* If charger connected send a charger gas gauge message (these don't have any functional use but can be associated with LEDs/tones) */ if (ChargerIsChargerConnected()) MessageSend(&theHeadset.task, (EventChargerGasGauge0+level), 0); #endif /* Send notification to AG if batt level has changed */ MessageSend(&theHeadset.task, EventGasGauge0+level, 0); #ifdef ROM_LEDS /* when changing from low battery state to a normal state, refresh the led state pattern to replace the low battery pattern should it have been shown */ if(theHeadset.battery_low_state == TRUE) { /* kick off a check to see if there is a low battery led state configured */ PM_DEBUG(("Battery Normal\n")); LEDManagerIndicateState( stateManagerGetState() ); } #endif /* reset any low battery warning that may be in place */ theHeadset.battery_low_state = FALSE; theHeadset.low_battery_flag_ag = FALSE; }
/**************************************************************************** NAME audioHandleSyncDisconnectInd DESCRIPTION Handle HFP_AUDIO_DISCONNECT_IND. This indicates that an incoming sychronous connection has been disconnected RETURNS */ void audioHandleSyncDisconnectInd ( const HFP_AUDIO_DISCONNECT_IND_T * pInd ) { Sink sink; /* Get the priority of the other link */ hfp_link_priority other = (pInd->priority == hfp_primary_link) ? hfp_secondary_link : hfp_primary_link; AUD_DEBUG(("AUD: Synchronous Disconnect Ind [%x]:\n",pInd->priority)) ; /* ensure disconnection was succesfull */ if(pInd->status == hfp_audio_disconnect_success) { MessageSend ( &theSink.task , EventSCOLinkClose , 0 ) ; /* update sco priority */ setScoPriorityFromHfpPriority(pInd->priority, sco_none); /* SCO has been disconnected, check for the prescence of another sco in hold state, this occurs when the AG has performed an audio transfer, promote the held call to active */ if(getScoPriorityFromHfpPriority(other) == sco_held_call) setScoPriorityFromHfpPriority(other, sco_active_call); AUD_DEBUG(("AUD: Synchronous Disconnect Ind [%x]: sco_pri = %d\n",pInd->priority, HfpLinkPriorityFromAudioSink(theSink.routed_audio) )) ; /* deroute the audio */ audioHandleRouting(audio_source_none); /*change the active call state if necessary*/ if ((stateManagerGetState() == deviceActiveCallSCO)) stateManagerEnterActiveCallState(); /* Send our link policy settings for normal role now SCO is dropped */ if(HfpLinkGetSlcSink(pInd->priority, &sink)) linkPolicyUseHfpSettings(pInd->priority, sink); } }
/**************************************************************************** NAME LedsEnableFilterOverrides DESCRIPTION Enable or disable filters overriding LEDs. This will not change which filters are active, it will just turn off any LEDs the filters are forcing on. RETURNS void */ void LedsEnableFilterOverrides(bool enable) { uint16 lFilterIndex; /* Run through all filters */ for (lFilterIndex = 0 ; lFilterIndex < theSink.theLEDTask->gLMNumFiltersUsed ; lFilterIndex ++ ) { LEDFilter_t *lEventFilter = &(theSink.theLEDTask->gEventFilters [ lFilterIndex ]); /* If filter is overriding an LED turn it off */ if (LedsIsFilterEnabled(lFilterIndex) && (lEventFilter->FilterType == OVERRIDE)) PioSetLedPin(lEventFilter->OverideLED, (enable ? LED_ON : LED_OFF)); } /* Restore state (ensures we haven't disabled any LEDs we shouldn't) */ #ifndef NO_LED LEDManagerIndicateState ( stateManagerGetState () ) ; #endif }
/**************************************************************************** NAME sinkCheckForAudioTransfer DESCRIPTION checks on connection for an audio connction and performs a transfer if not present RETURNS void */ void sinkCheckForAudioTransfer ( void ) { sinkState lState = stateManagerGetState() ; AUD_DEBUG(("AUD: Tx[%d] [%x]\n", lState , (int)theSink.routed_audio)) ; switch (lState) { case deviceIncomingCallEstablish : case deviceThreeWayCallWaiting : case deviceThreeWayCallOnHold : case deviceThreeWayMulticall : case deviceIncomingCallOnHold : case deviceActiveCallNoSCO : { Sink sink; hfp_call_state state; hfp_link_priority priority = hfp_invalid_link; /* check call state and sink of AG1 */ if((HfpLinkGetCallState(hfp_primary_link, &state))&&(state == hfp_call_state_active)&& (HfpLinkGetAudioSink(hfp_primary_link, &sink))&&(!sink)) { priority = hfp_primary_link; } /* or check call state and sink of AG2 */ else if((HfpLinkGetCallState(hfp_secondary_link, &state))&&(state == hfp_call_state_active)&& (HfpLinkGetAudioSink(hfp_secondary_link, &sink))&&(!sink)) { priority = hfp_secondary_link; } /* if call found with no audio */ if (priority) { hfp_audio_params * audio_params = NULL; HfpAudioTransferRequest(priority, hfp_audio_to_hfp , theSink.HFP_supp_features.packet_types, audio_params ); } } break ; default: break; } }
/**************************************************************************** DESCRIPTION function to handle a button press - informs button manager of a change currently makes direct call - may want to use message handling (tasks) */ static void ButtonsButtonDetected ( ButtonsTaskData * pButtonsTask, uint32 pButtonMask , ButtonsTime_t pTime ) { B_DEBUG(("B:But Det[%lx]\n", pButtonMask)) ; if( pButtonMask == 0 ) { MessageCancelAll ( &pButtonsTask->task , B_REPEAT_TIMER ) ; } else { BMButtonDetected ( pButtonMask, pTime ) ; if (stateManagerGetState() == deviceTestMode) { checkDUTKeyRelease(pButtonMask, pTime); } } }
/**************************************************************************** NAME powerManagerHandleVbat DESCRIPTION Called when the battery voltage is detected to be in a Normal state */ static void powerManagerHandleVbat(voltage_reading vbat, battery_level_source source) { /* Get the event associated with this battery level */ sink_battery_setting setting = theSink.conf1->power.bat_events[vbat.level]; sinkEvents_t event = setting.event; PM_DEBUG(("PM: Battery Voltage 0x%02X (%dmV)\n", vbat.level, vbat.voltage)); displayUpdateBatteryLevel(powerManagerIsChargerConnected()); /* Send indication if not charging, not in limbo state and indication enabled for this source */ if(!powerManagerIsChargerConnected() && (stateManagerGetState() != deviceLimbo) && (setting.sources & source)) { PM_DEBUG(("PM: Sending Event 0x%X\n", event)); MessageSend(&theSink.task, event, NULL); } switch(event) { case EventSysBatteryCritical: /* Always indicate critical battery */ powerManagerHandleVbatCritical(); usbSetVbatDead(TRUE); break; case EventSysBatteryLow: powerManagerHandleVbatLow(); usbSetVbatDead(FALSE); break; case EventSysGasGauge0 : case EventSysGasGauge1 : case EventSysGasGauge2 : case EventSysGasGauge3 : powerManagerHandleVbatNormal(event - EventSysGasGauge0); usbSetVbatDead(FALSE); break; default: break; } }
/**************************************************************************** NAME sinkFmRxAudioDisconnect DESCRIPTION Disconnects the FM audio via the audio library/FM audio plugin RETURNS nothing */ void sinkFmRxAudioDisconnect(void) { FM_DEBUG(("sinkFmRxAudioDisconnect \n")); /* ensure FM is on and FM audio currently being routed to speaker */ if ((theSink.conf2->sink_fm_data.fmRxOn) && sinkFmAudioSinkMatch(theSink.routed_audio)) { FM_DEBUG(("FM audio disconnected \n")); /* reset routed audio */ theSink.routed_audio = 0; /* disconnect FM audio via audio library */ AudioDisconnect(); /* Update limbo state */ if (stateManagerGetState() == deviceLimbo ) stateManagerUpdateLimboState(); } }
/**************************************************************************** NAME LedsEnableFilter DESCRIPTION enable / disable a given filter ID RETURNS void */ static void LedsEnableFilter ( uint16 pFilter , bool pEnable) { uint32 lOldMask = LED_GETACTIVEFILTERS() ; if (pEnable) { /*to set*/ LED_SETACTIVEFILTERS((lOldMask | ( (uint32)0x1 << pFilter ))); LED_DEBUG(("LED: EnF [%lx] [%lx] [%x]\n", lOldMask , LED_GETACTIVEFILTERS() , pFilter)); } else { /*to unset*/ LED_SETACTIVEFILTERS(lOldMask & ~( (uint32)0x1 << pFilter )); LED_DEBUG(("LED: DisF [%lx] [%lx] [%x]\n", lOldMask , LED_GETACTIVEFILTERS() , pFilter)); } #ifndef NO_LED /* Check if we should indicate state */ if ((theSink.theLEDTask->gEventFilters[pFilter].OverideDisable) && (lOldMask != LED_GETACTIVEFILTERS())) LEDManagerIndicateState ( stateManagerGetState () ) ; #endif }
/**************************************************************************** NAME batteryNormal DESCRIPTION Called when the battery voltage is detected to be in a Normal state */ static void powerManagerHandleVbatNormal(uint8 level) { #ifndef NO_LED bool low_batt = powerManagerIsVbatLow(); #endif PM_DEBUG(("PM: Battery Normal %d\n", level)); MessageSend(&theSink.task, EventSysBatteryOk, 0); /* If charger connected send a charger gas gauge message (these don't have any functional use but can be associated with LEDs/tones) */ if (powerManagerIsChargerConnected()) MessageSend(&theSink.task, (EventSysChargerGasGauge0+level), 0); /* reset any low battery warning that may be in place */ theSink.battery_state = POWER_BATT_LEVEL0 + level; csr2csrHandleAgBatteryRequestRes(level); #ifndef NO_LED /* when changing from low battery state to a normal state, refresh the led state pattern to replace the low battery pattern should it have been shown */ if(low_batt) LEDManagerIndicateState(stateManagerGetState()); #endif AudioSetPower(powerManagerGetLBIPM()); }
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 LedsCheckForFilter DESCRIPTION This function checksif a filter has been configured for the given event, if it has then activates / deactivates the filter Regardless of whether a filter has been activated or not, the event is signalled as completed as we have now deaklt with it (only checked for a filter if a pattern was not associated. RETURNS void */ void LedsCheckForFilter ( sinkEvents_t pEvent ) { uint16 lFilterIndex = 0 ; for (lFilterIndex = 0 ; lFilterIndex < theSink.theLEDTask->gLMNumFiltersUsed ; lFilterIndex ++ ) { LEDFilter_t *lEventFilter = &(theSink.theLEDTask->gEventFilters [ lFilterIndex ]); if((uint16)(lEventFilter->Event) == pEvent && lEventFilter->FilterType != DISABLED) { if (lEventFilter->FilterType != CANCEL) { /* Check filter isn't already enabled */ if (!LedsIsFilterEnabled(lFilterIndex)) { /* Enable filter */ LedsEnableFilter (lFilterIndex , TRUE) ; /* If it is an overide fLED filter and the currently playing pattern is OFF then turn on the overide led immediately*/ if ( lEventFilter->FilterType == OVERRIDE) { uint16 lOverideLEDIndex = lEventFilter->OverideLED ; /* this should only happen if the led in question is currently off*/ if ( theSink.theLEDTask->gActiveLEDS[lOverideLEDIndex].OnOrOff == LED_OFF) { LED_DEBUG(("LED: FilEnable Turn on[%d][%d] \n",lFilterIndex + 1 , lOverideLEDIndex )) ; PioSetLedPin ( lOverideLEDIndex , LED_ON) ; } } } } else { uint16 lFilterToCancel = lEventFilter->FilterToCancel ; /*disable the according filter*/ if ( lFilterToCancel != 0 ) { uint16 lFilterToCancelIndex = lFilterToCancel - 1 ; LEDFilter_t *lEventFilter1 = &(theSink.theLEDTask->gEventFilters [ lFilterToCancelIndex ]); uint16 lOverideLEDIndex = lEventFilter1->OverideLED ; LED_DEBUG(("LED: FilCancel[%d][%d] [%d]\n",lFilterIndex + 1 , lFilterToCancel , lFilterToCancelIndex )) ; /*lFilter To cancel = 1-n, LedsEbnable filter requires 0-n */ LedsEnableFilter (lFilterToCancelIndex , FALSE ) ; if ( theSink.theLEDTask->gActiveLEDS[lOverideLEDIndex].OnOrOff == LED_OFF) { /* LedsHandleOverideLED ( theSink.theLEDTask , LED_OFF) ;*/ if ( lEventFilter1->FilterType == OVERRIDE) { LED_DEBUG(("LED: FilCancel Turn off[%d][%d] [%d]\n",lFilterIndex + 1 , lFilterToCancel , lFilterToCancelIndex )) ; PioSetLedPin ( lOverideLEDIndex, LED_OFF) ; /* it is possible for the cancel filter to turn off leds used in a solid led state indication such as a solid blue pairing indication, should the charger be removed and then reinserted the solid blue state is turned off, this call will reset the state indication and turn it back on again */ #ifndef NO_LED LEDManagerIndicateState ( stateManagerGetState () ) ; #endif } } } else { LED_DEBUG(("LED: Fil !\n")) ; } } LED_DEBUG(("LM : Filter Found[%d]A[%x] [%d]\n", lFilterIndex + 1, pEvent , theSink.theLEDTask->gEventFilters[ lFilterIndex ].IsFilterActive )) ; } } }
/**************************************************************************** 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 audioHandleSyncConnectCfm DESCRIPTION Handle HFP_AUDIO_CONNECT_CFM. This indicates that an incoming sychronous connection has been established RETURNS */ void audioHandleSyncConnectCfm ( const HFP_AUDIO_CONNECT_CFM_T * pCfm ) { uint8 index = PROFILE_INDEX(pCfm->priority); hfp_call_state CallState; /* Get the priority of the other link */ hfp_link_priority other = (pCfm->priority == hfp_primary_link) ? hfp_secondary_link : hfp_primary_link; AUD_DEBUG(("Synchronous Connect Cfm from [%x]:\n", (uint16)pCfm->priority)) ; /* if successful */ if ( pCfm->status == hfp_success) { Sink sink; /* obtain sink for this audio connection */ if(HfpLinkGetSlcSink(pCfm->priority, &sink)) { /* Send our link policy settings for active SCO role */ linkPolicyUseHfpSettings(pCfm->priority, sink); } /* store in individual hfp struct as it may be necessary to disconnect and reconnect audio on a per hfp basis for multipoint multiparty calling */ theSink.profile_data[index].audio.tx_bandwidth= pCfm->tx_bandwidth; theSink.profile_data[index].audio.link_type= pCfm->link_type; theSink.profile_data[index].audio.codec_selected = pCfm->codec; /* Send an event to indicate that a SCO has been opened */ /* this indicates that an audio connection has been successfully created to the AG*/ MessageSend ( &theSink.task , EventSCOLinkOpen , 0 ) ; /* update the audio priority state is this sco a streaming audio sco?, check call state for this ag */ if(HfpLinkGetCallState(pCfm->priority, &CallState)) { /* determine sco priority based on call status */ switch(CallState) { /* no call so this is a steaming audio connection */ case hfp_call_state_idle: setScoPriorityFromHfpPriority(pCfm->priority, sco_streaming_audio); break; /* incoming call so this is an inband ring sco */ case hfp_call_state_incoming: case hfp_call_state_incoming_held: case hfp_call_state_twc_incoming: setScoPriorityFromHfpPriority(pCfm->priority, sco_inband_ring); break; /* active call states so this sco has highest priority */ case hfp_call_state_active: case hfp_call_state_twc_outgoing: /* this audio connection may have been the result of the an audio transfer from the AG and there is already an active call on the other AG, check for this and make this new audio connection held leaving the other routed audio connection intact */ if(getScoPriorityFromHfpPriority(other)==sco_active_call) setScoPriorityFromHfpPriority(pCfm->priority, sco_held_call); else setScoPriorityFromHfpPriority(pCfm->priority, sco_active_call); break; /* an outgoing call sco has highest priority, if there is a call on other AG it needs to be put on hold whilst this outgoing call is made */ case hfp_call_state_outgoing: /* does other AG have active call on it?, if so hold the audio */ if(getScoPriorityFromHfpPriority(other)==sco_active_call) setScoPriorityFromHfpPriority(other, sco_held_call); /* make the outgoing call audio the one that gets routed */ setScoPriorityFromHfpPriority(pCfm->priority, sco_active_call); break; /* this call is held so the sco is put to on hold priority which is lower than active call but higher than streaming */ case hfp_call_state_held_active: case hfp_call_state_held_remaining: if(theSink.routed_audio) setScoPriorityFromHfpPriority(pCfm->priority, sco_held_call); break; /* non covered states treat as highest priority sco connection */ default: setScoPriorityFromHfpPriority(pCfm->priority, sco_active_call); break; } } /* route the appropriate audio connection */ audioHandleRouting(audio_source_none); /*change the active call state if necessary*/ if ((stateManagerGetState() == deviceActiveCallNoSCO) ) { stateManagerEnterActiveCallState(); } #ifdef DEBUG_AUDIO switch (pCfm->link_type) { case (sync_link_unknown): AUD_DEBUG(("AUD: Link = ?\n")) ; break ; case (sync_link_sco) : AUD_DEBUG(("AUD: Link = SCO\n")) ; break; case sync_link_esco: AUD_DEBUG(("AUD: Link = eSCO\n")) ; break ; } #endif } else { AUD_DEBUG(("Synchronous Connect Cfm: FAILED\n")) ; } AUD_DEBUG(("AUD : Sco->\n")) ; }
/**************************************************************************** DESCRIPTION function to detect level changes of buttons / multiple buttons, both PIO and capsense */ static void ButtonsLevelDetect ( const uint32 pInput , ButtonsTaskData * pButtonsTask ) { uint32 lNewInput = (uint32) (pInput & (pButtonsTask->gPerformInputLevelCheck)) ; uint32 lOldInput = (uint32) (pButtonsTask->gBOldInputState & pButtonsTask->gPerformInputLevelCheck); B_DEBUG(("But Lev Det|:NewInput[%lx] OldInput[%lx]\n", lNewInput, pButtonsTask->gBOldInputState )) ; if ( ButtonsWasButtonPressed(lOldInput, lNewInput ) ) { /* check whether device needs to be made connectable as a result of a button press on a multipoint device */ if(theSink.features.GoConnectableButtonPress) sinkEnableMultipointConnectable(); /*cancel all previously timed messages*/ MessageCancelAll ( &pButtonsTask->task , B_INTERNAL_TIMER ) ; MessageCancelAll ( &pButtonsTask->task , B_REPEAT_TIMER ) ; /* send new timed messages*/ MessageSendLater ( &pButtonsTask->task , B_INTERNAL_TIMER , 0 , pButtonsTask->button_config->long_press_time ) ; MessageSendLater ( &pButtonsTask->task , B_REPEAT_TIMER , 0 , pButtonsTask->button_config->repeat_time ) ; /*having restrted the timers, reset the time*/ pButtonsTask->gBTime = B_SHORT ; if (stateManagerGetState() == deviceTestMode) { checkDUTKeyPress(lNewInput); } } /*button was released or was masked out, check to make sure there is an input bit change as vreg enable can generate an addition MSG without any input's changing state */ else if(lOldInput!= lNewInput ) { /*it was only a released if there was a button actually pressed last time around - buttons we have masked out still end up here but no state changes are made */ if ( lOldInput != 0 ) { /*if we have had a double press in the required time and the button pressed was the same as this one*/ if ( (pButtonsTask->gBTapCount ) && (lOldInput == pButtonsTask->gBMultipleState ) ) { /* button has been released, increment multiple press counter */ pButtonsTask->gBTapCount++; B_DEBUG(("TapCount: [%lx][%lx][%x]\n", lOldInput , pButtonsTask->gBMultipleState , pButtonsTask->gBTapCount )) ; /* if the multiple press count is 2, set the time as a double, this will be used if no further presses are detected before the multiple press timeout occurs */ if(pButtonsTask->gBTapCount == DOUBLE_PRESS) pButtonsTask->gBTime = B_DOUBLE ; /* if the multiple press count is 3, process immediately as a triple press and cancel the multiple press timer */ else if(pButtonsTask->gBTapCount == TRIPLE_PRESS) { pButtonsTask->gBTime = B_TRIPLE ; /* indicate a triple press has been detected */ ButtonsButtonDetected ( pButtonsTask , lOldInput , B_TRIPLE ); /* reset current state and multiple press counter */ pButtonsTask->gBMultipleState = 0x0000 ; pButtonsTask->gBTapCount = 0; MessageCancelAll ( &pButtonsTask->task , B_MULTIPLE_TIMER ) ; } } /*only send a message if it was a short one - long / v long /double handled elsewhere*/ if ( (pButtonsTask->gBTime == B_SHORT ) ) { ButtonsButtonDetected ( pButtonsTask , lOldInput , B_SHORT ); /*store the double state*/ pButtonsTask->gBMultipleState = lOldInput ; pButtonsTask->gBTapCount++; /*start the double timer - only applicable to a short press*/ MessageSendLater ( &pButtonsTask->task , B_MULTIPLE_TIMER , 0 ,pButtonsTask->button_config->double_press_time ) ; } else if ( (pButtonsTask->gBTime == B_LONG) ) { ButtonsButtonDetected ( pButtonsTask , lOldInput , B_LONG_RELEASE ); } else if ( (pButtonsTask->gBTime == B_VERY_LONG) ) { ButtonsButtonDetected ( pButtonsTask , lOldInput , B_VERY_LONG_RELEASE ); } else if ( (pButtonsTask->gBTime == B_VERY_VERY_LONG) ) { ButtonsButtonDetected ( pButtonsTask , lOldInput , B_VERY_VERY_LONG_RELEASE ); } if (pButtonsTask->gBTime != B_INVALID) { MessageCancelAll ( &pButtonsTask->task , B_INTERNAL_TIMER) ; MessageCancelAll ( &pButtonsTask->task , B_REPEAT_TIMER ) ; } /*removing this allows all releases to generate combination presses is this right?*/ if ( !lNewInput ) { pButtonsTask->gBTime = B_INVALID ; } } } }
/**************************************************************************** NAME sinkInitComplete DESCRIPTION Sink device initialisation has completed. RETURNS void */ void sinkInitComplete( const HFP_INIT_CFM_T *cfm ) { uint8 i; /* Make sure the profile instance initialisation succeeded. */ if (cfm->status == hfp_init_success) { /* initialise connection status for this instance */ for(i=0;i<2;i++) { theSink.profile_data[i].status.list_id = INVALID_LIST_ID; theSink.a2dp_link_data->list_id[i] = INVALID_LIST_ID; } /* Disable SDP security */ ConnectionSmSetSecurityLevel(protocol_l2cap,1,ssp_secl4_l0,TRUE,FALSE,FALSE); /* WAE - no ACL, Debug keys - off, Legacy pair key missing - on */ ConnectionSmSecModeConfig(&theSink.task, cl_sm_wae_acl_none, FALSE, TRUE); /* Require MITM on the MUX (incomming and outgoing)*/ if(theSink.features.ManInTheMiddle) { ConnectionSmSetSecurityLevel(0,3,ssp_secl4_l3,TRUE,TRUE,FALSE); } RegisterDeviceIdServiceRecord(); #ifdef ENABLE_PEER RegisterPeerDeviceServiceRecord(); peerInitPeerStatus(); #endif /* Initialise Inquiry Data to NULL */ theSink.inquiry.results = NULL; #ifdef ENABLE_AVRCP /* initialise the AVRCP library */ sinkAvrcpInit(); #endif #ifdef ENABLE_SUBWOOFER /* Initialise the swat the library - Use library default service record - Library does not auto handles messages */ if ( (theSink.rundata->subwoofer.esco_params.tx_bandwidth == 0) && (theSink.rundata->subwoofer.esco_params.rx_bandwidth == 0) && (theSink.rundata->subwoofer.esco_params.max_latency == 0) ) { /* Use library default eSCO parameters */ SwatInit(&theSink.task, SW_MAX_REMOTE_DEVICES, swat_role_source, FALSE, 0, 0, NULL); } else { /* Use custom eSCO parameters from configuration data */ SwatInit(&theSink.task, SW_MAX_REMOTE_DEVICES, swat_role_source, FALSE, 0, 0, &theSink.rundata->subwoofer.esco_params); } #endif /* No audio sources are gated at startup */ theSink.gated_audio = 0; /*if we receive the init message in the correct state*/ if ( stateManagerGetState() == deviceLimbo ) { #ifdef ENABLE_PBAP /* If hfp has been initialised successfully, start initialising PBAP */ INIT_DEBUG(("INIT: PBAP Init start\n")); initPbap(); #else /*init the configurable parameters*/ InitUserFeatures(); #endif } /* try to get a power table entry from ps if one exists after having read the user features as A2DP enable state is used to determine size of power table entry */ SetupPowerTable(); #ifdef ENABLE_MAPC /* if the map feature is enabled, start the map notification service at initialisation time */ initMap(); #endif /* disable automatic mic bias control as this is handled by the audio plugins */ AudioPluginSetMicPio(theSink.conf1->PIOIO.digital.mic_a, FALSE); AudioPluginSetMicPio(theSink.conf1->PIOIO.digital.mic_b, FALSE); } else /* If the profile initialisation has failed then things are bad so panic. */ Panic(); /* initialisation is complete */ theSink.SinkInitialising = FALSE; }
/**************************************************************************** NAME TonesPlayEvent DESCRIPTION function to indaicate an event by playing its associated tone uses underlying tones playback RETURNS void */ void TonesPlayEvent ( sinkEvents_t pEvent ) { uint16 lEvent = pEvent; int i = 0 ; /* Don't play tone for Mute Toggle, this event is auto-converted to either EventUsrMuteOn or EventUsrMuteOff */ if (pEvent == EventUsrMuteToggle) return; #ifdef ENABLE_SOUNDBAR /* In Limbo for sound bar build, playing tones may cause panic if I2S amplifier is powered down by PIO */ if(stateManagerGetState() == deviceLimbo) { /* If sound bar I2S amplifier shut down is managed by PIO, first check the logic state of amplifier power pin */ if((theSink.conf2->audio_routing_data.PluginFeatures.audio_output_type == OUTPUT_INTERFACE_TYPE_I2S) &&(theSink.features.AmplifierShutDownByPIO)) { /* Check configuration for AMP pio drive */ if(theSink.conf1->PIOIO.pio_outputs.PowerOnPIO != NO_PIO) { /* Do not attempt to play tones through I2S if amplifier is shut down already, otherwise, I2C comms will fail and cause panic */ if(theSink.audioAmpPowerPin != TRUE) return; } } } #endif /* ensure sink is not in muted state or trying to play the mute reminder */ if((theSink.sink_enable_present != TRUE)||(pEvent == EventSysMuteReminder)||(pEvent == EventUsrMuteOn)) { /* If Audio Prompts are disabled go straight to tones. Otherwise if Audio Prompt is assigned to this event tone playback would be skipped.*/ if(theSink.audio_prompts_enabled) { /* If there's a valid Audio Prompt event to play don't play any tones */ if(AudioPromptPlayEvent( pEvent )) return; } /* scan available tones list, list end is signified by NOT_DEFINED */ while ( theSink.conf2->gEventTones [i].tone != TONE_NOT_DEFINED ) { /* if an event matche is found then play tone */ if (theSink.conf2->gEventTones [i].event == lEvent ) { /* turn on audio amp */ PioSetPio ( theSink.conf1->PIOIO.pio_outputs.DeviceAudioActivePIO , pio_drive, TRUE) ; /* start check to turn amp off again if required */ MessageSendLater(&theSink.task , EventSysCheckAudioAmpDrive, 0, 1000); /* check event as tone queueing not allowed on mute and ring tones */ switch(pEvent) { case EventSysMuteReminder: case EventSysRingtone1: case EventSysRingtone2: /* check whether to play mute reminder tone at default volume level, never queue mute reminders to protect against the case that the tone is longer than the mute reminder timer */ TonesPlayTone (theSink.conf2->gEventTones [ i ].tone ,FALSE, (theSink.features.MuteToneFixedVolume)) ; break; /* for all other events use the QueueEventTones feature bit setting */ default: /* play tone */ TonesPlayTone (theSink.conf2->gEventTones [ i ].tone ,theSink.features.QueueEventTones, FALSE ) ; break; } } i++ ; } } }
/**************************************************************************** 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 handleBatteryLevelInd DESCRIPTION Called when the battery voltage is detected to be in a Normal state */ static void handleBatteryLevelInd(power_battery_level pLevel) { /* based on the pLevel and send different messages */ switch(pLevel) { case POWER_BATT_CRITICAL: PM_DEBUG(("PM: POWER_BATTERY_SHUTDOWN\n")); if ( (!ChargerIsChargerConnected()) && stateManagerGetState() != headsetLimbo ) { MessageSend(&theHeadset.task, EventLowBattery, 0); MessageSend(&theHeadset.task, EventPowerOff, 0); } /* reset any low battery warning that may be in place */ theHeadset.battery_low_state = FALSE; theHeadset.low_battery_flag_ag = FALSE; break; case POWER_BATT_LOW: #ifdef BHC612 if(theHeadset.batt_level != POWER_BATT_LOW) { theHeadset.batt_level = POWER_BATT_LOW; /*MessageSendLater(&theHeadset.task, EventBatteryLevelRequest, 0,100);*/ PM_DEBUG(("batt_level = low\n")); } #endif handleBatteryLow(); break; case POWER_BATT_LEVEL0: #ifdef BHC612 if(theHeadset.batt_level != POWER_BATT_LEVEL0) { theHeadset.batt_level = POWER_BATT_LEVEL0; /*MessageSendLater(&theHeadset.task, EventBatteryLevelRequest, 0,100);*/ PM_DEBUG(("batt_level = 0\n")); } #endif handleBatteryNormal(pLevel); break; case POWER_BATT_LEVEL1: #ifdef BHC612 if(theHeadset.batt_level != POWER_BATT_LEVEL1) { theHeadset.batt_level = POWER_BATT_LEVEL1; /*MessageSendLater(&theHeadset.task, EventBatteryLevelRequest, 0,100); */ PM_DEBUG(("batt_level = 1\n")); } #endif handleBatteryNormal(pLevel); break; case POWER_BATT_LEVEL2: #ifdef BHC612 if(theHeadset.batt_level != POWER_BATT_LEVEL2) { theHeadset.batt_level = POWER_BATT_LEVEL2; /*MessageSendLater(&theHeadset.task, EventBatteryLevelRequest, 0,100); */ PM_DEBUG(("batt_level = 2\n")); } #endif handleBatteryNormal(pLevel); break; case POWER_BATT_LEVEL3: #ifdef BHC612 if(theHeadset.batt_level != POWER_BATT_LEVEL3) { theHeadset.batt_level = POWER_BATT_LEVEL3; /*MessageSendLater(&theHeadset.task, EventBatteryLevelRequest, 0,100); */ PM_DEBUG(("batt_level = 3\n")); } #endif handleBatteryNormal(pLevel); break; default : PM_DEBUG(("PM: Unhandled Battery Level[%x]\n", pLevel)); break ; } /* notify the audio plugin of the new power state, if the charger is connected this is fixed at max level */ AudioSetPower(LBIPMPowerLevel()); }
/**************************************************************************** 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 ) ; } } }