/** \file vp880_linestate_control.c | |
* vp880_linestate_control.c | |
* | |
* This file contains the control functions for the Vp880 device API. | |
* | |
* Copyright (c) 2011, Microsemi | |
* | |
* $Revision: 1.1.2.1.8.3 $ | |
* $LastChangedDate: 2010-03-16 16:02:08 -0500 (Tue, 16 Mar 2010) $ | |
*/ | |
#include "../includes/vp_api_cfg.h" | |
#if defined (VP_CC_880_SERIES) | |
/* INCLUDES */ | |
#include "../../arch/uvb/vp_api_types.h" | |
#include "../../arch/uvb/vp_hal.h" | |
#include "../includes/vp_api_int.h" | |
#include "../includes/vp880_api.h" | |
#include "../vp880_api/vp880_api_int.h" | |
#include "../../arch/uvb/sys_service.h" | |
/**< Functions called by Set Line State to abstract device states used for ABS | |
* and non ABS devices (if they are different). Set line state then operates | |
* on device state only (abstracted from API-II line state). | |
*/ | |
#ifdef VP880_FXS_SUPPORT | |
#ifdef VP880_TRACKER_SUPPORT | |
static uint8 | |
Vp880GetLineStateNonABS( | |
VpLineCtxType *pLineCtx, | |
VpLineStateType state); | |
#endif | |
static bool | |
Vp880SetStateRinging( | |
VpLineCtxType *pLineCtx, | |
VpLineStateType state); | |
#endif | |
/** | |
* Vp880SetLineState() | |
* This function is the API-II wrapper function for Set Line State - Internal | |
* for the Vp880 API. | |
* | |
* Preconditions: | |
* Same as Vp880SetLineStateInt() | |
* | |
* Postconditions: | |
* Same as Vp880SetLineStateInt() | |
*/ | |
VpStatusType | |
Vp880SetLineState( | |
VpLineCtxType *pLineCtx, | |
VpLineStateType state) | |
{ | |
Vp880LineObjectType *pLineObj = pLineCtx->pLineObj; | |
VpStatusType status; | |
VpDevCtxType *pDevCtx = pLineCtx->pDevCtx; | |
Vp880DeviceObjectType *pDevObj = pDevCtx->pDevObj; | |
VpDeviceIdType deviceId = pDevObj->deviceId; | |
bool overrideSameState; | |
bool isFxs = FALSE; | |
VP_API_FUNC(VpLineCtxType, pLineCtx, ("Vp880SetLineState+")); | |
VpSysEnterCritical(deviceId, VP_CODE_CRITICAL_SEC); | |
/* | |
* Do not proceed unless device init has already completed or in progress. Also, don't proceed | |
* if device is currently running calibration | |
*/ | |
if ((!(pDevObj->state & (VP_DEV_INIT_CMP | VP_DEV_INIT_IN_PROGRESS))) || | |
(pDevObj->state & VP_DEV_IN_CAL)) { | |
VpSysExitCritical(deviceId, VP_CODE_CRITICAL_SEC); | |
VP_API_FUNC(VpLineCtxType, pLineCtx, ("Vp880SetLineState-")); | |
return VP_STATUS_DEV_NOT_INITIALIZED; | |
} | |
overrideSameState = FALSE; | |
isFxs = (((pLineObj->status & VP880_IS_FXO) == VP880_IS_FXO) ? FALSE : TRUE); | |
#ifdef VP880_FXS_SUPPORT | |
if (isFxs) { | |
if (!(VpCSLACIsSupportedFxsState(pDevCtx->deviceType, state))) { | |
VpSysExitCritical(deviceId, VP_CODE_CRITICAL_SEC); | |
VP_API_FUNC(VpLineCtxType, pLineCtx, ("-Vp890SetLineState() - Unsupported FXS State")); | |
return VP_STATUS_INVALID_ARG; | |
} | |
if (Vp880IsChnlUndrTst(pDevObj, pLineObj->channelId)) { | |
overrideSameState = TRUE; | |
} | |
if ((pLineObj->slicValueCache & VP880_SS_METERING_MASK) | |
#ifdef VP_CSLAC_SEQ_EN | |
|| (pLineObj->callerId.status & VP_CID_IN_PROGRESS) | |
#endif | |
) { | |
overrideSameState = TRUE; | |
} | |
} | |
#endif | |
#ifdef VP_CSLAC_SEQ_EN | |
if (pLineObj->cadence.status & VP_CADENCE_STATUS_ACTIVE) { | |
overrideSameState = TRUE; | |
} | |
#endif | |
if (!overrideSameState && (state == pLineObj->lineState.usrCurrent)) { | |
VpSysExitCritical(deviceId, VP_CODE_CRITICAL_SEC); | |
VP_LINE_STATE(VpLineCtxType, pLineCtx, ("Skipping VpSetLineState() for ch %d, same state %d, time %d", | |
pLineObj->channelId, state, pDevObj->timeStamp)); | |
VP_API_FUNC(VpLineCtxType, pLineCtx, ("Vp880SetLineState-")); | |
return VP_STATUS_SUCCESS; | |
} | |
VP_LINE_STATE(VpLineCtxType, pLineCtx, ("Setting Channel %d to State %d at time %d", | |
pLineObj->channelId, state, pDevObj->timeStamp)); | |
/* Clear the "called from API" flag. This affects the cadencer */ | |
pLineObj->status &= ~(VP880_SLS_CALL_FROM_API); | |
#ifdef VP880_FXS_SUPPORT | |
/* | |
* Special FXS handling to prevent setting the line to ringing if | |
* off-hook | |
*/ | |
if (isFxs) { | |
if ((pLineObj->lineState.condition & (VP_CSLAC_HOOK | VP_CSLAC_GKEY)) | |
&& ((state == VP_LINE_RINGING_POLREV) || (state == VP_LINE_RINGING))) { | |
/* | |
* Go to Ring Trip Exit state instead, which could be ringing -- but | |
* that's up to the application. | |
*/ | |
state = pLineObj->ringCtrl.ringTripExitSt; | |
} | |
#ifdef VP880_TRACKER_SUPPORT | |
if ((pDevObj->stateInt & VP880_IS_ABS) != VP880_IS_ABS) { | |
/* Stop the entering ringing timer and restore swRegParam */ | |
if (pDevObj->ringParams.channelArray[pLineObj->channelId] == TRUE) { | |
pDevObj->ringParams.channelArray[pLineObj->channelId] = FALSE; | |
if (pDevObj->ringParams.channelArray[0] == FALSE) { | |
if ((pDevObj->ringParams.swRegParam[1] & VP880_SWY_AUTOPOWER_MASK) == | |
VP880_SWY_AUTOPOWER_DIS) { | |
pDevObj->ringParams.swRegParam[1] |= VP880_SWY_AUTOPOWER_DIS; | |
} else { | |
pDevObj->ringParams.swRegParam[1] &= ~VP880_SWY_AUTOPOWER_DIS; | |
} | |
} | |
if (pDevObj->ringParams.channelArray[1] == FALSE) { | |
if ((pDevObj->ringParams.swRegParam[1] & VP880_SWZ_AUTOPOWER_MASK) == | |
VP880_SWZ_AUTOPOWER_DIS) { | |
pDevObj->ringParams.swRegParam[1] |= VP880_SWZ_AUTOPOWER_DIS; | |
} else { | |
pDevObj->ringParams.swRegParam[1] &= ~VP880_SWZ_AUTOPOWER_DIS; | |
} | |
} | |
if ((pDevObj->ringParams.channelArray[0] == FALSE) && | |
(pDevObj->ringParams.channelArray[1] == FALSE)) { | |
VP_LINE_STATE(VpLineCtxType, pLineCtx,("Vp880LimitInRushCurrent(): kill the timer")); | |
pDevObj->devTimer[VP_DEV_TIMER_ENTER_RINGING] = 0; | |
} | |
if ((pDevObj->swParamsCache[0] != pDevObj->ringParams.swRegParam[0]) | |
|| (pDevObj->swParamsCache[1] != pDevObj->ringParams.swRegParam[1]) | |
|| (pDevObj->swParamsCache[2] != pDevObj->ringParams.swRegParam[2])) { | |
VpMpiCmdWrapper(deviceId, pDevObj->ecVal, VP880_REGULATOR_PARAM_WRT, | |
VP880_REGULATOR_PARAM_LEN, pDevObj->ringParams.swRegParam); | |
VpMemCpy(pDevObj->swParamsCache, pDevObj->ringParams.swRegParam, | |
VP880_REGULATOR_PARAM_LEN); | |
VP_LINE_STATE(VpDevCtxType, pDevCtx, | |
("Ringing Exit Timer Management Switcher Programming: 0x%02X 0x%02X 0x%02X time %d", | |
pDevObj->swParamsCache[0], pDevObj->swParamsCache[1], pDevObj->swParamsCache[2], | |
pDevObj->timeStamp)); | |
} | |
} | |
} | |
#endif /* VP880_TRACKER_SUPPORT */ | |
} | |
#endif /* VP880_FXS_SUPPORT */ | |
status = Vp880SetLineStateInt(pLineCtx, state); | |
if (status == VP_STATUS_SUCCESS) { | |
/* | |
* Reset the "Count" for leaky line conditions because there are some | |
* normal state change conditions that will increment the count, therefore | |
* causing exit of LP for non-leaky line | |
*/ | |
#ifdef VP880_LP_SUPPORT /* Forced to undef if FXS Support is undef (see vp_api_cfg_int.h) */ | |
pLineObj->leakyLineCnt = 0; | |
#endif | |
pLineObj->lineState.usrCurrent = state; | |
} | |
/* | |
* Set the "called from API" flag. Convenience for API functions so setting | |
* this flag does not need to occur in multiple locations | |
*/ | |
pLineObj->status |= VP880_SLS_CALL_FROM_API; | |
VpSysExitCritical(deviceId, VP_CODE_CRITICAL_SEC); | |
VP_API_FUNC(VpLineCtxType, pLineCtx, ("Vp880SetLineState-")); | |
return status; | |
} | |
/** | |
* Vp880SetLineStateInt() | |
* This function sets the line state for a given channel of a given device. The valid line states | |
* are defined in the VpLineState type. | |
* | |
* Preconditions: | |
* The line must first be initialized prior to setting the line state. The state must be a valid | |
* line state as defined in the VpLineState type. | |
* | |
* Postconditions: | |
* Returns success code if the channel can be set and the line state is valid for the type of line | |
* specified by the line context. If successfull, the line specified by the line context is set to | |
* the line state specified. | |
*/ | |
VpStatusType | |
Vp880SetLineStateInt( | |
VpLineCtxType *pLineCtx, /**< Line context to change line state on */ | |
VpLineStateType state) /**< The desired line state to set */ | |
{ | |
#ifdef VP880_FXS_SUPPORT | |
uint8 userByte = 0xFF; | |
uint8 currentStateByte; | |
bool disconnectTimerSet = FALSE; | |
bool feedToDisable = FALSE; | |
bool polarityInversion = FALSE; | |
#endif /* VP880_FXS_SUPPORT */ | |
uint8 mpiByte = 0; | |
VpDevCtxType *pDevCtx = pLineCtx->pDevCtx; | |
VpStatusType status = VP_STATUS_SUCCESS; | |
Vp880LineObjectType *pLineObj = pLineCtx->pLineObj; | |
Vp880DeviceObjectType *pDevObj = pDevCtx->pDevObj; | |
uint8 ecVal = pLineObj->ecVal; | |
uint8 mpiIndex = 0; | |
uint8 mpiBuffer[3 + VP880_OP_COND_LEN + VP880_LOOP_SUP_LEN + VP880_GEN_CTRL_LEN]; | |
VpDeviceIdType deviceId = pDevObj->deviceId; | |
VpLineStateType currentState = pLineObj->lineState.currentState; | |
#ifdef VP_CSLAC_SEQ_EN | |
bool disableTones = TRUE; | |
VpSeqDataType *pCadence = &pLineObj->cadence; | |
#endif /* VP_CSLAC_SEQ_EN */ | |
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880SetLineStateInt+")); | |
#if defined (VP880_FXS_SUPPORT) && defined (VP_HIGH_GAIN_MODE_SUPPORTED) | |
/* | |
* Take care of Howler To/From State conditions. Note: Calling this function does NOT put | |
* the line INTO the Howler State. It only verifies that the conditions for entering Howler | |
* state are correct. It does however handle exiting the Howler Tone State. | |
*/ | |
if (((state == VP_LINE_HOWLER) || (state == VP_LINE_HOWLER_POLREV)) && | |
(currentState == state)) { | |
/* | |
* Same state changes are done here because the VpCSLACHowlerStateMgmt function handles | |
* illegal transitions and the upper level SetLineState function will allow setting the | |
* line to a Howler State during Tone Cadencing. So it can be called in a bypass mode for | |
* the same state. | |
*/ | |
return VP_STATUS_SUCCESS; | |
} | |
status = VpCSLACHowlerStateMgmt(pLineCtx, pLineObj->lineState.usrCurrent, | |
state, pLineObj->lineState.previous); | |
if (status != VP_STATUS_SUCCESS) { | |
return status; | |
} | |
if (((currentState == VP_LINE_HOWLER) || (currentState == VP_LINE_HOWLER_POLREV)) && | |
((state != VP_LINE_HOWLER) && (state != VP_LINE_HOWLER_POLREV))) { | |
VP_LINE_STATE(VpLineCtxType, pLineCtx, | |
("Howler Exit at time (%d) Starting Hook Freeze Timer", pDevObj->timeStamp)); | |
VpCSLACSetTimer(&pLineObj->lineTimers.timers.timer[VP_LINE_HOOK_FREEZE], | |
MS_TO_TICKRATE(30, pDevObj->devProfileData.tickRate)); | |
} | |
#endif /* defined (VP880_FXS_SUPPORT) && defined (VP_HIGH_GAIN_MODE_SUPPORTED) */ | |
/* Modify Operating conditions ONLY if not running calibration */ | |
if (pLineObj->lineState.calType == VP_CSLAC_CAL_NONE) { | |
uint8 opCondTarget = pLineObj->opCond[0]; | |
/* | |
* Read the status of the Operating Conditions register so we can change | |
* only the TX and RX if the line state is a non-communication mode. | |
* This also performs the line type/state verification. | |
*/ | |
opCondTarget &= (uint8)(~(VP880_CUT_TXPATH | VP880_CUT_RXPATH)); | |
opCondTarget &= (uint8)(~(VP880_HIGH_PASS_DIS | VP880_OPCOND_RSVD_MASK)); | |
status = Vp880GetTxRxPcmMode(pLineObj, state, &mpiByte); | |
if (status == VP_STATUS_SUCCESS) { | |
opCondTarget |= mpiByte; | |
} else { | |
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880SetLineStateInt-")); | |
return status; | |
} | |
if (opCondTarget != pLineObj->opCond[0]) { | |
pLineObj->opCond[0] = opCondTarget; | |
VP_LINE_STATE(VpLineCtxType, pLineCtx, ("2. Writing 0x%02X to Operating Conditions", | |
opCondTarget)); | |
VpMpiCmdWrapper(deviceId, ecVal, VP880_OP_COND_WRT, VP880_OP_COND_LEN, | |
pLineObj->opCond); | |
} | |
} | |
#ifdef VP_CSLAC_SEQ_EN | |
/* We're no longer in the middle of a time function */ | |
pCadence->status &= ~VP_CADENCE_STATUS_MID_TIMER; | |
pCadence->timeRemain = 0; | |
#endif /* VP_CSLAC_SEQ_EN */ | |
/* | |
* If this function is called by the application, stop the cadencer and | |
* reset the Loop Supervision if it is incorrect | |
*/ | |
if (!(pLineObj->status & VP880_SLS_CALL_FROM_API)) { | |
uint8 sigGenCtrl[VP880_GEN_CTRL_LEN]; | |
#ifdef VP_CSLAC_SEQ_EN | |
/* Abort ongoing metering cadences */ | |
if ((pCadence->status & VP_CADENCE_STATUS_METERING) && | |
(pCadence->status & VP_CADENCE_STATUS_ACTIVE)) { | |
if (pLineObj->slicValueCache & VP880_SS_METERING_MASK) { | |
/* Currently in the middle of a metering pulse. Schedule a | |
* pending abort, but don't change anything yet */ | |
pCadence->meterPendingAbort = TRUE; | |
pCadence->meterAbortLineState = state; | |
return VP_STATUS_SUCCESS; | |
} else { | |
/* If not in the middle of a pulse, terminate the cadence. */ | |
pLineObj->lineEvents.process |= VP_LINE_EVID_MTR_ABORT; | |
pLineObj->processData = pLineObj->cadence.meteringBurst; | |
pCadence->status = VP_CADENCE_RESET_VALUE; | |
pCadence->pActiveCadence = VP_PTABLE_NULL; | |
} | |
} else if (pLineObj->slicValueCache & VP880_SS_METERING_MASK) { | |
/* If metering is on without an active cadence, terminate the | |
* forever-on metering signal */ | |
pLineObj->lineEvents.process |= VP_LINE_EVID_MTR_ABORT; | |
pLineObj->processData = pLineObj->cadence.meteringBurst; | |
} | |
#endif /* VP_CSLAC_SEQ_EN */ | |
if (pLineObj->status & VP880_BAD_LOOP_SUP) { | |
pLineObj->status &= ~(VP880_BAD_LOOP_SUP); | |
mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, VP880_LOOP_SUP_WRT, | |
VP880_LOOP_SUP_LEN, pLineObj->loopSup); | |
} | |
/* | |
* Disable tones and cadencing if going to a state that prevents it and | |
* in all cases for the FXO line | |
*/ | |
switch(state) { | |
case VP_LINE_STANDBY: | |
case VP_LINE_STANDBY_POLREV: | |
case VP_LINE_TIP_OPEN: | |
case VP_LINE_DISCONNECT: | |
case VP_LINE_RINGING: | |
case VP_LINE_RINGING_POLREV: | |
case VP_LINE_FXO_LOOP_OPEN: | |
case VP_LINE_FXO_LOOP_CLOSE: | |
case VP_LINE_FXO_RING_GND: | |
case VP_LINE_FXO_OHT: | |
case VP_LINE_FXO_TALK: | |
/* | |
* Disable signal generator A/B/C/D before making any changes and | |
* stop previous cadences. | |
*/ | |
sigGenCtrl[0] = 0; | |
if (sigGenCtrl[0] != pLineObj->sigGenCtrl[0]) { | |
pLineObj->sigGenCtrl[0] = sigGenCtrl[0]; | |
mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, VP880_GEN_CTRL_WRT, | |
VP880_GEN_CTRL_LEN, pLineObj->sigGenCtrl); | |
} | |
break; | |
default: | |
/* Stop also if coming from Ringing */ | |
if ((pLineObj->lineState.usrCurrent != VP_LINE_RINGING) && | |
(pLineObj->lineState.usrCurrent != VP_LINE_RINGING_POLREV)) { | |
#ifdef VP_CSLAC_SEQ_EN | |
/* Keep tones/cadencing running */ | |
disableTones = FALSE; | |
#endif /* VP_CSLAC_SEQ_EN */ | |
} | |
break; | |
} | |
/* send down the previous mpi commands if there are any */ | |
if (mpiIndex > 0) { | |
VpMpiCmdWrapper(deviceId, ecVal, mpiBuffer[0], mpiIndex-1, &mpiBuffer[1]); | |
mpiIndex = 0; | |
} | |
#ifdef VP_CSLAC_SEQ_EN | |
if (disableTones == TRUE) { | |
pCadence->status = VP_CADENCE_RESET_VALUE; | |
pCadence->pActiveCadence = VP_PTABLE_NULL; | |
} | |
#ifdef VP880_FXS_SUPPORT | |
/* If the user is changing the line state stop callerId */ | |
if (pLineObj->callerId.status & VP_CID_IN_PROGRESS) { | |
VpCliStopCli(pLineCtx); | |
} | |
#endif /* VP880_FXS_SUPPORT */ | |
#endif /* VP_CSLAC_SEQ_EN */ | |
} /* if NOT called from Inside the API */ | |
/* FXO TYPE LINE HANDLING */ | |
if (pLineObj->status & VP880_IS_FXO) { | |
} else { /* FXS Handling */ | |
#ifdef VP880_FXS_SUPPORT | |
if (pDevObj->stateInt & VP880_IS_ABS) { | |
#ifdef VP880_ABS_SUPPORT | |
userByte = Vp880GetLineStateABS(pLineCtx, state, FALSE); | |
#endif /* VP880_ABS_SUPPORT */ | |
} else { | |
#ifdef VP880_TRACKER_SUPPORT | |
userByte = Vp880GetLineStateNonABS(pLineCtx, state); | |
#endif /* VP880_TRACKER_SUPPORT */ | |
} | |
if (userByte == 0xFF) { | |
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880SetLineStateInt-")); | |
return VP_STATUS_INVALID_ARG; | |
} | |
/* Modify userByte depending on the current polarity */ | |
currentStateByte = pLineObj->slicValueCache; | |
VP_LINE_STATE(VpLineCtxType, pLineCtx, | |
("Fetched userByte 0x%02X Compare with currentByte 0x%02X for Chan %d State %d Line Status 0x%04X", | |
userByte, currentStateByte, pLineObj->channelId, state, pLineObj->status)); | |
#ifdef VP_CSLAC_SEQ_EN | |
if (pCadence->pActiveCadence != VP_NULL) { | |
if ((pCadence->status & | |
(VP_CADENCE_STATUS_ACTIVE | VP_CADENCE_STATUS_IGNORE_POLARITY)) == | |
(VP_CADENCE_STATUS_ACTIVE | VP_CADENCE_STATUS_IGNORE_POLARITY)) { | |
userByte &= ~VP880_SS_POLARITY_MASK; | |
userByte |= (currentStateByte & VP880_SS_POLARITY_MASK); | |
} | |
} | |
#endif /* VP_CSLAC_SEQ_EN */ | |
if ((state == VP_LINE_RINGING) || (state == VP_LINE_RINGING_POLREV) | |
|| (currentState == VP_LINE_RINGING) || (currentState == VP_LINE_RINGING_POLREV)) { | |
/* | |
* Set State Ringing Returns TRUE if the line has NOT actually been changed. So return | |
* at this point results in preventing the internal line state values to be updated | |
* until the line is changed by the cadencer. | |
*/ | |
if (Vp880SetStateRinging(pLineCtx, state) == TRUE) { | |
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880SetLineStateInt-")); | |
return VP_STATUS_SUCCESS; | |
} | |
if ((state != VP_LINE_RINGING) && (state != VP_LINE_RINGING_POLREV)) { | |
/* | |
* Ringing exit scenario. We have to prevent Polarity Reversals from occuring while | |
* ringing is on the line otherwise the polarity reversal will occur during ringing | |
* which creates an undesirable transient on the line. | |
*/ | |
/* Save the desired slic byte to be used in the ring exit timer */ | |
pLineObj->nextSlicValue = userByte; | |
/* Check if polarity reversal is being performed. */ | |
if ((currentStateByte & VP880_SS_POLARITY_MASK) ^ (userByte & VP880_SS_POLARITY_MASK)) { | |
/* | |
* Polarity reversal is in this ring exit transition, so force the new slic | |
* state polarity to the same polarity as current ringing. | |
*/ | |
userByte &= ~VP880_SS_POLARITY_MASK; | |
userByte |= (currentStateByte & VP880_SS_POLARITY_MASK); | |
} | |
} | |
} | |
#ifdef VP880_LP_SUPPORT | |
if ((VpIsLowPowerTermType(pLineObj->termType)) | |
&& (pLineObj->lineTimers.timers.timer[VP_LINE_DISCONNECT_EXIT] & VP_ACTIVATE_TIMER)) { | |
pLineObj->nextSlicValue = userByte; | |
if ((pLineObj->lineState.currentState == VP_LINE_STANDBY) && | |
((state != VP_LINE_STANDBY) && (state != VP_LINE_DISCONNECT))) { | |
/* | |
* Disconnect Exit to LPM-Standby was started but we're now | |
* changing to non-LPM. Need to correct ICR values. | |
*/ | |
Vp880SetLPRegisters(pLineObj, FALSE); | |
Vp880WriteLPExitRegisters(pLineCtx, deviceId, pLineObj->ecVal, &userByte); | |
Vp880LowPowerMode(pDevCtx); | |
} | |
} | |
#endif /* VP880_LP_SUPPORT */ | |
/* | |
* Enable Disconnect Recovery time for hook status if going FROM | |
* Disconnect to a state that can detect off-hook | |
*/ | |
/* Coming from Disconnect...*/ | |
if ((pLineObj->lineState.currentState == VP_LINE_DISCONNECT) && | |
/* ..going to a state that can detect some feed condition */ | |
((state != VP_LINE_DISCONNECT) && (state != VP_LINE_RING_OPEN))) { | |
if (!(pLineObj->lineState.condition & VP_CSLAC_LINE_LEAK_TEST)) { | |
VpCSLACSetTimer(&pLineObj->lineTimers.timers.timer[VP_LINE_DISCONNECT_EXIT], | |
MS_TO_TICKRATE(VP_DISCONNECT_RECOVERY_TIME, | |
pDevObj->devProfileData.tickRate)); | |
/* Initialize the Disconnect Exit Timer State */ | |
pLineObj->discTimerExitState = 0; | |
VP_LINE_STATE(VpLineCtxType, pLineCtx, | |
("Chan %d Setting VP_LINE_DISCONNECT_EXIT time to %d ms (VP_DISCONNECT_RECOVERY_TIME) at time %d line status 0x%04X", | |
pLineObj->channelId, VP_DISCONNECT_RECOVERY_TIME, pDevObj->timeStamp, pLineObj->lineState.condition)); | |
disconnectTimerSet = TRUE; | |
} | |
} | |
/* | |
* Set the disable flag if disabling feed. This prevents the | |
* polrev timer from starting. | |
*/ | |
if ((state == VP_LINE_DISCONNECT) || (state == VP_LINE_TIP_OPEN) | |
|| (state == VP_LINE_RING_OPEN)) { | |
feedToDisable = TRUE; | |
} | |
/* | |
* Set Polarity Reverse timer if the SLIC is changing polarity. Exclude | |
* Disconnect type recovery conditions since a timer is set above for | |
* those conditions. | |
*/ | |
if ((currentStateByte & VP880_SS_POLARITY_MASK) != (userByte & VP880_SS_POLARITY_MASK)) { | |
/* | |
* Set this to mark the condition that requires loading of new | |
* calibration coefficients. | |
*/ | |
polarityInversion = TRUE; | |
/* | |
* Timer is set if not exiting disconnect and staying in feed | |
* conditions. | |
*/ | |
if ((disconnectTimerSet == FALSE) && (feedToDisable == FALSE)) { | |
VP_LINE_STATE(VpLineCtxType, pLineCtx, | |
("Setting Polarity Reversal Timer on channel %d at time %d status 0x%04X", | |
pLineObj->channelId, pDevObj->timeStamp, pLineObj->lineState.condition)); | |
pLineObj->lineTimers.timers.timer[VP_LINE_HOOK_FREEZE] = | |
(MS_TO_TICKRATE(VP_POLREV_DEBOUNCE_TIME, | |
pDevObj->devProfileData.tickRate) | VP_ACTIVATE_TIMER); | |
} | |
} | |
if (pLineObj->calLineData.calDone == TRUE) { | |
if ((polarityInversion == TRUE) || | |
(pLineObj->calLineData.forceCalDataWrite == TRUE)) { | |
pLineObj->calLineData.forceCalDataWrite = FALSE; | |
if (userByte & VP880_SS_POLARITY_MASK) { | |
VP_LINE_STATE(VpLineCtxType, pLineCtx, | |
("Updating DC Feed PolRev 0x%02X 0x%02X for Byte 0x%02X on channel %d at time %d", | |
pLineObj->calLineData.dcFeedPr[0], pLineObj->calLineData.dcFeedPr[1], | |
userByte, pLineObj->channelId, pDevObj->timeStamp)); | |
VpMpiCmdWrapper(deviceId, ecVal, VP880_DC_FEED_WRT, | |
VP880_DC_FEED_LEN, pLineObj->calLineData.dcFeedPr); | |
} else { | |
VP_LINE_STATE(VpLineCtxType, pLineCtx, | |
("Updating DC Feed Normal 0x%02X 0x%02X for Byte 0x%02X on channel %d at time %d", | |
pLineObj->calLineData.dcFeed[0], pLineObj->calLineData.dcFeed[1], | |
userByte, pLineObj->channelId, pDevObj->timeStamp)); | |
VpMpiCmdWrapper(deviceId, ecVal, VP880_DC_FEED_WRT, | |
VP880_DC_FEED_LEN, pLineObj->calLineData.dcFeed); | |
} | |
} | |
} | |
/* | |
* Update the line object previous and current line state variables. | |
* Note that this is not updated with ringing cadence until the line is | |
* actually set to ringing (i.e., not when the application sets the line | |
* to ringing with (possibly) a non-ringing state via the ringing | |
* cadence. | |
*/ | |
pLineObj->lineState.previous = currentState; | |
pLineObj->lineState.currentState = state; | |
VP_LINE_STATE(VpLineCtxType, pLineCtx, | |
("In Vp880SetLineStateInt() Current state %d, next State %d Byte 0x%02X Chan %d at time %d", | |
currentState, state, userByte, pLineObj->channelId, pDevObj->timeStamp)); | |
#ifdef VP880_LP_SUPPORT | |
if ((VpIsLowPowerTermType(pLineObj->termType)) | |
/* | |
* If going from/to Disconnect/Standby in LPM, states are the same | |
* so don't continue. | |
*/ | |
&& ((state == VP_LINE_DISCONNECT) || (currentState == VP_LINE_DISCONNECT))) { | |
if ((currentState == state) && (Vp880IsChnlUndrTst(pDevObj, pLineObj->channelId) == FALSE)) { | |
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880SetLineStateInt-")); | |
return VP_STATUS_SUCCESS; | |
} else { | |
if (currentState == VP_LINE_TIP_OPEN) { | |
Vp880GroundStartProc(FALSE, pLineCtx, currentStateByte, userByte); | |
} | |
if (state == VP_LINE_DISCONNECT) { | |
Vp880RunLPDisc(pLineCtx, TRUE, userByte); | |
} else { | |
Vp880RunLPDisc(pLineCtx, FALSE, userByte); | |
} | |
} | |
} else { | |
#endif /* VP880_LP_SUPPORT */ | |
if ((userByte & VP880_SS_LINE_FEED_MASK) == VP880_SS_TIP_OPEN) { | |
Vp880GroundStartProc(TRUE, pLineCtx, currentStateByte, userByte); | |
} else { | |
Vp880GroundStartProc(FALSE, pLineCtx, currentStateByte, userByte); | |
} | |
#ifdef VP880_LP_SUPPORT | |
} | |
#endif /* VP880_LP_SUPPORT */ | |
#ifdef VP_HIGH_GAIN_MODE_SUPPORTED | |
/* If going to the Howler State, set registers after all other changes have been made */ | |
if ((state == VP_LINE_HOWLER) || (state == VP_LINE_HOWLER_POLREV)) { | |
/* | |
* This error check should be unnecessary since the code at the top of this function | |
* returns immediately if going to/from any combination of howler states. But since | |
* doing this will result in permanent incorrect setting of the DC Feed and ICR | |
* registers, do everything possible to prevent it. | |
*/ | |
if ((pLineObj->lineState.previous != VP_LINE_HOWLER) && | |
(pLineObj->lineState.previous != VP_LINE_HOWLER_POLREV)) { | |
VpCLSACHighGainMode(pLineCtx, TRUE); | |
} else { | |
VP_ERROR(VpLineCtxType, pLineCtx, | |
("Howler Change from (%d) To (%d)", pLineObj->lineState.previous, state)); | |
} | |
} | |
#endif /* VP_HIGH_GAIN_MODE_SUPPORTED */ | |
#endif /* VP880_FXS_SUPPORT */ | |
} | |
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880SetLineStateInt-")); | |
return status; | |
} /* Vp880SetLineStateInt */ | |
#ifdef VP880_FXS_SUPPORT | |
/** | |
* Vp880SetStateRinging() | |
* This function starts cadence and non-cadence ringing, as well as ringing exit. | |
* | |
* Preconditions: | |
* None. Calling function must know that this code should execute. | |
* | |
* Postconditions: | |
* Line object is modified if ringing cadence or exiting (timers). If not cadencing and ringing | |
* is started, then return TRUE. Otherwise return FALSE. | |
*/ | |
bool | |
Vp880SetStateRinging( | |
VpLineCtxType *pLineCtx, | |
VpLineStateType state) | |
{ | |
/* Extract Line information used often in this function */ | |
Vp880LineObjectType *pLineObj = pLineCtx->pLineObj; | |
uint8 ecVal = pLineObj->ecVal; | |
uint8 channelId = pLineObj->channelId; | |
VpLineStateType currentState = pLineObj->lineState.currentState; | |
/* Extract Device information used often in this function */ | |
VpDevCtxType *pDevCtx = pLineCtx->pDevCtx; | |
Vp880DeviceObjectType *pDevObj = pDevCtx->pDevObj; | |
VpDeviceIdType deviceId = pDevObj->deviceId; | |
#ifdef VP_CSLAC_SEQ_EN | |
VpProfilePtrType pProfile; | |
VpSeqDataType *pCadence = &pLineObj->cadence; | |
#endif /* VP_CSLAC_SEQ_EN */ | |
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880SetStateRinging+")); | |
VP_LINE_STATE(VpLineCtxType, pLineCtx, | |
("Ringing Control for channel %d: CurrentState %d, UserState %d NextState %d", | |
channelId, currentState, pLineObj->lineState.usrCurrent, state)); | |
if ((state == VP_LINE_RINGING) || (state == VP_LINE_RINGING_POLREV)) { | |
VP_LINE_STATE(VpLineCtxType, pLineCtx, | |
("Ringing Enter on Ch (%d) time (%d)", channelId, pDevObj->timeStamp)); | |
#ifdef VP_CSLAC_SEQ_EN | |
pCadence->pActiveCadence = pLineObj->pRingingCadence; | |
/* | |
* We're entering a ringing state, so determine if we need to cadence. If we're not | |
* cadencing, this is "always on", so we can disable the currently active cadence sequence | |
* and immediately implement the ringing state change. | |
*/ | |
pProfile = pLineObj->cadence.pActiveCadence; | |
if (pProfile == VP_PTABLE_NULL) { | |
pCadence->status = VP_CADENCE_RESET_VALUE; | |
} else { | |
/* | |
* We have a non-null cadence. If the cadence was not previously started, we'll start | |
* it here and let the sequencer take over. Otherwise, it was previously started and | |
* this state change is at the request of the sequencer. | |
*/ | |
if (!(pCadence->status & VP_CADENCE_STATUS_ACTIVE)) { | |
/* We have a cadence and are just starting it */ | |
pCadence->status |= VP_CADENCE_STATUS_ACTIVE; | |
pCadence->index = VP_PROFILE_TYPE_SEQUENCER_START; | |
pCadence->pCurrentPos = &pProfile[VP_PROFILE_TYPE_SEQUENCER_START]; | |
pCadence->length = pProfile[VP_PROFILE_LENGTH]; | |
pCadence->status &= ~VP_CADENCE_STATUS_IGNORE_POLARITY; | |
pCadence->status |= | |
(pProfile[VP_PROFILE_MPI_LEN] & 0x01) ? VP_CADENCE_STATUS_IGNORE_POLARITY : 0; | |
/* | |
* Nullify any internal sequence so that the API doesn't think that an internal | |
* sequence of some sort is running | |
*/ | |
pLineObj->intSequence[VP_PROFILE_TYPE_LSB] = VP_PRFWZ_PROFILE_NONE; | |
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880SetStateRinging-")); | |
return TRUE; | |
} | |
} | |
#endif /* VP_CSLAC_SEQ_EN */ | |
/* | |
* Cadencing already called or null cadence. We're ready to set the line to the Ringing | |
* State but we have to first make sure that the signal generator parameters in the device | |
* are setup for the ringing profile | |
*/ | |
/* | |
* This step is only necessary if called internal to the VP-API-II. If externally called, | |
* generators are disabled by higher level state control function. | |
*/ | |
if (pLineObj->status & VP880_SLS_CALL_FROM_API) { | |
uint8 tempRingCtrlData = VP880_GEN_ALLOFF; | |
if (tempRingCtrlData != pLineObj->sigGenCtrl[0]) { | |
pLineObj->sigGenCtrl[0] = tempRingCtrlData; | |
VpMpiCmdWrapper(deviceId, ecVal, VP880_GEN_CTRL_WRT, | |
VP880_GEN_CTRL_LEN, pLineObj->sigGenCtrl); | |
} | |
} | |
if (pLineObj->ringingParams != VP_PTABLE_NULL) { | |
if (((pLineObj->status & VP880_RING_GEN_NORM) && (state == VP_LINE_RINGING)) | |
|| ((pLineObj->status & VP880_RING_GEN_REV) && (state == VP_LINE_RINGING_POLREV))) { | |
/* Last generator programming was with the ringnig parameters. Nothing to do */ | |
} else { | |
int16 biasErr; | |
uint8 bias[VP880_RINGER_PARAMS_LEN]; | |
VpMemCpy(bias, pLineObj->ringingParams, VP880_RINGER_PARAMS_LEN); | |
biasErr = (int16)((((uint16)(pLineObj->ringingParams[1]) << 8) & 0xFF00) + | |
((uint16)(pLineObj->ringingParams[2]) & 0x00FF)); | |
if (state == VP_LINE_RINGING) { | |
/* Normal polarity */ | |
biasErr -= ((pDevObj->vp880SysCalData.sigGenAError[channelId][0] - | |
pDevObj->vp880SysCalData.vocOffset[channelId][VP880_NORM_POLARITY]) * 16 / 10); | |
pLineObj->status |= VP880_RING_GEN_NORM; | |
pLineObj->status &= ~VP880_RING_GEN_REV; | |
} else { | |
/* Reverse polarity */ | |
biasErr += ((pDevObj->vp880SysCalData.sigGenAError[channelId][0] - | |
pDevObj->vp880SysCalData.vocOffset[channelId][VP880_REV_POLARITY]) * 16 / 10); | |
pLineObj->status |= VP880_RING_GEN_REV; | |
pLineObj->status &= ~VP880_RING_GEN_NORM; | |
} | |
bias[1] = (uint8)((biasErr >> 8) & 0x00FF); | |
bias[2] = (uint8)(biasErr & 0x00FF); | |
VpMpiCmdWrapper(deviceId, ecVal, VP880_RINGER_PARAMS_WRT, | |
VP880_RINGER_PARAMS_LEN, bias); | |
} | |
} | |
/* | |
* If the application chose auto-ring trip as determined by a non-ringing ring-trip-exit | |
* state, enable the silicon Auto-State Changes (Auto-Ring Trip). This provides the fastest | |
* ringing removal. When changing the line to a non-ringing state the silicon waits for a | |
* zero crossing to actually remove ringing. If using only the API then it's possible the | |
* API would miss a recent zero crossing in which case the silicon wouldn't remove ringing | |
* until the next zero crossing - much later. When exiting ringing, we'll restore this | |
* bit back to non-Auto-State control. | |
*/ | |
{ | |
uint8 ssCfg[VP880_SS_CONFIG_LEN] = {0x00}; | |
if ((pLineObj->ringCtrl.ringTripExitSt != VP_LINE_RINGING) | |
&& (pLineObj->ringCtrl.ringTripExitSt != VP_LINE_RINGING_POLREV)) { | |
VpMpiCmdWrapper(deviceId, ecVal, VP880_SS_CONFIG_RD, VP880_SS_CONFIG_LEN, ssCfg); | |
ssCfg[0] &= ~VP880_AUTO_SSC_DIS; | |
VpMpiCmdWrapper(deviceId, ecVal, VP880_SS_CONFIG_WRT, VP880_SS_CONFIG_LEN, ssCfg); | |
} | |
} | |
#ifdef VP880_ABS_SUPPORT | |
/* | |
* Disable the ABS Ringing Exit Timer if previously running. Enable device HPM -- BUT only | |
* do this for single control systems. No need to do this for slave which are not in control | |
* of their own supplies, and can't do this for Master supply systems because another device | |
* being fed by this one may require HP. | |
*/ | |
if ((pDevObj->stateInt & VP880_IS_ABS) && | |
((pDevObj->devProfileData.systemConfig & VP880_ABS_CFG_MASK) == VP880_ABS_CFG_SINGLE)) { | |
uint8 regCtrl = VP880_SWY_HP | VP880_SWZ_HP; | |
/* | |
* Disable the currently running time that will try to change the supply mode back to | |
* Medium Power. This is normal during ring cadence condition. The timer only has a | |
* chance to expire when ringing cadence is complete via ring trip or when the calling | |
* party "gives up" (i.e., VpSetLineState() is called with non-Ringing state) | |
*/ | |
pDevObj->devTimer[VP_DEV_TIMER_EXIT_RINGING] = 0; | |
VpMpiCmdWrapper(deviceId, ecVal, VP880_REGULATOR_CTRL_WRT, | |
VP880_REGULATOR_CTRL_LEN, ®Ctrl); | |
} | |
#endif /* VP880_ABS_SUPPORT */ | |
#ifdef VP_CSLAC_SEQ_EN | |
/* | |
* If we're in an active Ringing Cadence, and ready to go into the Ringing state, generate | |
* the Ringing Event and indicate that this is the Ringing On event | |
*/ | |
if ((pCadence->status & VP_CADENCE_STATUS_ACTIVE) | |
&& (pProfile[VP_PROFILE_TYPE_LSB] == VP_PRFWZ_PROFILE_RINGCAD)) { | |
pLineObj->lineEvents.process |= VP_LINE_EVID_RING_CAD; | |
pLineObj->processData = VP_RING_CAD_MAKE; | |
} | |
#endif /* VP_CSLAC_SEQ_EN */ | |
#ifdef VP880_TRACKER_SUPPORT | |
if ((!(pDevObj->stateInt & VP880_IS_ABS)) /* Tracker Device */ | |
/* Disable HV Clamps ONLY when Ringing AND operating in High Voltage mode. */ | |
&& (pDevObj->stateInt & VP880_IS_HIGH_VOLTAGE) | |
&& (pDevObj->devProfileData.lowVoltOverride == FALSE)) { | |
pLineObj->icr2Values[VP880_ICR2_SWY_CTRL_INDEX+1] |= VP880_ICR2_SWY_LIM_CTRL; | |
VP_LINE_STATE(VpLineCtxType, pLineCtx, | |
("Disable HV Clamps: ICR2_WRT 0x%02X 0x%02X 0x%02X 0x%02X Ch %d Time %d", | |
pLineObj->icr2Values[0], pLineObj->icr2Values[1], | |
pLineObj->icr2Values[2], pLineObj->icr2Values[3], | |
pLineObj->channelId, pDevObj->timeStamp)); | |
VpMpiCmdWrapper(deviceId, ecVal, VP880_ICR2_WRT, VP880_ICR2_LEN, pLineObj->icr2Values); | |
} | |
#endif /* VP880_TRACKER_SUPPORT */ | |
} else if ((currentState == VP_LINE_RINGING_POLREV) || (currentState == VP_LINE_RINGING)) { | |
VP_LINE_STATE(VpLineCtxType, pLineCtx, | |
("Ringing Exit on Ch (%d) time (%d)", channelId, pDevObj->timeStamp)); | |
/* | |
* Regardless of why we're exiting ringing, need to start the timer that monitors the | |
* SLIC state for ringing exit and restores ASSC bit (from enable while in ringing to | |
* disable for all other conditions). This timer needs to start ASAP. | |
*/ | |
pLineObj->lineTimers.timers.timer[VP_LINE_RING_EXIT_PROCESS] = (1 | VP_ACTIVATE_TIMER); | |
pLineObj->lineTimers.timers.trackingTime = 0; | |
VP_LINE_STATE(VpLineCtxType, pLineCtx, | |
("Enabling VP_LINE_RING_EXIT_PROCESS on Ch %d timeStamp (%d)", | |
pLineObj->channelId, pDevObj->timeStamp)); | |
#ifdef VP880_TRACKER_SUPPORT | |
/* Re-Enable the HV Clamp since Ringing is being removed */ | |
if (!(pDevObj->stateInt & VP880_IS_ABS)) { /* Tracker Device */ | |
/* | |
* Enable HV Clamps ONLY if they were previously disabled -- occurs only | |
* operating in High Voltage mode. | |
*/ | |
if ((pDevObj->stateInt & VP880_IS_HIGH_VOLTAGE) && | |
(pDevObj->devProfileData.lowVoltOverride == FALSE)) { | |
pLineObj->icr2Values[VP880_ICR2_SWY_CTRL_INDEX+1] &= ~(VP880_ICR2_SWY_LIM_CTRL); | |
VP_LINE_STATE(VpLineCtxType, pLineCtx, | |
("Enable HV Clamps: ICR2_WRT 0x%02X 0x%02X 0x%02X 0x%02X Ch %d Time %d", | |
pLineObj->icr2Values[0], pLineObj->icr2Values[1], | |
pLineObj->icr2Values[2], pLineObj->icr2Values[3], | |
pLineObj->channelId, pDevObj->timeStamp)); | |
VpMpiCmdWrapper(deviceId, ecVal, VP880_ICR2_WRT, | |
VP880_ICR2_LEN, pLineObj->icr2Values); | |
} else { | |
VP_LINE_STATE(VpLineCtxType, pLineCtx, | |
("No change to HV Clamps needed Ch %d Time %d", | |
pLineObj->channelId, pDevObj->timeStamp)); | |
} | |
} | |
#endif /* VP880_TRACKER_SUPPORT */ | |
#ifdef VP880_ABS_SUPPORT | |
/* | |
* For ABS devices, start the ringing exit timer used to trigger Switcher Medium Power | |
* Enterance. OK if this was already set, simply causes it to continue. | |
* | |
* Don't set it unless the device is supplying it's own supply and not to any other device. | |
*/ | |
if ((pDevObj->stateInt & VP880_IS_ABS) && | |
(pDevObj->devProfileData.systemConfig & VP880_ABS_CFG_MASK) == VP880_ABS_CFG_SINGLE) { | |
VpCSLACSetTimer(&pDevObj->devTimer[VP_DEV_TIMER_EXIT_RINGING], | |
MS_TO_TICKRATE(VP_DEV_TIMER_EXIT_RINGING_SAMPLE, pDevObj->devProfileData.tickRate)); | |
} | |
#endif /* VP880_ABS_SUPPORT */ | |
} | |
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880SetStateRinging-")); | |
return FALSE; | |
} /* Vp880SetStateRinging() */ | |
/** | |
* Vp880GroundStartProc() | |
* This function implements the Ground Start procedures when entering or exiting a ground start | |
* state. It should be called only by SetLineState. | |
* | |
* This function handles all transitions into TIP-OPEN for all termination types. It also handles | |
* LPM Exit from TIP-OPEN and all other non-LPM state transitions. | |
* | |
* gsMode: TRUE => we're going to TIP-OPEN state for all termination types | |
* FALSE => we're exiting TIP-OPEN state for LPM or other non-LPM state transition except | |
* going to TIP-OPEN | |
* | |
* Preconditions: | |
* None. Calling function must know that this code should execute. | |
* | |
* Postconditions: | |
* Procedures according to operational notes are implemented for enter/exit a ground start state. | |
* A timer is set when exiting ground start to delay disable of DC bias. | |
*/ | |
void | |
Vp880GroundStartProc( | |
bool gsMode, | |
VpLineCtxType *pLineCtx, | |
uint8 currentLineState, | |
uint8 userByte) | |
{ | |
Vp880LineObjectType *pLineObj = pLineCtx->pLineObj; | |
uint8 ecVal = pLineObj->ecVal; | |
VpDevCtxType *pDevCtx = pLineCtx->pDevCtx; | |
Vp880DeviceObjectType *pDevObj = pDevCtx->pDevObj; | |
VpDeviceIdType deviceId = pDevObj->deviceId; | |
uint8 beforeState = (currentLineState & VP880_SS_LINE_FEED_MASK); | |
uint8 afterState = (userByte & VP880_SS_LINE_FEED_MASK); | |
uint8 originalUserByte = userByte; | |
uint8 mpiBuffer[5 + VP880_ICR1_LEN + VP880_ICR2_LEN + VP880_ICR3_LEN + | |
VP880_ICR4_LEN + VP880_SS_CONFIG_LEN]; | |
uint8 mpiIndex = 0; | |
uint8 icr1Before[VP880_ICR1_LEN]; | |
uint8 icr2Before[VP880_ICR2_LEN]; | |
uint8 icr3Before[VP880_ICR3_LEN]; | |
uint8 icr4Before[VP880_ICR4_LEN]; | |
#ifdef VP880_ABS_SUPPORT | |
uint8 sysStateCfg[VP880_SS_CONFIG_LEN]; | |
#endif | |
bool transferToRinging = FALSE; | |
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880GroundStartProc+")); | |
if (((currentLineState & VP880_SS_LINE_FEED_MASK) == VP880_SS_TIP_OPEN) | |
&& (((afterState & VP880_SS_LINE_FEED_MASK) == VP880_SS_FEED_BALANCED_RINGING) | |
|| ((afterState & VP880_SS_LINE_FEED_MASK) == VP880_SS_FEED_UNBALANCED_RINGING))) { | |
transferToRinging = TRUE; | |
} | |
VpMemCpy(icr1Before, pLineObj->icr1Values, VP880_ICR1_LEN); | |
VpMemCpy(icr2Before, pLineObj->icr2Values, VP880_ICR2_LEN); | |
VpMemCpy(icr3Before, pLineObj->icr3Values, VP880_ICR3_LEN); | |
VpMemCpy(icr4Before, pLineObj->icr4Values, VP880_ICR4_LEN); | |
if (gsMode == TRUE) { | |
/* gsMode: TRUE => we're going to TIP-OPEN state */ | |
/* | |
* Implement "Return to Ground Start Idle" part of Ground Start | |
* workaround for VoicePort Devices | |
*/ | |
pLineObj->icr1Values[VP880_ICR1_RING_AND_DAC_LOCATION] |= VP880_ICR1_RING_AND_DAC_B2_3; | |
pLineObj->icr1Values[VP880_ICR1_RING_AND_DAC_LOCATION+1] |= VP880_ICR1_RING_AND_DAC_B2_3; | |
if (beforeState == VP880_SS_DISCONNECT) { | |
pLineObj->icr1Values[VP880_ICR1_BIAS_OVERRIDE_LOCATION] &= | |
~VP880_ICR1_LINE_BIAS_OVERRIDE; | |
pLineObj->icr2Values[VP880_ICR2_SENSE_INDEX] &= | |
(uint8)(~(VP880_ICR2_DAC_SENSE | VP880_ICR2_FEED_SENSE)); | |
if (icr2Before[VP880_ICR2_SENSE_INDEX] != pLineObj->icr2Values[VP880_ICR2_SENSE_INDEX]) { | |
VP_LINE_STATE(VpLineCtxType, pLineCtx, | |
("Ground Start Enter: Write ICR2 0x%02X 0x%02X 0x%02X 0x%02X Ch %d time %d", | |
pLineObj->icr2Values[0], pLineObj->icr2Values[1], | |
pLineObj->icr2Values[2], pLineObj->icr2Values[3], pLineObj->channelId, | |
pDevObj->timeStamp)); | |
mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, VP880_ICR2_WRT, | |
VP880_ICR2_LEN, pLineObj->icr2Values); | |
} | |
} | |
if ((pLineObj->icr1Values[VP880_ICR1_RING_AND_DAC_LOCATION] != icr1Before[VP880_ICR1_RING_AND_DAC_LOCATION]) | |
|| (pLineObj->icr1Values[VP880_ICR1_RING_AND_DAC_LOCATION+1] != icr1Before[VP880_ICR1_RING_AND_DAC_LOCATION+1]) | |
|| (pLineObj->icr1Values[VP880_ICR1_BIAS_OVERRIDE_LOCATION] != icr1Before[VP880_ICR1_BIAS_OVERRIDE_LOCATION])) { | |
mpiIndex = Vp880ProtectedWriteICR1(pLineObj, mpiIndex, mpiBuffer); | |
} | |
#ifdef VP880_ABS_SUPPORT | |
if (pDevObj->stateInt & VP880_IS_ABS) { | |
/* | |
* Disable Auto Battery Switch so Ring stays near -48V for a | |
* Ring to Ground, but first send down previously buffered MPI Cmd/Data if any. | |
*/ | |
if (mpiIndex > 0) { | |
VpMpiCmdWrapper(deviceId, ecVal, mpiBuffer[0], mpiIndex-1, &mpiBuffer[1]); | |
mpiIndex = 0; | |
} | |
VpMpiCmdWrapper(deviceId, ecVal, VP880_SS_CONFIG_RD, VP880_SS_CONFIG_LEN, sysStateCfg); | |
sysStateCfg[0] |= VP880_AUTO_BAT_SWITCH_DIS; | |
VpMpiCmdWrapper(deviceId, ecVal, VP880_SS_CONFIG_WRT, VP880_SS_CONFIG_LEN, sysStateCfg); | |
} | |
#endif /* VP880_ABS_SUPPORT */ | |
/* Set Polarity of input to Gkey Detector */ | |
pLineObj->icr4Values[VP880_ICR4_GKEY_DET_LOCATION] |= VP880_ICR4_GKEY_DET; | |
pLineObj->icr4Values[VP880_ICR4_GKEY_DET_LOCATION+1] &= ~VP880_ICR4_GKEY_DET; | |
if ((pLineObj->icr4Values[VP880_ICR4_GKEY_DET_LOCATION] != icr4Before[VP880_ICR4_GKEY_DET_LOCATION]) | |
|| (pLineObj->icr4Values[VP880_ICR4_GKEY_DET_LOCATION+1] != icr4Before[VP880_ICR4_GKEY_DET_LOCATION+1])) { | |
mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, VP880_ICR4_WRT, | |
VP880_ICR4_LEN, pLineObj->icr4Values); | |
VP_LINE_STATE(VpLineCtxType, pLineCtx, | |
("Ground Start Enter: Write ICR4 0x%02X 0x%02X 0x%02X 0x%02X Ch %d time %d", | |
pLineObj->icr4Values[0], pLineObj->icr4Values[1], | |
pLineObj->icr4Values[2], pLineObj->icr4Values[3], pLineObj->channelId, | |
pDevObj->timeStamp)); | |
} | |
if (mpiIndex > 0) { | |
/* send down the mpi commands if any exist */ | |
VpMpiCmdWrapper(deviceId, ecVal, mpiBuffer[0], mpiIndex-1, &mpiBuffer[1]); | |
mpiIndex = 0; | |
} | |
/* | |
* Perform the SLIC state change, controlled by logic that determines | |
* if low power mode can be used. | |
*/ | |
Vp880LLSetSysState(deviceId, pLineCtx, userByte, TRUE); | |
/* | |
* Connect Longitudinal Loop Control to ILout Pin Override | |
* After this point, the Hook Bit indicates Ground Key | |
*/ | |
pLineObj->icr3Values[VP880_ICR3_LONG_LOOP_CTRL_LOCATION] |= VP880_ICR3_LONG_LOOP_CONTROL; | |
pLineObj->icr3Values[VP880_ICR3_LONG_LOOP_CTRL_LOCATION+1] &= ~VP880_ICR3_LONG_LOOP_CONTROL; | |
if ((pLineObj->icr3Values[VP880_ICR3_LONG_LOOP_CTRL_LOCATION] != | |
icr3Before[VP880_ICR3_LONG_LOOP_CTRL_LOCATION]) | |
|| (pLineObj->icr3Values[VP880_ICR3_LONG_LOOP_CTRL_LOCATION+1] != | |
icr3Before[VP880_ICR3_LONG_LOOP_CTRL_LOCATION+1])) { | |
VP_LINE_STATE(VpLineCtxType, pLineCtx, | |
("Ground Start Enter: Write ICR3 0x%02X 0x%02X 0x%02X 0x%02X Ch %d time %d", | |
pLineObj->icr3Values[0], pLineObj->icr3Values[1], | |
pLineObj->icr3Values[2], pLineObj->icr3Values[3], pLineObj->channelId, | |
pDevObj->timeStamp)); | |
VpMpiCmdWrapper(deviceId, ecVal, VP880_ICR3_WRT, VP880_ICR3_LEN, pLineObj->icr3Values); | |
} | |
/* | |
* Enable a (30ms) State Change Mask to allow for the line to stabilize | |
* before considering valid Ring Ground detection. Initliaze the Ground Start Service timer | |
*/ | |
pLineObj->lineTimers.timers.timer[VP_LINE_GND_START_TIMER] = | |
(MS_TO_TICKRATE(VP880_GND_START_DEBOUNCE, | |
pDevObj->devProfileData.tickRate)) | VP_ACTIVATE_TIMER; | |
pLineObj->gsTimerExitState = 0; | |
} else { | |
/* | |
* gsMode: FALSE => we're exiting TIP-OPEN state for LPM or other | |
* non-LPM state transition except going to TIP-OPEN | |
*/ | |
#ifdef VP880_LP_SUPPORT | |
if (VpIsLowPowerTermType(pLineObj->termType)) { | |
/* | |
* This is a LPM Termination Type. Check the line object to see if | |
* it's currently capable of LPM. | |
*/ | |
if (pLineObj->status & VP880_LOW_POWER_EN) { | |
/* | |
* The line is capable of LPM. That means this transition will be | |
* handled as part of the Low Power Mode Entry algorithm. | |
*/ | |
VP_LINE_STATE(VpLineCtxType, pLineCtx, | |
("Line in LPM - Adjusting LP Mode on channel %d, NOT writing to device time %d", | |
pLineObj->channelId, pDevObj->timeStamp)); | |
Vp880LLSetSysState(deviceId, pLineCtx, userByte, FALSE); | |
} else { | |
/* | |
* If we're coming from non-LPM into a LPM state, the LPM | |
* exit function handles the state transition cleanly except | |
* when exiting ringing. So under all other conditions we don't | |
* want to write the state here. | |
*/ | |
/* | |
* We're going to a LPM state IF the other line is in a state | |
* that supports LPM AND this line is going to a LPM state. | |
*/ | |
/* devState "set" means the other line is in a LPM state. */ | |
Vp880DeviceStateIntType devState = (pLineObj->channelId == 0) ? | |
(pDevObj->stateInt & VP880_LINE1_LP) : | |
(pDevObj->stateInt & VP880_LINE0_LP); | |
uint8 beforeRing = (beforeState & VP880_SS_LINE_FEED_MASK); | |
if (((userByte == VP880_SS_DISCONNECT) || (userByte == VP880_SS_IDLE)) | |
&& (devState) && (beforeRing != VP880_SS_FEED_BALANCED_RINGING) | |
&& (beforeRing != VP880_SS_FEED_UNBALANCED_RINGING) | |
&& (!(pLineObj->status & VP880_LINE_LEAK))) { | |
if (userByte == VP880_SS_IDLE) { | |
VP_LINE_STATE(VpLineCtxType, pLineCtx, | |
("Line Going to LPM - Adjusting LP Mode on channel %d, writing to device time %d", | |
pLineObj->channelId, pDevObj->timeStamp)); | |
Vp880LLSetSysState(deviceId, pLineCtx, VP880_SS_ACTIVE, TRUE); | |
} else { | |
VP_LINE_STATE(VpLineCtxType, pLineCtx, | |
("Line Going to LPM - Adjusting LP Mode on channel %d, NOT writing to device time %d", | |
pLineObj->channelId, pDevObj->timeStamp)); | |
Vp880LLSetSysState(deviceId, pLineCtx, userByte, FALSE); | |
} | |
} else { | |
/* | |
* Almost there. Last "block" is if we're going to Ringing | |
* from Tip Open. In that case, we want the workarounds | |
* to be removed first, which is done in the "Ground Start | |
* Timer Service" routine. But set to Active to start | |
* providing normal feed. | |
*/ | |
if (transferToRinging) { | |
userByte = VP880_SS_ACTIVE; | |
} | |
VP_LINE_STATE(VpLineCtxType, pLineCtx, | |
("Adjusting LP Mode on channel %d, writing to device time %d", | |
pLineObj->channelId, pDevObj->timeStamp)); | |
Vp880LLSetSysState(deviceId, pLineCtx, userByte, TRUE); | |
} | |
} | |
} else { | |
#endif | |
if (beforeState != afterState) { | |
bool writeIcrReg = FALSE; | |
/* | |
* When exiting from Disconnect to a feed state in silicon <= VC, the API goes | |
* through Tip-Open to avoid a short polarity reversal transition ("Ping Timer"). | |
* While in Disconnect, some ICR settings are made to minimize power but prevent | |
* normal operation while in other feed states. The ICR settings are managed here, | |
* but this logic needs to treat > VC silicon Disconnect state the same as <= VC | |
* silicon Disconnect State AND Active "Ping" timer. | |
*/ | |
if ((beforeState == VP880_SS_DISCONNECT) || | |
(pLineObj->lineTimers.timers.timer[VP_LINE_PING_TIMER] & VP_ACTIVATE_TIMER)) { | |
/* | |
* Due to Speedup management, all Disoonnect exit state | |
* changes need to start with Acitve. Most transitions can | |
* tolerate the final state setting after 100ms (standard | |
* disconnect exit time) duration, except Ringing. Ringing | |
* should start as soon as possible, which is generally some | |
* time after the ~30ms device speedup. | |
*/ | |
Vp880LLSetSysState(deviceId, pLineCtx, | |
(VP880_SS_ACTIVE | (userByte & VP880_SS_POLARITY_MASK)), TRUE); | |
pLineObj->nextSlicValue = userByte; | |
/* Release Line Bias control */ | |
pLineObj->icr1Values[VP880_ICR1_BIAS_OVERRIDE_LOCATION] &= | |
~VP880_ICR1_LINE_BIAS_OVERRIDE; | |
pLineObj->icr2Values[VP880_ICR2_SENSE_INDEX] &= | |
(uint8)(~(VP880_ICR2_DAC_SENSE | VP880_ICR2_FEED_SENSE)); | |
writeIcrReg = TRUE; | |
VP_LINE_STATE(VpLineCtxType, pLineCtx, | |
("Disconnect Exit: Write ICR1 0x%02X 0x%02X 0x%02X 0x%02X Ch %d time %d", | |
pLineObj->icr1Values[0], pLineObj->icr1Values[1], | |
pLineObj->icr1Values[2], pLineObj->icr1Values[3], | |
pLineObj->channelId, pDevObj->timeStamp)); | |
VP_LINE_STATE(VpLineCtxType, pLineCtx, | |
("Disconnect Exit: Write ICR2 0x%02X 0x%02X 0x%02X 0x%02X Ch %d time %d", | |
pLineObj->icr2Values[0], pLineObj->icr2Values[1], | |
pLineObj->icr2Values[2], pLineObj->icr2Values[3], | |
pLineObj->channelId, pDevObj->timeStamp)); | |
if (((userByte & VP880_SS_LINE_FEED_MASK) == VP880_SS_FEED_BALANCED_RINGING) || | |
((userByte & VP880_SS_LINE_FEED_MASK) == VP880_SS_FEED_UNBALANCED_RINGING)) { | |
/* | |
* Going to Ringing. Overwrite the standard disconnect | |
* exit time to cause ringing entry ASAP. | |
*/ | |
pLineObj->lineTimers.timers.timer[VP_LINE_DISCONNECT_EXIT] = | |
(MS_TO_TICKRATE(VP880_SPEEDUP_HOLD_TIME * 2, | |
pDevObj->devProfileData.tickRate)) | VP_ACTIVATE_TIMER; | |
/* Initialize the Disconnect Exit Timer State */ | |
pLineObj->discTimerExitState = 0; | |
VP_LINE_STATE(VpLineCtxType, pLineCtx, | |
("Chan %d Setting VP_LINE_DISCONNECT_EXIT time to %d ms (VP880_SPEEDUP_HOLD_TIME * 2) at time %d line status 0x%04X", | |
pLineObj->channelId, (VP880_SPEEDUP_HOLD_TIME * 2), pDevObj->timeStamp, pLineObj->lineState.condition)); | |
} | |
} else { | |
Vp880LLSetSysState(deviceId, pLineCtx, userByte, TRUE); | |
if (afterState == VP880_SS_DISCONNECT) { | |
pLineObj->lineTimers.timers.timer[VP_LINE_DISCONNECT_EXIT] = | |
(MS_TO_TICKRATE(VP880_SPEEDUP_HOLD_TIME, | |
pDevObj->devProfileData.tickRate)) | VP_ACTIVATE_TIMER; | |
VP_LINE_STATE(VpLineCtxType, pLineCtx, | |
("Chan %d Setting VP_LINE_DISCONNECT_EXIT time to %d ms (VP880_SPEEDUP_HOLD_TIME) at time %d", | |
pLineObj->channelId, VP880_SPEEDUP_HOLD_TIME, pDevObj->timeStamp)); | |
pLineObj->icr1Values[VP880_ICR1_BIAS_OVERRIDE_LOCATION] |= | |
VP880_ICR1_LINE_BIAS_OVERRIDE; | |
pLineObj->icr1Values[VP880_ICR1_BIAS_OVERRIDE_LOCATION+1] &= | |
~(VP880_ICR1_LINE_BIAS_OVERRIDE); | |
pLineObj->icr1Values[VP880_ICR1_BIAS_OVERRIDE_LOCATION+1] |= | |
VP880_ICR1_LINE_BIAS_OVERRIDE_NORM; | |
pLineObj->icr2Values[VP880_ICR2_SENSE_INDEX] |= | |
(VP880_ICR2_DAC_SENSE | VP880_ICR2_FEED_SENSE); | |
pLineObj->icr2Values[VP880_ICR2_SENSE_INDEX+1] &= ~VP880_ICR2_DAC_SENSE; | |
pLineObj->icr2Values[VP880_ICR2_SENSE_INDEX+1] |= VP880_ICR2_FEED_SENSE; | |
writeIcrReg = TRUE; | |
VP_LINE_STATE(VpLineCtxType, pLineCtx, | |
("Disconnect Enter: Write ICR1 0x%02X 0x%02X 0x%02X 0x%02X Ch %d time %d", | |
pLineObj->icr1Values[0], pLineObj->icr1Values[1], | |
pLineObj->icr1Values[2], pLineObj->icr1Values[3], | |
pLineObj->channelId, pDevObj->timeStamp)); | |
VP_LINE_STATE(VpLineCtxType, pLineCtx, | |
("Disconnect Enter: Write ICR2 0x%02X 0x%02X 0x%02X 0x%02X Ch %d time %d", | |
pLineObj->icr2Values[0], pLineObj->icr2Values[1], | |
pLineObj->icr2Values[2], pLineObj->icr2Values[3], | |
pLineObj->channelId, pDevObj->timeStamp)); | |
} | |
} | |
if (writeIcrReg == TRUE) { | |
mpiIndex = Vp880ProtectedWriteICR1(pLineObj, mpiIndex, mpiBuffer); | |
mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, VP880_ICR2_WRT, | |
VP880_ICR2_LEN, pLineObj->icr2Values); | |
/* send down the mpi commands */ | |
VpMpiCmdWrapper(deviceId, ecVal, mpiBuffer[0], mpiIndex-1, &mpiBuffer[1]); | |
mpiIndex = 0; | |
} | |
} else { | |
if (beforeState != VP_LINE_DISCONNECT) { | |
VP_LINE_STATE(VpLineCtxType, pLineCtx, | |
("Setting channel %d to device Linestate 0x%02X time %d", | |
pLineObj->channelId, userByte, pDevObj->timeStamp)); | |
Vp880LLSetSysState(deviceId, pLineCtx, userByte, TRUE); | |
} | |
} | |
#ifdef VP880_LP_SUPPORT | |
} | |
#endif | |
/* | |
* We are transferring from Tip Open to some other state. Need to remove | |
* the Ground Start workarounds | |
*/ | |
if ((currentLineState & VP880_SS_LINE_FEED_MASK) == VP880_SS_TIP_OPEN) { | |
#ifdef VP880_ABS_SUPPORT | |
if (pDevObj->stateInt & VP880_IS_ABS) { | |
/* Re-enable Auto Battery switch only on VC and later silicon */ | |
if (pDevObj->staticInfo.rcnPcn[VP880_RCN_LOCATION] >= VP880_REV_VC) { | |
VpMpiCmdWrapper(deviceId, ecVal, VP880_SS_CONFIG_RD, | |
VP880_SS_CONFIG_LEN, sysStateCfg); | |
sysStateCfg[0] &= ~VP880_AUTO_BAT_SWITCH_DIS; | |
mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, | |
VP880_SS_CONFIG_WRT, VP880_SS_CONFIG_LEN, sysStateCfg); | |
} | |
} | |
#endif | |
pLineObj->icr1Values[VP880_ICR1_RING_AND_DAC_LOCATION] &= | |
~VP880_ICR1_RING_AND_DAC_B2_3; | |
pLineObj->icr1Values[VP880_ICR1_RING_AND_DAC_LOCATION+1] |= | |
VP880_ICR1_RING_AND_DAC_B2_3; | |
mpiIndex = Vp880ProtectedWriteICR1(pLineObj, mpiIndex, mpiBuffer); | |
VP_LINE_STATE(VpLineCtxType, pLineCtx, | |
("Tip Open Exit: Write ICR1 0x%02X 0x%02X 0x%02X 0x%02X Ch %d time %d", | |
pLineObj->icr1Values[0], pLineObj->icr1Values[1], | |
pLineObj->icr1Values[2], pLineObj->icr1Values[3], | |
pLineObj->channelId, pDevObj->timeStamp)); | |
if (mpiIndex > 0) { | |
/* send down the mpi commands */ | |
VpMpiCmdWrapper(deviceId, ecVal, mpiBuffer[0], mpiIndex-1, &mpiBuffer[1]); | |
} | |
/* | |
* Unless the state we're going to is ringing, set a timer for 210ms | |
* before disabling the DC bias. If we are going to Ringing, disable | |
* the DC Bias immediately. | |
* Holding the DC bias results in retaining the Tip voltage near | |
* ground. For normal ground start signaling, 210ms is long enough | |
* for the CPE to detect the Tip-Gnd voltage. If we're going directly | |
* to ringing, this isn't a normal ground start sequence. | |
*/ | |
if (((afterState & VP880_SS_LINE_FEED_MASK) == VP880_SS_FEED_BALANCED_RINGING) | |
|| ((afterState & VP880_SS_LINE_FEED_MASK) == VP880_SS_FEED_UNBALANCED_RINGING)) { | |
pLineObj->lineTimers.timers.timer[VP_LINE_GND_START_TIMER] = | |
(1 | VP_ACTIVATE_TIMER); | |
} else { | |
pLineObj->lineTimers.timers.timer[VP_LINE_GND_START_TIMER] = | |
(MS_TO_TICKRATE(VP880_GND_START_TIME, | |
pDevObj->devProfileData.tickRate)) | VP_ACTIVATE_TIMER; | |
} | |
/* Initialize the ground start exit timer state */ | |
pLineObj->gsTimerExitState = 0; | |
/* | |
* Delay writing to the Ringing State until the workarounds are | |
* removed. | |
*/ | |
pLineObj->nextSlicValue = originalUserByte; | |
VP_LINE_STATE(VpLineCtxType, pLineCtx, | |
("Chan %d Setting VP_LINE_GND_START_TIMER time to %d ms (VP880_GND_START_TIME) at time %d", | |
pLineObj->channelId, VP880_GND_START_TIME, pDevObj->timeStamp)); | |
} | |
} | |
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880GroundStartProc-")); | |
} /* Vp880GroundStartProc() */ | |
#ifdef VP880_ABS_SUPPORT | |
/** | |
* Vp880GetLineStateABS | |
* Locally used function by Vp880SetLineState to get the line state byte used | |
* for ABS devices. | |
* | |
* Preconditions: | |
* None. State to byte mapping only. | |
* | |
* Postconditions: | |
* Returns the byte that should be used in the device System State register | |
* for the API State passed. | |
*/ | |
uint8 | |
Vp880GetLineStateABS( | |
VpLineCtxType *pLineCtx, | |
VpLineStateType state, | |
bool forced) | |
{ | |
Vp880LineObjectType *pLineObj = pLineCtx->pLineObj; | |
uint8 ecVal = pLineObj->ecVal; | |
uint8 channelId = pLineObj->channelId; | |
VpDevCtxType *pDevCtx = pLineCtx->pDevCtx; | |
Vp880DeviceObjectType *pDevObj = pDevCtx->pDevObj; | |
VpDeviceIdType deviceId = pDevObj->deviceId; | |
uint8 returnVal = 0xFF; | |
uint8 icr6PreValues[VP880_ICR6_LEN]; | |
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880GetLineStateABS+")); | |
VpMemCpy(icr6PreValues, pLineObj->icr6Values, VP880_ICR6_LEN); | |
pLineObj->icr6Values[VP880_DC_CAL_ABS_INDEX] &= ~VP880_DC_CAL_ABS_MASK; | |
switch(state) { | |
case VP_LINE_STANDBY: | |
returnVal = VP880_SS_IDLE; | |
pLineObj->icr6Values[VP880_DC_CAL_ABS_INDEX] |= | |
pDevObj->vp880SysCalData.absNormCal[channelId]; | |
break; | |
case VP_LINE_TIP_OPEN: | |
pLineObj->icr6Values[VP880_DC_CAL_ABS_INDEX] |= | |
(icr6PreValues[0] & VP880_DC_CAL_ABS_MASK); | |
returnVal = VP880_SS_TIP_OPEN; | |
break; | |
case VP_LINE_ACTIVE: | |
case VP_LINE_TALK: | |
#ifdef VP_HIGH_GAIN_MODE_SUPPORTED | |
case VP_LINE_HOWLER: | |
#endif | |
returnVal = VP880_SS_ACTIVE; | |
pLineObj->icr6Values[VP880_DC_CAL_ABS_INDEX] |= | |
pDevObj->vp880SysCalData.absNormCal[channelId]; | |
break; | |
case VP_LINE_STANDBY_POLREV: | |
returnVal = VP880_SS_IDLE_POLREV; | |
pLineObj->icr6Values[VP880_DC_CAL_ABS_INDEX] |= | |
pDevObj->vp880SysCalData.absPolRevCal[channelId]; | |
break; | |
case VP_LINE_ACTIVE_POLREV: | |
case VP_LINE_TALK_POLREV: | |
#ifdef VP_HIGH_GAIN_MODE_SUPPORTED | |
case VP_LINE_HOWLER_POLREV: | |
#endif | |
returnVal = VP880_SS_ACTIVE_POLREV; | |
pLineObj->icr6Values[VP880_DC_CAL_ABS_INDEX] |= | |
pDevObj->vp880SysCalData.absPolRevCal[channelId]; | |
break; | |
case VP_LINE_OHT: | |
returnVal = VP880_SS_ACTIVE_MID_BAT; | |
pLineObj->icr6Values[VP880_DC_CAL_ABS_INDEX] |= | |
pDevObj->vp880SysCalData.absNormCal[channelId]; | |
break; | |
case VP_LINE_OHT_POLREV: | |
returnVal = VP880_SS_ACTIVE_MID_BAT_PR; | |
pLineObj->icr6Values[VP880_DC_CAL_ABS_INDEX] |= | |
pDevObj->vp880SysCalData.absPolRevCal[channelId]; | |
break; | |
case VP_LINE_DISCONNECT: | |
pLineObj->icr6Values[VP880_DC_CAL_ABS_INDEX] |= | |
(icr6PreValues[0] & VP880_DC_CAL_ABS_MASK); | |
returnVal = VP880_SS_DISCONNECT; | |
break; | |
case VP_LINE_RINGING: | |
if (pLineObj->status & VP880_UNBAL_RINGING) { | |
returnVal = VP880_SS_UNBALANCED_RINGING; | |
} else { | |
returnVal = VP880_SS_BALANCED_RINGING; | |
} | |
pLineObj->icr6Values[VP880_DC_CAL_ABS_INDEX] |= | |
pDevObj->vp880SysCalData.absNormCal[channelId]; | |
break; | |
case VP_LINE_RINGING_POLREV: | |
if (pLineObj->status & VP880_UNBAL_RINGING) { | |
returnVal = VP880_SS_UNBALANCED_RINGING_PR; | |
} else { | |
returnVal = VP880_SS_BALANCED_RINGING_PR; | |
} | |
pLineObj->icr6Values[VP880_DC_CAL_ABS_INDEX] |= | |
pDevObj->vp880SysCalData.absPolRevCal[channelId]; | |
break; | |
default: | |
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Error in Vp880GetLineStateABS-")); | |
return returnVal; | |
} | |
/* | |
* Only modifying the first byte (possibly), so that's the only one we | |
* should compare with to determine if MPI write is needed. | |
*/ | |
VP_CALIBRATION(VpLineCtxType, pLineCtx, ("ABS: Channel %d ICR6-Pre-ABS 0x%02X Target 0x%02X", | |
pLineObj->channelId, icr6PreValues[0], pLineObj->icr6Values[VP880_DC_CAL_ABS_INDEX])); | |
if ((pLineObj->icr6Values[VP880_DC_CAL_ABS_INDEX] != icr6PreValues[0]) || (forced == TRUE)) { | |
VpMpiCmdWrapper(deviceId, ecVal, VP880_ICR6_WRT, VP880_ICR6_LEN, pLineObj->icr6Values); | |
} | |
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880GetLineStateABS-")); | |
return returnVal; | |
} | |
#endif | |
#ifdef VP880_TRACKER_SUPPORT | |
/** | |
* Vp880GetLineStateNonABS | |
* Locally used function by Vp880SetLineState to get the line state byte used | |
* for non-ABS devices. | |
* | |
* Preconditions: | |
* None. State to byte mapping only. | |
* | |
* Postconditions: | |
* Returns the byte that should be used in the device System State register | |
* for the API State passed. | |
*/ | |
uint8 | |
Vp880GetLineStateNonABS( | |
VpLineCtxType *pLineCtx, | |
VpLineStateType state) | |
{ | |
Vp880LineObjectType *pLineObj = pLineCtx->pLineObj; | |
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880GetLineStateNonABS+")); | |
switch(state) { | |
case VP_LINE_STANDBY: | |
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880GetLineStateNonABS-")); | |
return VP880_SS_IDLE; | |
case VP_LINE_TIP_OPEN: | |
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880GetLineStateNonABS-")); | |
return VP880_SS_TIP_OPEN; | |
case VP_LINE_ACTIVE: | |
case VP_LINE_TALK: | |
case VP_LINE_OHT: | |
#ifdef VP_HIGH_GAIN_MODE_SUPPORTED | |
case VP_LINE_HOWLER: | |
#endif | |
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880GetLineStateNonABS-")); | |
return VP880_SS_ACTIVE; | |
case VP_LINE_ACTIVE_POLREV: | |
case VP_LINE_TALK_POLREV: | |
case VP_LINE_OHT_POLREV: | |
#ifdef VP_HIGH_GAIN_MODE_SUPPORTED | |
case VP_LINE_HOWLER_POLREV: | |
#endif | |
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880GetLineStateNonABS-")); | |
return VP880_SS_ACTIVE_POLREV; | |
case VP_LINE_STANDBY_POLREV: | |
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880GetLineStateNonABS-")); | |
return VP880_SS_IDLE_POLREV; | |
case VP_LINE_DISCONNECT: | |
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880GetLineStateNonABS-")); | |
return VP880_SS_DISCONNECT; | |
case VP_LINE_RINGING: | |
if (pLineObj->status & VP880_UNBAL_RINGING) { | |
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880GetLineStateNonABS-")); | |
return VP880_SS_UNBALANCED_RINGING; | |
} else { | |
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880GetLineStateNonABS-")); | |
return VP880_SS_BALANCED_RINGING; | |
} | |
case VP_LINE_RINGING_POLREV: | |
if (pLineObj->status & VP880_UNBAL_RINGING) { | |
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880GetLineStateNonABS-")); | |
return VP880_SS_UNBALANCED_RINGING_PR; | |
} else { | |
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880GetLineStateNonABS-")); | |
return VP880_SS_BALANCED_RINGING_PR; | |
} | |
default: | |
break; | |
} | |
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880GetLineStateNonABS-")); | |
return 0xFF; | |
} | |
#endif | |
#endif | |
/** | |
* Vp880LLSetSysState() | |
* This function writes to the System State register and based on the state | |
* being set, determines if low power mode can or cannot be used, or if the line | |
* is recovering from Disconnect to a Feed state. In the latter case, a timer | |
* is set to transition through Tip Open first to prevent "ping" of the phone. | |
* In this case, the final state is set when the timer expires. | |
* | |
* Preconditions: | |
* This function is called internally by the API-II only. | |
* | |
* Postconditions: | |
* The System State Register is updated with either the value passed, or | |
* Tip Open. If low power mode termination type exists, a flag in the device | |
* object indicating that low power can or cannot be used is modified. If coming | |
* out of Disconnect, Tip Open is written and a timer is set. | |
*/ | |
void | |
Vp880LLSetSysState( | |
VpDeviceIdType deviceId, | |
VpLineCtxType *pLineCtx, | |
uint8 lineState, | |
bool writeToDevice) | |
{ | |
Vp880LineObjectType *pLineObj = pLineCtx->pLineObj; | |
uint8 ecVal = pLineObj->ecVal; | |
uint8 channelId = pLineObj->channelId; | |
VpDevCtxType *pDevCtx = pLineCtx->pDevCtx; | |
Vp880DeviceObjectType *pDevObj = pDevCtx->pDevObj; | |
#ifdef VP880_FXS_SUPPORT | |
bool lineIsFxs = FALSE; | |
#endif | |
uint8 lineStatePre[VP880_SYS_STATE_LEN]; | |
lineStatePre[0] = pLineObj->slicValueCache; | |
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880LLSetSysState+")); | |
#ifdef VP880_FXS_SUPPORT | |
if (!(pLineObj->status & VP880_IS_FXO)) { | |
lineIsFxs = TRUE; | |
if ((Vp880IsChnlUndrTst(pDevObj, channelId) == TRUE) || | |
(pLineObj->status & VP880_LINE_IN_CAL)) { | |
pDevObj->stateInt &= | |
((channelId == 0) ? ~VP880_LINE0_LP : ~VP880_LINE1_LP); | |
VP_LINE_STATE(VpLineCtxType, pLineCtx,("3. Clearing LP flag for channel %d",channelId)); | |
} else { | |
bool lpTermType = FALSE; | |
bool hookStatus = FALSE; | |
#ifdef VP880_LP_SUPPORT | |
if (VpIsLowPowerTermType(pLineObj->termType)) { | |
lpTermType = TRUE; | |
VpCSLACGetLineStatus(pLineCtx, VP_INPUT_RAW_HOOK, &hookStatus); | |
} | |
#endif | |
hookStatus = (pLineObj->lineState.currentState == VP_LINE_STANDBY) ? hookStatus : FALSE; | |
if ((lpTermType == TRUE) && (hookStatus == FALSE) && (!(pLineObj->status & VP880_LINE_LEAK))) { | |
if ((pLineObj->lineState.currentState == VP_LINE_DISCONNECT) || | |
(pLineObj->lineState.currentState == VP_LINE_STANDBY)) { | |
if (((lineStatePre[0] & VP880_SS_LINE_FEED_MASK) != VP880_SS_FEED_BALANCED_RINGING) | |
&& ((lineStatePre[0] & VP880_SS_LINE_FEED_MASK) != VP880_SS_FEED_UNBALANCED_RINGING)) { | |
pDevObj->stateInt |= | |
((channelId == 0) ? VP880_LINE0_LP : VP880_LINE1_LP); | |
VP_LINE_STATE(VpLineCtxType, pLineCtx,("1. Setting LP flag for channel %d Hook Status %d", | |
channelId, hookStatus)); | |
} else { | |
VP_LINE_STATE(VpLineCtxType, pLineCtx,("1. Delay Setting LP flag for channel %d",channelId)); | |
} | |
} else { | |
pDevObj->stateInt &= | |
((channelId == 0) ? ~VP880_LINE0_LP : ~VP880_LINE1_LP); | |
VP_LINE_STATE(VpLineCtxType, pLineCtx,("1. Clearing LP flag for channel %d Device Status 0x%08lX", | |
channelId, pDevObj->stateInt)); | |
} | |
} else { | |
pDevObj->stateInt &= | |
((channelId == 0) ? ~VP880_LINE0_LP : ~VP880_LINE1_LP); | |
VP_LINE_STATE(VpLineCtxType, pLineCtx,("2. Clearing LP flag for channel %d Device Status 0x%08lX", | |
channelId, pDevObj->stateInt)); | |
} | |
} | |
} | |
#endif | |
/* Device Write Override: setting flags without a device write */ | |
if (writeToDevice == FALSE) { | |
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880LLSetSysState-")); | |
return; | |
} | |
#ifdef VP880_FXS_SUPPORT | |
if (((lineStatePre[0] & VP880_SS_LINE_FEED_MASK) == VP880_SS_DISCONNECT) | |
&& (lineIsFxs == TRUE)) { | |
bool nextIsFeed = TRUE; | |
switch (lineState & VP880_SS_LINE_FEED_MASK) { | |
case VP880_SS_DISCONNECT: | |
case VP880_SS_TIP_OPEN: | |
case VP880_SS_RING_OPEN: | |
case VP880_SS_SHUTDOWN: | |
nextIsFeed = FALSE; | |
break; | |
default: | |
break; | |
} | |
VP_LINE_STATE(VpLineCtxType, pLineCtx, ("Preparing 0x%02X for Next Slic State on Channel %d", | |
lineState, channelId)); | |
pLineObj->nextSlicValue = lineState; | |
if ((pDevObj->staticInfo.rcnPcn[VP880_RCN_LOCATION] <= VP880_REV_VC) && (nextIsFeed)) { | |
lineState = VP880_SS_TIP_OPEN; | |
VP_LINE_STATE(VpLineCtxType, pLineCtx, ("2. Setting Chan %d to TIP_OPEN at Time %d", | |
channelId, pDevObj->timeStamp)); | |
pLineObj->lineTimers.timers.timer[VP_LINE_PING_TIMER] = | |
(MS_TO_TICKRATE(VP880_PING_TIME, | |
pDevObj->devProfileData.tickRate)) | VP_ACTIVATE_TIMER; | |
} | |
Vp880UpdateBufferChanSel(pDevObj, channelId, lineState, TRUE); | |
#ifdef VP880_TRACKER_SUPPORT | |
if ((lineState == VP880_SS_BALANCED_RINGING) && (IN_RUSH_CYCLE_TOTAL > 0) && | |
(pDevObj->devProfileData.peakManagement == TRUE)) { | |
Vp880LimitInRushCurrent(pDevObj, ecVal, FALSE); | |
pLineObj->slicValueCache = VP880_SS_BALANCED_RINGING; | |
} else { | |
#endif | |
if (pLineObj->slicValueCache != lineState) { | |
pLineObj->slicValueCache = lineState; | |
VP_LINE_STATE(VpLineCtxType, pLineCtx, ("2. Setting Chan %d to System State 0x%02X at Time %d", | |
channelId, lineState, pDevObj->timeStamp)); | |
VpMpiCmdWrapper(deviceId, ecVal, VP880_SYS_STATE_WRT, | |
VP880_SYS_STATE_LEN, &pLineObj->slicValueCache); | |
} | |
#ifdef VP880_TRACKER_SUPPORT | |
} | |
#endif | |
} else { | |
#endif | |
if (pLineObj->lineTimers.timers.timer[VP_LINE_PING_TIMER] & VP_ACTIVATE_TIMER) { | |
pLineObj->nextSlicValue = lineState; | |
} else { | |
Vp880UpdateBufferChanSel(pDevObj, channelId, lineState, TRUE); | |
#if defined (VP880_TRACKER_SUPPORT) && defined (VP880_FXS_SUPPORT) | |
if (((lineState == VP880_SS_BALANCED_RINGING) || (lineState == VP880_SS_UNBALANCED_RINGING)) | |
&& (IN_RUSH_CYCLE_TOTAL > 0) && | |
(pDevObj->devProfileData.peakManagement == TRUE)) { | |
Vp880LimitInRushCurrent(pDevObj, ecVal, FALSE); | |
pLineObj->slicValueCache = lineState; | |
} else { | |
#endif | |
if (pLineObj->slicValueCache != lineState) { | |
pLineObj->slicValueCache = lineState; | |
VP_LINE_STATE(VpLineCtxType, pLineCtx, ("1. Setting Chan %d to System State 0x%02X at Time %d", | |
channelId, lineState, pDevObj->timeStamp)); | |
VpMpiCmdWrapper(deviceId, ecVal, VP880_SYS_STATE_WRT, | |
VP880_SYS_STATE_LEN, &pLineObj->slicValueCache); | |
if ((pLineObj->lineTimers.timers.timer[VP_LINE_DISCONNECT_EXIT] & VP_ACTIVATE_TIMER) && | |
(pDevObj->staticInfo.rcnPcn[VP880_RCN_LOCATION] <= VP880_REV_VC)){ | |
pLineObj->nextSlicValue = pLineObj->slicValueCache; | |
} | |
} | |
#if defined (VP880_TRACKER_SUPPORT) && defined (VP880_FXS_SUPPORT) | |
} | |
#endif | |
} | |
#ifdef VP880_FXS_SUPPORT | |
} | |
#endif | |
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880LLSetSysState-")); | |
} | |
#if defined (VP880_TRACKER_SUPPORT) && defined (VP880_FXS_SUPPORT) | |
/** | |
* Vp880LimitInRushCurrent() | |
* This function reduces the in-rush current during the battery transition | |
* | |
* Preconditions: | |
* None. | |
* | |
* Postconditions: | |
* Slac state is in ringing | |
*/ | |
void | |
Vp880LimitInRushCurrent( | |
Vp880DeviceObjectType *pDevObj, | |
uint8 ecVal, /* 0x00, 0x80, 0x01, 0x02, 0x81, 0x82 */ | |
bool callback) /* TRUE if called from device timer, FALSE if called by Set Line State */ | |
{ | |
#if (IN_RUSH_CYCLE_TOTAL > 0) | |
VpDeviceIdType deviceId = pDevObj->deviceId; | |
uint8 regCtrl, ssConf, sStateRing, sState, channelId; | |
if (callback == FALSE) { /* Must be valid channel */ | |
channelId = ((ecVal & VP880_EC_BITS_MASK) >> 1); | |
VP_LINE_STATE(None, NULL,("Vp880LimitInRushCurrent(): Initialization")); | |
/* Limit only in fixed ringing */ | |
if (pDevObj->ringParams.channelArray[channelId] == FALSE) { | |
VpMemCpy(pDevObj->ringParams.swRegParam, pDevObj->swParamsCache, VP880_REGULATOR_PARAM_LEN); | |
} else { | |
VpMemCpy(pDevObj->swParamsCache, pDevObj->ringParams.swRegParam, VP880_REGULATOR_PARAM_LEN); | |
} | |
pDevObj->ringParams.channelArray[channelId] = TRUE; | |
/* The in-rush current limitation applies only in fixed voltage */ | |
/* Assuming that SWY is for VP880_EC_CH1 and SWZ is for VP880_EC_CH2 */ | |
if ((((ecVal & VP880_EC_BITS_MASK) == VP880_EC_CH1) && | |
(((pDevObj->swParamsCache[0] & VP880_YRING_TRACK_MASK) == VP880_YRING_TRACK_EN) || | |
((pDevObj->stateInt & VP880_LINE0_IS_FXO) == VP880_LINE0_IS_FXO))) || | |
(((ecVal & VP880_EC_BITS_MASK) == VP880_EC_CH2) && | |
(((pDevObj->swParamsCache[0] & VP880_ZRING_TRACK_MASK) == VP880_ZRING_TRACK_EN) || | |
(pDevObj->stateInt & VP880_LINE1_IS_FXO)))) { | |
sState = VP880_SS_BALANCED_RINGING; | |
VpMpiCmdWrapper(deviceId, ecVal, VP880_SYS_STATE_WRT, | |
VP880_SYS_STATE_LEN, &sState); | |
VP_LINE_STATE(None, NULL,("Vp880LimitInRushCurrent(): Tracking enable, set and exit")); | |
return; | |
} else { | |
if (pDevObj->ringParams.channelArray[0] == TRUE) { | |
pDevObj->swParamsCache[1] |= VP880_SWY_AUTOPOWER_DIS; | |
} | |
if (pDevObj->ringParams.channelArray[1] == TRUE) { | |
pDevObj->swParamsCache[2] |= VP880_SWZ_AUTOPOWER_DIS; | |
} | |
VpMpiCmdWrapper(deviceId, pDevObj->ecVal, VP880_REGULATOR_PARAM_WRT, | |
VP880_REGULATOR_PARAM_LEN, pDevObj->swParamsCache); | |
VP_LINE_STATE(None, NULL, | |
("Inrush Current Limit Switcher Programming: 0x%02X 0x%02X 0x%02X time %d", | |
pDevObj->swParamsCache[0], pDevObj->swParamsCache[1], pDevObj->swParamsCache[2], | |
pDevObj->timeStamp)); | |
} | |
/* Set the battery to medium power */ | |
VpMpiCmdWrapper(deviceId, pDevObj->ecVal, VP880_REGULATOR_CTRL_RD, | |
VP880_REGULATOR_CTRL_LEN, ®Ctrl); | |
if (channelId == 0) { | |
regCtrl &= ~VP880_SWY_MODE_MASK; | |
regCtrl |= VP880_SWY_MP; | |
} else { | |
regCtrl &= ~VP880_SWZ_MODE_MASK; | |
regCtrl |= VP880_SWZ_MP; | |
} | |
VpMpiCmdWrapper(deviceId, pDevObj->ecVal, VP880_REGULATOR_CTRL_WRT, | |
VP880_REGULATOR_CTRL_LEN, ®Ctrl); | |
/* Enable ZXR and set the slac state in balanced ringing */ | |
VpMpiCmdWrapper(deviceId, ecVal, VP880_SS_CONFIG_RD, VP880_SS_CONFIG_LEN, | |
&ssConf); | |
if ((ssConf & VP880_ZXR_MASK) == VP880_ZXR_DIS) { | |
ssConf &= ~VP880_ZXR_DIS; | |
VpMpiCmdWrapper(deviceId, ecVal, VP880_SS_CONFIG_WRT, | |
VP880_SS_CONFIG_LEN, &ssConf); | |
} | |
sStateRing = VP880_SS_BALANCED_RINGING; | |
VpMpiCmdWrapper(deviceId, ecVal, VP880_SYS_STATE_WRT, | |
VP880_SYS_STATE_LEN, &sStateRing); | |
pDevObj->ringParams.stage[channelId] = 0; | |
pDevObj->devTimer[VP_DEV_TIMER_ENTER_RINGING] = (MS_TO_TICKRATE(10, | |
pDevObj->devProfileData.tickRate)) | VP_ACTIVATE_TIMER; | |
} else { /* callback - no valid channel */ | |
VP_LINE_STATE(None, NULL,("Vp880LimitInRushCurrent(): callback")); | |
for (channelId = 0; channelId < VP880_MAX_NUM_CHANNELS; channelId++) { | |
uint8 ecValCh = ((channelId == 0) ? VP880_EC_CH1 : VP880_EC_CH2); | |
if (pDevObj->ringParams.channelArray[channelId] == TRUE) { | |
pDevObj->ringParams.stage[channelId]++; | |
if ((pDevObj->ringParams.stage[channelId] <= IN_RUSH_CYCLE_TOTAL) && | |
(pDevObj->ringParams.stage[channelId] > | |
(IN_RUSH_CYCLE_TOTAL - IN_RUSH_CYCLE_END))) { | |
/* Charge the battery capacitor in medium power and allow ringing */ | |
VP_LINE_STATE(None, NULL,("Vp880LimitInRushCurrent(): Finish Settling %d", | |
pDevObj->ringParams.stage[channelId])); | |
pDevObj->devTimer[VP_DEV_TIMER_ENTER_RINGING] = (MS_TO_TICKRATE(10, | |
pDevObj->devProfileData.tickRate)) | VP_ACTIVATE_TIMER; | |
} else if (pDevObj->ringParams.stage[channelId] < IN_RUSH_CYCLE_TOTAL) { | |
/* Charge the battery capacitor in medium power and avoid ringing */ | |
VP_LINE_STATE(None, NULL,("Vp880LimitInRushCurrent(): Settling %d", | |
pDevObj->ringParams.stage[channelId])); | |
sState = VP880_SS_ACTIVE; | |
VpMpiCmdWrapper(deviceId, (ecValCh | pDevObj->ecVal), | |
VP880_SYS_STATE_WRT, VP880_SYS_STATE_LEN, &sState); | |
sState = VP880_SS_BALANCED_RINGING; | |
VpMpiCmdWrapper(deviceId, (ecValCh | pDevObj->ecVal), | |
VP880_SYS_STATE_WRT, VP880_SYS_STATE_LEN, &sState); | |
pDevObj->devTimer[VP_DEV_TIMER_ENTER_RINGING] = (MS_TO_TICKRATE(10, | |
pDevObj->devProfileData.tickRate)) | VP_ACTIVATE_TIMER; | |
} else { | |
VP_LINE_STATE(None, NULL,("Vp880LimitInRushCurrent(): Conclusion")); | |
/* Reset the regulator in original mode */ | |
pDevObj->ringParams.channelArray[channelId] = FALSE; | |
if (pDevObj->ringParams.channelArray[0] == FALSE) { | |
if ((pDevObj->swParamsCache[1] & VP880_SWY_AUTOPOWER_MASK) == VP880_SWY_AUTOPOWER_DIS) { | |
pDevObj->swParamsCache[1] |= VP880_SWY_AUTOPOWER_DIS; | |
} else { | |
pDevObj->swParamsCache[1] &= ~VP880_SWY_AUTOPOWER_DIS; | |
} | |
} | |
if (pDevObj->ringParams.channelArray[1] == FALSE) { | |
if ((pDevObj->swParamsCache[1] & VP880_SWZ_AUTOPOWER_MASK) == VP880_SWZ_AUTOPOWER_DIS) { | |
pDevObj->swParamsCache[1] |= VP880_SWZ_AUTOPOWER_DIS; | |
} else { | |
pDevObj->swParamsCache[1] &= ~VP880_SWZ_AUTOPOWER_DIS; | |
} | |
} | |
if ((pDevObj->ringParams.channelArray[0] == FALSE) && | |
(pDevObj->ringParams.channelArray[1] == FALSE)) { | |
VP_LINE_STATE(None, NULL,("Vp880LimitInRushCurrent(): kill the timer")); | |
pDevObj->devTimer[VP_DEV_TIMER_ENTER_RINGING] = 0; | |
} | |
VpMpiCmdWrapper(deviceId, pDevObj->ecVal, VP880_REGULATOR_PARAM_WRT, | |
VP880_REGULATOR_PARAM_LEN, pDevObj->swParamsCache); | |
VP_LINE_STATE(None, NULL, | |
("Inrush Current Limit Conclusion Switcher Programming: 0x%02X 0x%02X 0x%02X time %d", | |
pDevObj->swParamsCache[0], pDevObj->swParamsCache[1], pDevObj->swParamsCache[2], | |
pDevObj->timeStamp)); | |
/* Set the battery to high power */ | |
VpMpiCmdWrapper(deviceId, pDevObj->ecVal, VP880_REGULATOR_CTRL_RD, | |
VP880_REGULATOR_CTRL_LEN, ®Ctrl); | |
regCtrl |= ((channelId == 0) ? VP880_SWY_HP : VP880_SWZ_HP); | |
VpMpiCmdWrapper(deviceId, pDevObj->ecVal, VP880_REGULATOR_CTRL_WRT, | |
VP880_REGULATOR_CTRL_LEN, ®Ctrl); | |
} | |
} | |
} | |
} | |
#endif | |
} | |
#endif /* VP880_TRACKER_SUPPORT */ | |
#endif |