/**************************************************************************** NAME powerManagerGetLBIPM DESCRIPTION Returns the Power level to use for Low Battery Intelligent Power Management (LBIPM). Note will always return high level if this feature is disabled. RETURNS void */ power_battery_level powerManagerGetLBIPM( void ) { PM_DEBUG(("PM: Battery Management %s\n", theSink.lbipmEnable ? "Enabled" : "Disabled")); PM_DEBUG(("PM: Using %s Level ", powerManagerIsChargerConnected() ? "Chg" : "Batt")); PM_DEBUG(("%d\n", powerManagerIsChargerConnected() ? POWER_BATT_LEVEL3 : theSink.battery_state)); /* Get current battery level */ if(theSink.lbipmEnable && !powerManagerIsChargerConnected()) return theSink.battery_state; /* LBIPM disabled or charger is connected so use full power level */ return POWER_BATT_LEVEL3; }
/**************************************************************************** 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 InitUserFeatures DESCRIPTION This function initialises all of the user features - this will result in a poweron message if a user event is configured correctly and the device will complete the power on RETURNS */ void InitUserFeatures ( void ) { /* Set to a known value*/ theSink.VoiceRecognitionIsActive = hfp_invalid_link ; theSink.buttons_locked = FALSE; theSink.last_outgoing_ag = hfp_primary_link; theSink.csr_speech_recognition_is_active = FALSE ; if (theSink.VolumeOrientationIsInverted) { MessageSend ( &theSink.task , EventUsrVolumeOrientationInvert , 0 ) ; } /* set the LED enable disable state which now persists over a reset */ #ifndef NO_LED if (theSink.theLEDTask->gLEDSEnabled) { LedManagerEnableLEDS () ; } else { LedManagerDisableLEDS () ; } #endif /* Set inquiry tx power and RSSI inquiry mode */ ConnectionWriteInquiryTx(theSink.conf2->rssi.tx_power); ConnectionWriteInquiryMode(&theSink.task, inquiry_mode_eir); /* RSSI with EIR data */ /* Check if we're here as result of a watchdog timeout */ powerManagerCheckPanic(); /*automatically power on the heasdet as soon as init is complete*/ if(theSink.panic_reconnect) { INIT_DEBUG(("INIT: Recover to state 0x%X\n", theSink.rundata->old_state)); if(theSink.rundata->old_state != deviceLimbo) MessageSend( &theSink.task , EventSysPowerOnPanic , NULL ) ; else theSink.panic_reconnect = FALSE; } else if((theSink.features.AutoPowerOnAfterInitialisation && !powerManagerIsChargerConnected())) { INIT_DEBUG(("INIT: Power On\n")); MessageSend( &theSink.task , EventUsrPowerOn , NULL ) ; } /* initialise the default volume levels */ theSink.volume_levels->a2dp_volume[0].masterVolume = ((theSink.conf1->volume_config.volume_control_config.no_of_steps * theSink.features.DefaultA2dpVolLevel)/ VOLUME_NUM_VOICE_STEPS); theSink.volume_levels->a2dp_volume[1].masterVolume = ((theSink.conf1->volume_config.volume_control_config.no_of_steps * theSink.features.DefaultA2dpVolLevel)/ VOLUME_NUM_VOICE_STEPS); /* set task to receive system messages */ MessageSystemTask(&theSink.task); INIT_DEBUG(("INIT: complete\n")); }
/**************************************************************************** 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 powerManagerHandleVbatCritical DESCRIPTION Called when the battery voltage is detected to be in critical state */ static void powerManagerHandleVbatCritical( void ) { PM_DEBUG(("PM: Battery Critical\n")); /* Reset low batt warning */ theSink.battery_state = POWER_BATT_CRITICAL; /* Power Off */ if(!powerManagerIsChargerConnected()) { powerManagerPowerOff(); } }
/******************************************************************************* NAME sinkUpgradePowerEventHandler DESCRIPTION Handle power events and pass relevant ones to the Upgrade library. The upgrade library is interested in two kinds of events: 1. When the library is in the 'normal' state then low battery when a charger is not connected triggers transition to the 'low battery error' state. 2. When the library is in the 'low battery error' state, then connecting a charger (or when battery level will magically change from low to ok without connecting a charger) enables transition back to the 'normal' state. PARAMETERS void RETURNS void */ void sinkUpgradePowerEventHandler(void) { upgrade_power_state_t power_state = upgrade_battery_ok; if(powerManagerIsChargerConnected()) { power_state = upgrade_charger_connected; } else if(powerManagerIsVbatLow() || powerManagerIsVbatCritical()) { /* Only when charger is not connected and battery level is low notify * the upgrade library. This is because purpose the upgrade library's * low battery handling is to prevent draining battery to much when * charger is not connected. */ power_state = upgrade_battery_low; } UpgradePowerManagementSetState(power_state); }
/**************************************************************************** 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()); }
/**************************************************************************** NAME usbGetChargeCurrent DESCRIPTION Get USB charger limits RETURNS void */ sink_charge_current* usbGetChargeCurrent(void) { /* USB charging not enabled - no limits */ if(!USB_CLASS_ENABLED(USB_DEVICE_CLASS_TYPE_BATTERY_CHARGING)) return NULL; USB_DEBUG(("USB: Status ")); /* Set charge current */ switch(UsbAttachedStatus()) { case HOST_OR_HUB: USB_DEBUG(("Host/Hub ")); if(theSink.usb.suspended) { USB_DEBUG(("Suspended (Battery %s)\n", usbDeadBatteryProvision() ? "Dead" : "Okay")); if(usbDeadBatteryProvision()) return &theSink.usb.config.i_susp_db; else return &theSink.usb.config.i_susp; } else if(powerManagerIsChargerFullCurrent()) { USB_DEBUG(("%sEnumerated (Chg Full)\n", theSink.usb.enumerated ? "" : "Not ")); if(!theSink.usb.enumerated) return &theSink.usb.config.i_att; else return &theSink.usb.config.i_conn; } else { USB_DEBUG(("%sEnumerated (Chg Partial)\n", theSink.usb.enumerated ? "" : "Not ")); if(!theSink.usb.enumerated) return &theSink.usb.config.i_att_trickle; else return &theSink.usb.config.i_conn_trickle; } #ifdef HAVE_FULL_USB_CHARGER_DETECTION case DEDICATED_CHARGER: USB_DEBUG(("Dedicated Charger Port%s\n", theSink.usb.vbus_okay ? "" : " Limited")); if(theSink.usb.vbus_okay) return &theSink.usb.config.i_dchg; else return &theSink.usb.config.i_lim; case HOST_OR_HUB_CHARGER: case CHARGING_PORT: USB_DEBUG(("Charger Port%s\n", theSink.usb.vbus_okay ? "" : " Limited")); if(theSink.usb.vbus_okay) return &theSink.usb.config.i_chg; else return &theSink.usb.config.i_lim; #endif case DETACHED: default: USB_DEBUG(("Detached\n")); if(powerManagerIsChargerConnected()) return &theSink.usb.config.i_disc; else return NULL; } }
/**************************************************************************** NAME powerManagerChargerSetup DESCRIPTION Update the charger settings based on USB limits and provided temperature reading. Updates current, trim, boost charge settings and enables or disables the charger. On BC7 this can also be used to select whether the chip draws power from VBAT or VBYP. RETURNS bool */ bool powerManagerChargerSetup(voltage_reading* vthm) { /* Get temperature limits */ sink_charge_current* usb_limits = usbGetChargeCurrent(); sink_charge_setting setting; PM_DEBUG(("PM: Set Current\n")); if(vthm) { PM_DEBUG(("PM: Temp 0x%02X %dmV\n", vthm->level, vthm->voltage)); setting = theSink.conf1->power.chg_settings[vthm->level]; } else { PM_DEBUG(("PM: No Temp Reading\n")); setting = default_charge_setting; } if(usb_limits) { /* Apply minimum settings from combined limits */ setting.current.charge = usb_limits->charge & setting.current.charge; if(usb_limits->boost < setting.current.boost) setting.current.boost = usb_limits->boost; setting.current.vsel = usb_limits->vsel | setting.current.vsel; setting.current.disable_leds = usb_limits->disable_leds | setting.current.disable_leds; if(usb_limits->current < setting.current.current) setting.current.current = usb_limits->current; } if(!powerManagerIsChargerConnected()) { /* Must apply these settings when charger removed */ setting.current.boost = power_boost_disabled; setting.current.charge = FALSE; #ifdef HAVE_VBAT_SEL setting.current.vsel = vsel_bat; } else if(setting.current.power_off) { /* If outside operating temp cannot run from battery */ setting.current.vsel = vsel_chg; #endif } switch(setting.termination.type) { case vterm_voltage: /* BC7 allows us to set absolute termination voltage */ PM_DEBUG(("PM: Termination Voltage %d\n", setting.termination.voltage * POWER_VSCALE)); if(!PowerChargerSetVterm(setting.termination.voltage)) { PM_DEBUG(("PM: Failed, disabling charger\n")); setting.current.charge = FALSE; } break; case vterm_trim: /* BC5 needs to modify trim setting */ PM_DEBUG(("PM: Trim Termination Voltage -%d\n", setting.termination.trim)); /* Disable charger if unable to trim */ if(!PowerChargerReduceTrim(setting.termination.trim)) { PM_DEBUG(("PM: Failed, disabling charger\n")); setting.current.charge = FALSE; } break; default: /* Use default termination voltage */ PM_DEBUG(("PM: Termination Voltage Unchanged\n")); break; } /* Disable LEDs if required */ PM_DEBUG(("PM: %s LEDs\n", setting.current.disable_leds ? "Disable" : "Enable")); #ifndef NO_LED LedManagerForceDisable(setting.current.disable_leds); #endif /* With VBAT_SEL we can wait for temp reading before enabling charger. Without we enable charger by default and may need to turn it off. */ if(vthm) { PM_DEBUG(("PM: Current %d, Boost 0x%X\n", setting.current.current, setting.current.boost)); PM_DEBUG(("PM: Charger %s\n", (setting.current.charge ? "Enabled" : "Disabled"))); /* Set charge current */ PowerChargerSetCurrent(setting.current.current); PowerChargerSetBoost(setting.current.boost); #ifdef HAVE_VBAT_SEL } /* Flip the switch to draw current from VBAT or VBYP */ PM_DEBUG(("PM: SEL %s\n", ((setting.current.vsel == vsel_bat) ? "VBAT" : "VBYP") )); PsuConfigure(PSU_VBAT_SWITCH, PSU_SMPS_INPUT_SEL_VBAT, ((setting.current.vsel == vsel_bat) ? TRUE : FALSE)); if(vthm) { #endif /* Enable/disable charger */ PowerChargerEnable(setting.current.charge); /* Power off */ if(setting.current.power_off) { PM_DEBUG(("PM: Power Off\n")); powerManagerPowerOff(); } } return((vthm) && (setting.current.charge)); }