blob: 8081e90d2b5a93997c1561105be377e894ca01ec [file] [log] [blame]
/** \file vp880_control_common.c
* vp880_control_common.c
*
* This file contains the control functions for the Vp880 device API.
*
* Copyright (c) 2011, Microsemi
*
* $Revision: 1.1.2.1.8.3 $
* $LastChangedDate: 2011-12-05 14:08:52 -0600 (Mon, 05 Dec 2011) $
*/
#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"
/**< Profile index for Generator A/B and C/D starting points (std tone) */
#define VP880_SIGGEN_AB_START (8)
#define VP880_SIGGEN_CD_START (16)
/**< Function called by Set Option only. Implements the options specified by
* the user. The calling function implements the Device/Line control. If a line
* option is set and a device option is passed, the calling function will call
* this function once for each line and pass it the line contexts. Therefore,
* this function will only be subjected to either a device context and device
* option, or a line context and a line option.
*/
static VpStatusType
Vp880SetOptionInternal(
VpLineCtxType *pLineCtx,
VpDevCtxType *pDevCtx,
VpOptionIdType option,
void *value);
/* Function called by SetOptionInternal for Event Masking only */
static void
Vp880MaskNonSupportedEvents(
VpOptionEventMaskType *pLineEventsMask,
VpOptionEventMaskType *pDevEventsMask);
/* Function called by SetOptionInternal to set tx and rx timeslot */
static VpStatusType
Vp880SetTimeSlot(
VpLineCtxType *pLineCtx,
uint8 txSlot,
uint8 rxSlot);
/**
* Vp880ApiTick()
* This function should be called on a periodic basis or attached to an
* interrupt.
*
* Preconditions:
* The device must first be initialized.
*
* Postconditions:
* The value passed (by pointer) is set to TRUE if there is an updated event.
* The user should call the GetEventStatus function to determine the cause of
* the event (TRUE value set). This function always returns the success code.
*/
VpStatusType
Vp880ApiTick(
VpDevCtxType *pDevCtx,
bool *pEventStatus)
{
VpLineCtxType *pLineCtx;
Vp880DeviceObjectType *pDevObj = pDevCtx->pDevObj;
Vp880LineObjectType *pLineObj;
VpDeviceIdType deviceId = pDevObj->deviceId;
uint8 ecVal;
uint8 numIntServiced = 2;
uint8 channelId;
uint8 maxChan = pDevObj->staticInfo.maxChannels;
bool tempClkFault, tempBat1Fault, tempBat2Fault, lineInTest;
bool intServCalled = FALSE;
bool linesCal[] = {FALSE, FALSE};
uint16 timeStampPre, tickAdder;
#ifdef VP_CSLAC_SEQ_EN
bool isSeqRunning = FALSE;
#endif
/*
* Do NOT Add VP_API_FUNC or VP_API_FUNC_INT to this function or any
* function directly and constantly called by this function (Calibration
* processes excluded). It will overload the console during debug.
*/
*pEventStatus = FALSE;
VpSysEnterCritical(deviceId, VP_CODE_CRITICAL_SEC);
/*
* Can't allow tick functions to proceed until Init Device function has
* been called. Otherwise, "tickrate" is unknown and initally 0.
*/
if (pDevObj->devProfileData.tickRate == 0) {
VpSysExitCritical(deviceId, VP_CODE_CRITICAL_SEC);
return VP_STATUS_DEV_NOT_INITIALIZED;
}
/*
* The timestamp is in 0.5mS increments, but the device tickrate is
* something else. So increment by the scaled amount and detect rollover
* by finding if the previous value is greater than the new value.
*/
timeStampPre = pDevObj->timeStamp;
tickAdder = pDevObj->devProfileData.tickRate / VP_CSLAC_TICKSTEP_0_5MS;
pDevObj->timeStamp+=tickAdder;
/*
* Since the tickrate can be in steps that are not multiples of 0.5ms, determine the
* roundoff remaining over the past several ticks and when it exceeds (0.5ms / 2), increase
* the timestamp.
*/
pDevObj->timeRemainder += (pDevObj->devProfileData.tickRate % VP_CSLAC_TICKSTEP_0_5MS);
if (pDevObj->timeRemainder >= (VP_CSLAC_TICKSTEP_0_5MS / 2)) {
pDevObj->timeRemainder -= VP_CSLAC_TICKSTEP_0_5MS;
pDevObj->timeStamp += 1;
}
if (timeStampPre > pDevObj->timeStamp) {
pDevObj->deviceEvents.signaling |= VP_DEV_EVID_TS_ROLLOVER;
}
/*
* Always reset the device object flag indicating that the PCM buffer has
* already been read this tick, because it hasn't. This flag is used
* throughout the VP-API-II to determine whether the PCM buffer needs to be
* read or whether the data exists in the device object already.
*/
pDevObj->state &= ~VP_DEV_TEST_BUFFER_READ;
#if defined (VP880_INTERRUPT_LEVTRIG_MODE)
VpSysEnableInt(deviceId);
#endif
/* Ensure that device is initialized */
if (!(pDevObj->state & VP_DEV_INIT_CMP)) {
if (Vp880FindSoftwareInterrupts(pDevCtx)) {
*pEventStatus = TRUE;
}
VpSysExitCritical(deviceId, VP_CODE_CRITICAL_SEC);
return VP_STATUS_SUCCESS;
}
/* Check if since last tick, one of the lines changed to/from WideBand mode */
if (pDevObj->lastCodecChange != 0) {
VpOptionCodecType codecMode;
/*
* A wideband mode change was made. Figure out which was the last channel
* changed and enforce that channel's codec setting on both channels.
*/
pLineCtx = pDevCtx->pLineCtx[pDevObj->lastCodecChange-1];
VP_LINE_STATE(VpDevCtxType, pDevCtx, ("Last Codec Change Channel %d",
(pDevObj->lastCodecChange-1)));
/*
* Only way line context can be null here is if the codec mode was just
* set and before calling the tick, the line context was set "free".
* Highly unusual, but technically possible. It would mean that the
* particular line object is no longer needed (because all available
* line contexts/objects MUST be associated with the device context
* passed into VpApiTick()).
*/
if (pLineCtx != VP_NULL) {
pLineObj = pLineCtx->pLineObj;
codecMode = pLineObj->codec;
if (codecMode == VP_OPTION_WIDEBAND) {
pDevObj->ecVal |= VP880_WIDEBAND_MODE;
} else {
pDevObj->ecVal &= ~VP880_WIDEBAND_MODE;
}
VP_LINE_STATE(VpDevCtxType, pDevCtx, ("Updating Device ecVal to 0x%02X",
pDevObj->ecVal));
/* Force both lines and device to correct wideband mode */
for(channelId=0; channelId < maxChan; channelId++ ) {
pLineCtx = pDevCtx->pLineCtx[channelId];
if (pLineCtx == VP_NULL) {
continue;
}
pLineObj = pLineCtx->pLineObj;
pLineObj->codec = codecMode;
if (codecMode == VP_OPTION_WIDEBAND) {
pLineObj->ecVal |= VP880_WIDEBAND_MODE;
} else {
pLineObj->ecVal &= ~VP880_WIDEBAND_MODE;
}
VP_LINE_STATE(VpLineCtxType, pLineCtx, ("Updating Line %d ecVal to 0x%02X",
pLineObj->channelId, pLineObj->ecVal));
}
}
pDevObj->lastCodecChange = 0;
}
ecVal = pDevObj->ecVal;
/* Service API Timers */
Vp880ServiceTimers(pDevCtx);
#ifdef VP880_LP_SUPPORT
/* Service LPM Termination Types. */
Vp880LowPowerMode(pDevCtx);
#endif
if (pDevObj->state & VP_DEV_IN_CAL) {
/*
* While in calibration, read from the signaling register just so the
* interrupt line clears. Otherwise, the system will see constant
* interrupts (if active level) AND the VP-API-II will generate multiple
` * interrupts when calibration completes.
*/
VpMpiCmdWrapper(deviceId, ecVal, VP880_UL_SIGREG_RD,
VP880_UL_SIGREG_LEN, pDevObj->intReg);
VpMpiCmdWrapper(deviceId, ecVal, VP880_UL_SIGREG_RD,
VP880_UL_SIGREG_LEN, pDevObj->intReg);
VpSysExitCritical(deviceId, VP_CODE_CRITICAL_SEC);
return VP_STATUS_SUCCESS;
}
/* Reset event pointers pointers */
pDevObj->dynamicInfo.lastChan = 0;
for (channelId = 0; channelId < maxChan; channelId++) {
pLineCtx = pDevCtx->pLineCtx[channelId];
if (pLineCtx != VP_NULL) {
pLineObj = pLineCtx->pLineObj;
#ifdef VP_CSLAC_SEQ_EN
/* Evaluate if Cadencing is required */
if (pLineObj->cadence.status & VP_CADENCE_STATUS_ACTIVE) {
isSeqRunning = TRUE;
}
#endif
/* Determine line and system calibration status */
if ((pLineObj->status & VP880_IS_FXO) ||
(pLineObj->calLineData.calDone == TRUE)) {
pLineObj->calLineData.calDone = TRUE;
linesCal[pLineObj->channelId] = TRUE;
}
if (pDevObj->stateInt & VP880_CAL_RELOAD_REQ) {
pLineObj->calLineData.calDone = TRUE;
linesCal[pLineObj->channelId] = TRUE;
Vp880UpdateCalValue(pLineCtx);
}
if (pDevObj->stateInt & VP880_SYS_CAL_RESET) {
if (!(pLineObj->status & VP880_IS_FXO)) {
pLineObj->calLineData.calDone = FALSE;
linesCal[pLineObj->channelId] = FALSE;
}
}
if (pDevObj->stateInt & VP880_SYS_CAL_COMPLETE) {
pLineObj->calLineData.calDone = TRUE;
linesCal[pLineObj->channelId] = TRUE;
}
}
}
if ((linesCal[0] == TRUE) && (linesCal[1] == TRUE)) {
pDevObj->stateInt |= VP880_SYS_CAL_COMPLETE;
}
pDevObj->stateInt &= ~(VP880_CAL_RELOAD_REQ | VP880_SYS_CAL_RESET);
#ifdef VP_CSLAC_SEQ_EN
if (isSeqRunning == TRUE) {
VpServiceSeq(pDevCtx);
}
#endif
/*
* Test the interrupt to see if there is a pending interrupt. If there is,
* read the interrupt registers (if running in an interrupt driven mode).
* If running in polled mode, automatically read the interrupt/status
* registers.
*/
#if defined (VP880_EFFICIENT_POLLED_MODE)
/* Poll the device PIO-INT line */
pDevObj->state |=
(VpSysTestInt(deviceId) ? VP_DEV_PENDING_INT : 0x00);
#elif defined (VP880_SIMPLE_POLLED_MODE)
pDevObj->state |= VP_DEV_PENDING_INT;
#endif
/*
* Adjust the EC value for Wideband mode as needed and set the line test
* flag if any line is under test.
*/
lineInTest = FALSE;
#if defined (VP880_INCLUDE_TESTLINE_CODE)
for (channelId = 0; channelId < maxChan; channelId++) {
pLineCtx = pDevCtx->pLineCtx[channelId];
if (pLineCtx != VP_NULL) {
if (Vp880IsChnlUndrTst(pDevObj, channelId) == TRUE) {
lineInTest = TRUE;
}
}
}
/*
* Also want to consider a line in test if running Read Loop Conditions.
* But if in Read Loop Conditions, the function "..IsChn" returns FALSE.
*/
lineInTest = ((pDevObj->currentTest.nonIntrusiveTest == TRUE) ? TRUE : lineInTest);
#endif
/* Read the PCM buffer once per tick IF there is line under test. */
if (pDevObj->staticInfo.rcnPcn[VP880_RCN_LOCATION] > VP880_REV_VC) {
if ((lineInTest == TRUE) && (!(pDevObj->state & VP_DEV_TEST_BUFFER_READ))) {
VpMpiCmdWrapper(deviceId, ecVal, VP880_TX_PCM_BUFF_RD,
VP880_TX_PCM_BUFF_LEN, pDevObj->txBuffer);
pDevObj->state |= VP_DEV_TEST_BUFFER_READ;
}
}
/* Service all pending interrupts (up to 2) */
while ((pDevObj->state & VP_DEV_PENDING_INT) && (numIntServiced > 0)) {
VpMpiCmdWrapper(deviceId, ecVal, VP880_UL_SIGREG_RD,
VP880_UL_SIGREG_LEN, pDevObj->intReg);
if (numIntServiced == 2) {
VpMemCpy(pDevObj->intReg2, pDevObj->intReg, VP880_UL_SIGREG_LEN);
}
/*******************************************************
* HANDLE Clock Fail Events *
*******************************************************/
if (!(pDevObj->devTimer[VP_DEV_TIMER_WB_MODE_CHANGE] & VP_ACTIVATE_TIMER)) {
/* Get the current status of the fault bit */
tempClkFault = (pDevObj->intReg[0] & VP880_CFAIL_MASK) ? TRUE : FALSE;
/*
* Compare it with what we already know. If different, generate
* events and update the line status bits
*/
if(tempClkFault ^ pDevObj->dynamicInfo.clkFault) {
#ifdef VP880_FXS_SUPPORT
if (!(pDevObj->stateInt & VP880_FORCE_FREE_RUN)) {
if (tempClkFault) {
/* Entering clock fault, possibly a system restart. */
Vp880FreeRun(pDevCtx, VP_FREE_RUN_START);
/*
* Clear the flag used to indicate that Vp880FreeRun() was
* called by the application -- because it wasn't.
*/
pDevObj->stateInt &= ~VP880_FORCE_FREE_RUN;
} else {
/*
* Exiting clock fault (note: this function does not affect
* VP880_FORCE_FREE_RUN flag).
*/
Vp880RestartComplete(pDevCtx);
}
}
#endif
pDevObj->dynamicInfo.clkFault = tempClkFault;
pDevObj->deviceEvents.faults |= VP_DEV_EVID_CLK_FLT;
}
}
/* Get the current status of the first battery fault bit */
tempBat1Fault = (pDevObj->intReg[0] & VP880_OCALMY_MASK) ? TRUE : FALSE;
tempBat2Fault = (pDevObj->intReg[0] & VP880_OCALMZ_MASK) ? TRUE : FALSE;
/* If line 1 is FXO, the Y supply is ignored */
pLineCtx = pDevCtx->pLineCtx[0];
if (pLineCtx != VP_NULL) {
pLineObj = pLineCtx->pLineObj;
if (!(pLineObj->status & VP880_IS_FXO)) {
if(tempBat1Fault ^ pDevObj->dynamicInfo.bat1Fault) {
pDevObj->dynamicInfo.bat1Fault = tempBat1Fault;
pDevObj->deviceEvents.faults |= VP_DEV_EVID_BAT_FLT;
}
}
}
/* If line 2 is FXO, the Z supply is ignored */
pLineCtx = pDevCtx->pLineCtx[1];
if (pLineCtx != VP_NULL) {
pLineObj = pLineCtx->pLineObj;
if (!(pLineObj->status & VP880_IS_FXO)) {
if(tempBat2Fault ^ pDevObj->dynamicInfo.bat2Fault) {
pDevObj->dynamicInfo.bat2Fault = tempBat2Fault;
pDevObj->deviceEvents.faults |= VP_DEV_EVID_BAT_FLT;
}
}
}
/*
* Compare it with what we already know. If different, generate
* events and update the line status bits
*/
intServCalled = TRUE;
Vp880ServiceInterrupts(pDevCtx);
/*
* If level triggered, the interrupt may have been disabled (to prevent
* a flood of interrupts), so reenable it.
*/
#if defined (VP880_INTERRUPT_LEVTRIG_MODE)
VpSysEnableInt(deviceId);
#endif
/* Clear the current interrupt indication */
pDevObj->state &= ~(VP_DEV_PENDING_INT);
numIntServiced--;
/*
* If operating in Efficient Polled Mode, check to see if the interrupt
* line is still indicating an active interrupt. If in simple polled mode,
* repeat the loop and service interrupts (if anything is changed).
*/
#if defined (VP880_EFFICIENT_POLLED_MODE)
/* Poll the PIO-INT line */
pDevObj->state |= (VpSysTestInt(deviceId) ? VP_DEV_PENDING_INT : 0x00);
#elif defined (VP880_SIMPLE_POLLED_MODE)
pDevObj->state |= VP_DEV_PENDING_INT;
#endif
}/* End while Interrupts*/
/* Make sure Vp880ServiceInterrupts() is called at least once per tick to
* keep the API line status up to date */
if (intServCalled == FALSE) {
Vp880ServiceInterrupts(pDevCtx);
}
/* Update the dial pulse handler for lines that are set for pulse decode */
for (channelId = 0; channelId < maxChan; channelId++) {
pLineCtx = pDevCtx->pLineCtx[channelId];
if (pLineCtx != VP_NULL) {
pLineObj = pLineCtx->pLineObj;
if (pLineObj->status & VP880_INIT_COMPLETE) {
if (pLineObj->status & VP880_IS_FXO) {
} else {
#ifdef VP880_FXS_SUPPORT
Vp880ProcessFxsLine(pDevObj, pLineCtx);
#endif
}
}
}
}
/* Collect all event activity and report to the calling function */
if (Vp880FindSoftwareInterrupts(pDevCtx)) {
*pEventStatus = TRUE;
}
VpSysExitCritical(deviceId, VP_CODE_CRITICAL_SEC);
return VP_STATUS_SUCCESS;
}
/**
* Vp880IsChnlUndrTst()
* This function determines if a particular line of a device is currently
* running a test.
*
* Preconditions:
* None.
*
* Postconditions:
* Device not affected. Return value TRUE if the line is currently running a
* test, FALSE otherwise.
*/
bool
Vp880IsChnlUndrTst(
Vp880DeviceObjectType *pDevObj,
uint8 channelId)
{
#ifdef VP880_INCLUDE_TESTLINE_CODE
if (pDevObj->currentTest.nonIntrusiveTest == FALSE) {
if ((TRUE == pDevObj->currentTest.prepared) &&
(channelId == pDevObj->currentTest.channelId)) {
return TRUE;
}
}
#endif
return FALSE;
}
/**
* Vp880ServiceInterrupts()
* This function should only be called by Vp880ApiTick when an interrupt
* occurs.
*
* Preconditions:
* The device must first be initialized.
*
* Postconditions:
* The Global Signaling Register is read and the data is stored in the device
* object. Depending on the dial pulse mode option set, the hook event (on/off)
* is generated if a hook status changed. All FXO events are reported by this
* function (i.e., no other processing necessary). This function will return
* TRUE if an event has been generated.
*/
bool
Vp880ServiceInterrupts(
VpDevCtxType *pDevCtx)
{
bool retFlag = FALSE;
#ifdef VP880_FXS_SUPPORT
VpLineCtxType *pLineCtx;
Vp880DeviceObjectType *pDevObj = pDevCtx->pDevObj;
Vp880LineObjectType *pLineObj;
VpDeviceIdType deviceId = pDevObj->deviceId;
uint8 ecVal;
uint8 channelId;
VpCslacLineCondType tempHookSt, tempGnkSt, tempThermFault;
VpLineStateType state;
bool freezeGkey;
bool ringTrip;
uint8 maxChannels = pDevObj->staticInfo.maxChannels;
for (channelId = 0; channelId < maxChannels; channelId++) {
freezeGkey = FALSE;
ringTrip = FALSE;
pLineCtx = pDevCtx->pLineCtx[channelId];
if (pLineCtx != VP_NULL) {
pLineObj = pLineCtx->pLineObj;
if (!(pLineObj->status & VP880_INIT_COMPLETE)) {
continue;
}
ecVal = pLineObj->ecVal;
state = pLineObj->lineState.currentState;
if (!(pLineObj->status & VP880_IS_FXO)) { /* Line Type is FXS */
VpLineStateType usrState = pLineObj->lineState.usrCurrent;
/*
* If debouncing for Ring Exit or Caller ID, ignore hook.
* Otherwise process.
*/
if (VpCSLACHookMaskEnabled(pLineObj->lineTimers.timers.timer)
|| (pDevObj->devTimer[VP_DEV_TIMER_LP_CHANGE] & VP_ACTIVATE_TIMER)
|| (pLineObj->lineState.calType != VP_CSLAC_CAL_NONE)
|| (pDevObj->state & VP_DEV_IN_CAL)
#ifdef VP_CSLAC_SEQ_EN
|| ((pLineObj->cadence.status & VP_CADENCE_STATUS_ACTIVE)
&& (pLineObj->intSequence[VP_PROFILE_TYPE_LSB] == VP_PRFWZ_PROFILE_FWD_DISC_INT))
|| ((pLineObj->cadence.status & VP_CADENCE_STATUS_ACTIVE)
&& (pLineObj->intSequence[VP_PROFILE_TYPE_LSB] == VP_PRFWZ_PROFILE_TIP_OPEN_INT))
#endif /* VP_CSLAC_SEQ_EN */
|| ((state == VP_LINE_DISCONNECT))
|| ((state == VP_LINE_TIP_OPEN))) {
tempHookSt = (VpCslacLineCondType)(pLineObj->lineState.condition & VP_CSLAC_HOOK);
} else {
if (pLineObj->status & VP880_LOW_POWER_EN) {
if (pDevObj->intReg[channelId] & VP880_HOOK1_MASK) {
tempHookSt = VP_CSLAC_STATUS_INVALID;
} else {
tempHookSt = VP_CSLAC_HOOK;
}
} else {
if (pDevObj->intReg[channelId] & VP880_HOOK1_MASK) {
tempHookSt = VP_CSLAC_HOOK;
} else {
tempHookSt = VP_CSLAC_STATUS_INVALID;
}
}
}
if (pDevObj->intReg[channelId] & VP880_TEMPA1_MASK) {
tempThermFault = VP_CSLAC_THERM_FLT;
} else {
tempThermFault = VP_CSLAC_STATUS_INVALID;
}
if ((pDevObj->devTimer[VP_DEV_TIMER_LP_CHANGE] & VP_ACTIVATE_TIMER)
|| (pLineObj->lineState.calType != VP_CSLAC_CAL_NONE)
|| (state == VP_LINE_DISCONNECT)
|| (pLineObj->lineTimers.timers.timer[VP_LINE_DISCONNECT_EXIT] & VP_ACTIVATE_TIMER)
|| (pLineObj->lineTimers.timers.timer[VP_LINE_RING_EXIT_PROCESS] & VP_ACTIVATE_TIMER)
|| (pLineObj->lineTimers.timers.timer[VP_LINE_GND_START_TIMER] & VP_ACTIVATE_TIMER)) {
tempGnkSt = (VpCslacLineCondType)(pLineObj->lineState.condition & VP_CSLAC_GKEY);
freezeGkey = TRUE;
} else {
if (pDevObj->intReg[channelId] & VP880_GNK1_MASK) {
tempGnkSt = VP_CSLAC_GKEY;
} else {
tempGnkSt = VP_CSLAC_STATUS_INVALID;
}
}
/*
* We "think" we know what Hook and Gkey are now, but it's
* possible the API-II is in the middle of the VoicePort Ground
* Start workaround. Check for the conditions where what is
* detected MUST be a Ground Key and not a Hook
*/
if ((!(pDevObj->devTimer[VP_DEV_TIMER_LP_CHANGE] & VP_ACTIVATE_TIMER))
&& (freezeGkey == FALSE)) {
if ((state == VP_LINE_TIP_OPEN)
|| (pLineObj->lineTimers.timers.timer[VP_LINE_GND_START_TIMER] & VP_ACTIVATE_TIMER)) {
uint8 currentHook = (pDevObj->intReg[channelId] & VP880_HOOK1_MASK);
tempGnkSt = (currentHook || tempGnkSt) ? VP_CSLAC_GKEY : VP_CSLAC_STATUS_INVALID;
tempHookSt = VP_CSLAC_STATUS_INVALID;
}
}
/* If the hook conditions changed, continue line processing */
if((VpCslacLineCondType)(pLineObj->lineState.condition & VP_CSLAC_HOOK) != tempHookSt) {
pLineObj->lineState.condition &= ~VP_CSLAC_HOOK;
pLineObj->lineState.condition |= tempHookSt;
if (pDevObj->staticInfo.rcnPcn[VP880_RCN_LOCATION] > VP880_REV_VC) {
/*
* Read the test buffer IF it was not yet read this tick AND we're
* running Dial Pulse Detection AND we're not in LPM (i.e., if
* Dial Pulse Detection is not occurring).
*/
if ((pLineObj->pulseMode == VP_OPTION_PULSE_DECODE_ON) &&
(!(pDevObj->state & VP_DEV_TEST_BUFFER_READ)) &&
(!(pLineObj->status & VP880_LOW_POWER_EN))) {
VpMpiCmdWrapper(deviceId, ecVal, VP880_TX_PCM_BUFF_RD,
VP880_TX_PCM_BUFF_LEN, pDevObj->txBuffer);
pDevObj->state |= VP_DEV_TEST_BUFFER_READ;
}
}
/* Apply the hysteresis on the hook threshold (if available) */
if (pLineObj->hookHysteresis != 0) {
uint8 loopSupervision[VP880_LOOP_SUP_LEN];
VpMemCpy(loopSupervision, pLineObj->loopSup, VP880_LOOP_SUP_LEN);
if ((loopSupervision[VP880_LOOP_SUP_LIU_THRESH_BYTE]
& VP880_LOOP_SUP_LIU_THRESH_BITS) >= pLineObj->hookHysteresis) {
loopSupervision[VP880_LOOP_SUP_LIU_THRESH_BYTE] -=
pLineObj->hookHysteresis;
} else {
loopSupervision[VP880_LOOP_SUP_LIU_THRESH_BYTE] &=
~VP880_LOOP_SUP_LIU_THRESH_BITS;
}
VpMpiCmdWrapper(deviceId, ecVal, VP880_LOOP_SUP_WRT,
VP880_LOOP_SUP_LEN, loopSupervision);
}
if ((pLineObj->status & VP880_LOW_POWER_EN) && tempHookSt
#ifdef VP880_INCLUDE_TESTLINE_CODE
&& (pDevObj->currentTest.nonIntrusiveTest == FALSE)
#endif
){
VP_HOOK(VpLineCtxType, pLineCtx,
("Off-Hook Detected in Low Power Mode on line %d time %d UserState %d Current State %d Status 0x%04X",
channelId, pDevObj->timeStamp, pLineObj->lineState.usrCurrent, pLineObj->lineState.currentState, pLineObj->status));
if ((pLineObj->lineState.calType == VP_CSLAC_CAL_NONE)
&& (Vp880IsChnlUndrTst(pDevObj, channelId) == FALSE)
) {
/* Force line to feed state and start leaky line detection */
pLineObj->lineState.currentState = VP_LINE_OHT;
pDevObj->stateInt &= ~((channelId == 0) ? VP880_LINE0_LP : VP880_LINE1_LP);
pLineObj->lineState.condition |= VP_CSLAC_LINE_LEAK_TEST;
}
break;
}
#ifdef VP_CSLAC_SEQ_EN
/*
* There was a sufficient hook activity to stop the active
* CID -- unless the CID sequence knew this would happen and
* set the debounce flag. In which case, let CID continue.
*/
if (pLineObj->callerId.status & VP_CID_IN_PROGRESS) {
if (pLineObj->callerId.status & VP_CID_IS_DEBOUNCE) {
/* Hook event is fully debounced and ready to go */
pLineObj->callerId.status &= ~VP_CID_IS_DEBOUNCE;
} else {
VpCliStopCli(pLineCtx);
Vp880SetLineTone(pLineCtx, VP_PTABLE_NULL,
VP_PTABLE_NULL, VP_NULL);
}
}
#endif /* VP_CSLAC_SEQ_EN */
if (tempHookSt == VP_CSLAC_HOOK) {
ringTrip = TRUE;
/* This function returns TRUE if an event is posted. */
if (Vp880OffHookMgmt(pDevObj, pLineCtx, ecVal) == TRUE) {
retFlag = TRUE;
}
} else {
if (Vp880OnHookMgmt(pDevObj, pLineCtx, ecVal) == TRUE) {
retFlag = TRUE;
}
}
}
/* If the gkey conditions changed, continue line processing */
if((VpCslacLineCondType)(pLineObj->lineState.condition & VP_CSLAC_GKEY) != tempGnkSt) {
VP_HOOK(VpLineCtxType, pLineCtx, ("GKEY Change to %d on Ch %d Time %d",
tempGnkSt, channelId, pDevObj->timeStamp));
if (tempGnkSt == VP_CSLAC_GKEY) {
ringTrip = TRUE;
pLineObj->lineEvents.signaling |= VP_LINE_EVID_GKEY_DET;
pLineObj->lineState.condition |= VP_CSLAC_GKEY;
} else {
pLineObj->lineEvents.signaling |= VP_LINE_EVID_GKEY_REL;
pLineObj->lineState.condition &= ~(VP_CSLAC_GKEY);
}
retFlag = TRUE;
pLineObj->lineEventHandle = pDevObj->timeStamp;
}
/*
* Force to Ring Trip Exit state if off-hook or ground-key is
* detected while ringing EXCEPT in case running a line test.
*/
if ((ringTrip == TRUE)
&& (Vp880IsChnlUndrTst(pDevObj, channelId) == FALSE)
&& ((usrState == VP_LINE_RINGING) || (usrState == VP_LINE_RINGING_POLREV))) {
/*
* If ringtrip occurs (off-hook detected while ringing) AND we're exiting to a
* non-ringing state, debounce the hook bit and start the ringing exit process.
*/
if ((pLineObj->ringCtrl.ringTripExitSt != VP_LINE_RINGING) &&
(pLineObj->ringCtrl.ringTripExitSt != VP_LINE_RINGING_POLREV)) {
VP_LINE_STATE(VpLineCtxType, pLineCtx, ("Forcing Ring Trip"));
Vp880SetLineState(pLineCtx, pLineObj->ringCtrl.ringTripExitSt);
/*
* This timer should be set in Vp880SetLineStateInt() called by
* Vp880SetLineState(). But just in case, make sure the ring exit time
* is set so that ringing will be removed and correctly hook debounce
* will be sest
*/
pLineObj->lineTimers.timers.timer[VP_LINE_RING_EXIT_PROCESS] =
(1 | VP_ACTIVATE_TIMER);
pLineObj->lineTimers.timers.trackingTime = 0;
} else {
VP_LINE_STATE(VpLineCtxType, pLineCtx,
("NO RING TRIP configured for Ch %d!!!!", pLineObj->channelId));
VP_LINE_STATE(VpLineCtxType, pLineCtx,
("Ringing will continue until fault or stopped by the Application"));
}
}
if((VpCslacLineCondType)(pLineObj->lineState.condition & VP_CSLAC_THERM_FLT)
!= tempThermFault) {
pLineObj->lineEventHandle = pDevObj->timeStamp;
pLineObj->lineState.condition &= ~(VP_CSLAC_THERM_FLT);
pLineObj->lineState.condition |= tempThermFault;
pLineObj->lineEvents.faults |= VP_LINE_EVID_THERM_FLT;
retFlag = TRUE;
if (tempThermFault == VP_CSLAC_THERM_FLT) {
#ifdef VP880_INCLUDE_TESTLINE_CODE
if((Vp880IsChnlUndrTst(pDevObj, channelId) == TRUE)
|| (pDevObj->currentTest.nonIntrusiveTest == TRUE)) {
pLineObj->lineEvents.test |= VP_LINE_EVID_ABORT;
} else if (pDevObj->criticalFault.thermFltDiscEn == TRUE) {
#endif /* VP880_INCLUDE_TESTLINE_CODE */
Vp880SetLineState(pLineCtx, VP_LINE_DISCONNECT);
#ifdef VP880_INCLUDE_TESTLINE_CODE
}
#endif /* VP880_INCLUDE_TESTLINE_CODE */
}
}
}
}
}
#endif /* VP880_FXS_SUPPORT */
return retFlag;
}
/**
* Vp880SetRelGain
* This function adjusts the GR and GX values for a given channel of a given
* device. It multiplies the profile values by a factor from 0.0 to 4.0. The
* adjustment factors are specified in the txLevel and rxLevel parameters,
* which are 2.14 fixed-point numbers.
*
* Preconditions:
* The line must first be initialized prior to adjusting the gains. Any
* pre-existing results must be cleared by calling VpGetResults() before
* calling this function.
*
* Postconditions:
* Returns error if device is not initialized or results are not cleared.
* Otherwise, generates a VE_LINE_EVID_GAIN_CMP event and saves results in
* the device object for later retrieval by VpGetResults().
*/
#ifdef CSLAC_GAIN_RELATIVE
VpStatusType
Vp880SetRelGain(
VpLineCtxType *pLineCtx, /**< Line context to change gains on */
uint16 txLevel, /**< Adjustment to line's relative Tx level */
uint16 rxLevel, /**< Adjustment to line's relative Rx level */
uint16 handle) /**< Handle value returned with the event */
{
Vp880LineObjectType *pLineObj = pLineCtx->pLineObj;
Vp880DeviceObjectType *pDevObj = pLineCtx->pDevCtx->pDevObj;
VpDeviceIdType deviceId = pDevObj->deviceId;
VpRelGainResultsType *relGainResults = &pDevObj->relGainResults;
uint8 ecVal = pLineObj->ecVal;
uint32 gxInt, grInt;
uint8 gainCSD[VP880_GX_GAIN_LEN];
uint8 mpiBuffer[2 + VP880_GX_GAIN_LEN + VP880_GR_GAIN_LEN] ;
uint8 mpiIndex = 0;
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880SetRelGain+"));
/* Proceed if device state is either in progress or complete */
if (pDevObj->state & (VP_DEV_INIT_CMP | VP_DEV_INIT_IN_PROGRESS)) {
} else {
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880SetRelGain-"));
return VP_STATUS_DEV_NOT_INITIALIZED;
}
/*
* Do not proceed if the device calibration is in progress. This could
* damage the device.
*/
if (pDevObj->state & VP_DEV_IN_CAL) {
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880SetRelGain-"));
return VP_STATUS_DEV_NOT_INITIALIZED;
}
VpSysEnterCritical(deviceId, VP_CODE_CRITICAL_SEC);
if (pDevObj->deviceEvents.response & VP880_READ_RESPONSE_MASK) {
VpSysExitCritical(deviceId, VP_CODE_CRITICAL_SEC);
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880SetRelGain-"));
return VP_STATUS_DEVICE_BUSY;
}
relGainResults->gResult = VP_GAIN_SUCCESS;
/* Multiply the profile gain values by the requested adjustments. */
gxInt = (uint32)pLineObj->gain.gxInt * txLevel / 16384;
grInt = (uint32)pLineObj->gain.grInt * rxLevel / 16384;
/* If overflow or underflow occurred, generate out-of-range result. */
/* Requirement: 1.0 <= gxInt <= 4.0 */
if ((gxInt < (uint32)0x4000) || (gxInt > (uint32)0x10000)) {
VP_GAIN(VpLineCtxType, pLineCtx, ("Vp880SetRelGain(): %u * %cxLevel / 16384 = %u, %s is %u",
(unsigned)pLineObj->gain.gxInt, 't', (unsigned)gxInt,
(gxInt < (uint32)0x4000) ? "minimum" : "maximum",
(gxInt < (uint32)0x4000) ? 0x4000U : 0x10000U));
relGainResults->gResult |= VP_GAIN_GX_OOR;
gxInt = pLineObj->gain.gxInt;
}
/* Requirement: 0.25 <= grInt <= 1.0 */
if ((grInt < (uint32)0x1000) || (grInt > (uint32)0x4000)) {
VP_GAIN(VpLineCtxType, pLineCtx, ("Vp880SetRelGain(): %u * %cxLevel / 16384 = %u, %s is %u",
(unsigned)pLineObj->gain.grInt, 'r', (unsigned)grInt,
(grInt < (uint32)0x1000) ? "minimum" : "maximum",
(grInt < (uint32)0x1000) ? 0x1000U : 0x4000U));
relGainResults->gResult |= VP_GAIN_GR_OOR;
grInt = pLineObj->gain.grInt;
}
VP_GAIN(VpLineCtxType, pLineCtx, ("Vp880SetRelGain(): %u * %cxLevel / 16384 = %u",
(unsigned)pLineObj->gain.gxInt, 't', (unsigned)gxInt));
VP_GAIN(VpLineCtxType, pLineCtx, ("Vp880SetRelGain(): %u * %cxLevel / 16384 = %u",
(unsigned)pLineObj->gain.grInt, 'r', (unsigned)grInt));
/*
* Write adjusted gain values to the device, and remember them for
* VpGetResults().
*/
VpConvertFixed2Csd((uint16)(gxInt - 0x4000), gainCSD);
relGainResults->gxValue = ((uint16)gainCSD[0] << 8) + gainCSD[1];
mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, VP880_GX_GAIN_WRT,
VP880_GX_GAIN_LEN, gainCSD);
VP_GAIN(VpLineCtxType, pLineCtx,
("Post Converted gxInt (write to GX_WRT): gainCSD = 0x%02X 0x%02X",
gainCSD[0], gainCSD[1]));
VpConvertFixed2Csd((uint16)grInt, gainCSD);
relGainResults->grValue = ((uint16)gainCSD[0] << 8) + gainCSD[1];
mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, VP880_GR_GAIN_WRT,
VP880_GR_GAIN_LEN, gainCSD);
VP_GAIN(VpLineCtxType, pLineCtx,
("Post Converted grInt (write to GR_WRT): gainCSD = 0x%02X 0x%02X",
gainCSD[0], gainCSD[1]));
/* send down the mpi commands */
VpMpiCmdWrapper(deviceId, ecVal, mpiBuffer[0], mpiIndex-1, &mpiBuffer[1]);
/* Generate the gain-complete event. */
pLineObj->lineEvents.response |= VP_LINE_EVID_GAIN_CMP;
pLineObj->lineEventHandle = handle;
VpSysExitCritical(deviceId, VP_CODE_CRITICAL_SEC);
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880SetRelGain-"));
return VP_STATUS_SUCCESS;
}
#endif
/**
* Vp880MuteChannel()
* This function disables or enables the PCM highway for the selected line and
* should only be called by API internal functions.
*
* Preconditions:
* The line context must be valid (i.e., pointing to a valid Vp880 line object
* type).
*
* Postconditions:
* If mode is TRUE the TX/RX path is cut. If FALSE, the TX/RX path is enabled
* according to the current line state and mode used for talk states.
*/
void
Vp880MuteChannel(
VpLineCtxType *pLineCtx, /**< Line affected */
bool mode) /**< TRUE = Disable TX/RX, FALSE = enable */
{
VpDevCtxType *pDevCtx = pLineCtx->pDevCtx;
Vp880DeviceObjectType *pDevObj = pDevCtx->pDevObj;
Vp880LineObjectType *pLineObj = pLineCtx->pLineObj;
uint8 ecVal = pLineObj->ecVal;
VpDeviceIdType deviceId = pDevObj->deviceId;
uint8 postState;
uint8 mpiByte = 0;
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880MuteChannel+"));
/*
* 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.
*/
postState = pLineObj->opCond[0];
postState &= (uint8)(~(VP880_CUT_TXPATH | VP880_CUT_RXPATH));
postState &= (uint8)(~(VP880_HIGH_PASS_DIS | VP880_OPCOND_RSVD_MASK));
/*
* If disabling, simple. Otherwise enable based on the current line state
* and the state of the "talk" option. The "talk" option is maintained in
* the line object and abstracted in Vp880GetTxRxMode() function
*/
Vp880GetTxRxPcmMode(pLineObj, pLineObj->lineState.currentState, &mpiByte);
if (mode == TRUE) {
/*
* If awaiting DTMF detection, enable TX, disable RX. This is higher
* priority than Mute mode. Otherwise, disable both TX and RX.
*/
postState |= VP880_CUT_RXPATH; /* Mute == TRUE always cuts RX path */
#if defined (VP_CSLAC_SEQ_EN) && defined (VP880_FXS_SUPPORT)
if (!(pLineObj->callerId.status & VP_CID_AWAIT_TONE)) {
#endif
/* Not awaiting tone, TX Path is disabled as well */
postState |= VP880_CUT_TXPATH;
#if defined (VP_CSLAC_SEQ_EN) && defined (VP880_FXS_SUPPORT)
}
#endif
} else {
/*
* It's possible that a Mute off is occuring because of end of DTMF
* detection, or end of data generation, or end of Mute period. However,
* we only need to check if Mute On is still enabled since DTMF
* detection will not occur while data is being generated.
*/
#if defined (VP_CSLAC_SEQ_EN) && defined (VP880_FXS_SUPPORT)
if (pLineObj->callerId.status & VP_CID_MUTE_ON) {
/*
* Some "other" operation completed, but we're still in a Mute On
* period.
*/
postState |= (VP880_CUT_RXPATH | VP880_CUT_TXPATH);
} else {
#endif
postState |= mpiByte;
#if defined (VP_CSLAC_SEQ_EN) && defined (VP880_FXS_SUPPORT)
}
#endif
}
if (postState != pLineObj->opCond[0]) {
VP_LINE_STATE(VpLineCtxType, pLineCtx, ("3. Writing 0x%02X to Operating Conditions",
postState));
pLineObj->opCond[0] = postState;
VpMpiCmdWrapper(deviceId, ecVal, VP880_OP_COND_WRT, VP880_OP_COND_LEN,
pLineObj->opCond);
}
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880MuteChannel-"));
return;
}
/**
* Vp880GetTxRxPcmMode()
* This function returns the TX/RX PCM bits for the PCM (enable/disable) mode
* corresponding to the state passed. The results should be or'-ed with the
* bits set to 0 prior to calling this function.
*
* Preconditions:
* None. Mapping function only.
*
* Postconditions:
* None. Mapping function only.
*/
VpStatusType
Vp880GetTxRxPcmMode(
Vp880LineObjectType *pLineObj,
VpLineStateType state, /**< The state associating with PCM mode */
uint8 *mpiByte) /**< Device Specific byte */
{
VP_API_FUNC_INT(None, VP_NULL, ("Vp880GetTxRxPcmMode+"));
switch(pLineObj->pcmTxRxCtrl) {
case VP_OPTION_PCM_BOTH:
*mpiByte = 0x00;
break;
case VP_OPTION_PCM_RX_ONLY:
*mpiByte = VP880_CUT_TXPATH;
break;
case VP_OPTION_PCM_TX_ONLY:
*mpiByte = VP880_CUT_RXPATH;
break;
case VP_OPTION_PCM_ALWAYS_ON:
*mpiByte = 0x00;
return VP_STATUS_SUCCESS;
default:
*mpiByte = 0x00;
break;
}
switch(state) {
/* Non-Talk States */
case VP_LINE_STANDBY:
case VP_LINE_STANDBY_POLREV:
case VP_LINE_TIP_OPEN:
case VP_LINE_ACTIVE:
case VP_LINE_ACTIVE_POLREV:
#ifdef VP_HIGH_GAIN_MODE_SUPPORTED
case VP_LINE_HOWLER:
case VP_LINE_HOWLER_POLREV:
#endif
case VP_LINE_DISCONNECT:
case VP_LINE_RINGING:
case VP_LINE_RINGING_POLREV:
if (pLineObj->status & VP880_IS_FXO) {
VP_API_FUNC_INT(None, VP_NULL, ("Vp880GetTxRxPcmMode-"));
return VP_STATUS_INVALID_ARG;
}
*mpiByte |= (VP880_CUT_TXPATH | VP880_CUT_RXPATH);
break;
case VP_LINE_FXO_LOOP_OPEN:
case VP_LINE_FXO_LOOP_CLOSE:
case VP_LINE_FXO_RING_GND:
if (!(pLineObj->status & VP880_IS_FXO)) {
VP_API_FUNC_INT(None, VP_NULL, ("Vp880GetTxRxPcmMode-"));
return VP_STATUS_INVALID_ARG;
}
*mpiByte |= (VP880_CUT_TXPATH | VP880_CUT_RXPATH);
break;
/* Talk States */
case VP_LINE_TALK:
case VP_LINE_TALK_POLREV:
case VP_LINE_OHT:
case VP_LINE_OHT_POLREV:
if (pLineObj->status & VP880_IS_FXO) {
VP_API_FUNC_INT(None, VP_NULL, ("Vp880GetTxRxPcmMode-"));
return VP_STATUS_INVALID_ARG;
}
break;
case VP_LINE_FXO_OHT:
case VP_LINE_FXO_TALK:
if (!(pLineObj->status & VP880_IS_FXO)) {
VP_API_FUNC_INT(None, VP_NULL, ("Vp880GetTxRxPcmMode-"));
return VP_STATUS_INVALID_ARG;
}
break;
default:
break;
}
VP_API_FUNC_INT(None, VP_NULL, ("Vp880GetTxRxPcmMode-"));
return VP_STATUS_SUCCESS;
}
/**
* Vp880SetLineTone()
* This function sets the line tone with the cadence specified on the line.
*
* Preconditions:
* The line must first be initialized.
*
* Postconditions:
* The tone specified by the tone profile is sent on the line at the cadence
* specified by the cadence profile. If the tone is NULL, all line tones are
* removed. If the cadence is NULL, the cadence is set to "Always On". This
* function returns the success code if the tone cadence is a valid tone cadence
* and the tone profile is a valid tone profile, or in the case where the user
* passes in profile indexes, if the tone/cadence indexes are within the range
* of the device.
*/
VpStatusType
Vp880SetLineTone(
VpLineCtxType *pLineCtx,
VpProfilePtrType pToneProfile, /**< A pointer to a tone profile, or an
* index into the profile table for the tone
* to put on the line.
*/
VpProfilePtrType pCadProfile, /**< A pointer to a tone cadence profile, or
* an index into the profile table for the
* tone cadence to put on the line.
*/
VpDtmfToneGenType *pDtmfControl) /**< Indicates to send a DTMF tone
* (either upstream or downstream) if
* this parameter is not VP_NULL AND
* the tone specified is VP_PTABLE_NULL
*/
{
Vp880LineObjectType *pLineObj = pLineCtx->pLineObj;
VpDevCtxType *pDevCtx = pLineCtx->pDevCtx;
Vp880DeviceObjectType *pDevObj = pDevCtx->pDevObj;
VpProfilePtrType pToneProf = VP_PTABLE_NULL;
#ifdef VP_CSLAC_SEQ_EN
VpProfilePtrType pCadProf = VP_PTABLE_NULL;
#endif
VpDigitType digit = VP_DIG_NONE;
VpDirectionType direction = VP_DIRECTION_INVALID;
uint8 ecVal = pLineObj->ecVal;
uint8 sigGenCtrl, mpiIndex = 0;
uint8 mpiByte = 0;
uint8 mpiBuffer[2 + VP880_SIGA_PARAMS_LEN + VP880_SIGCD_PARAMS_LEN];
/* Initialize SigGen A/B values to 0 */
uint8 sigGenAB[VP880_SIGA_PARAMS_LEN] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
VpDeviceIdType deviceId = pDevObj->deviceId;
uint8 opCondTarget = pLineObj->opCond[0];
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880SetLineTone+"));
/* Proceed if device state is either in progress or complete */
if (pDevObj->state & (VP_DEV_INIT_CMP | VP_DEV_INIT_IN_PROGRESS)) {
} else {
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880SetLineTone-"));
return VP_STATUS_DEV_NOT_INITIALIZED;
}
/*
* Do not proceed if the device calibration is in progress. This could
* damage the device.
*/
if (pDevObj->state & VP_DEV_IN_CAL) {
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880SetLineTone-"));
return VP_STATUS_DEV_NOT_INITIALIZED;
}
/* Check the legality of the Tone profile */
if (!VpCSLACIsProfileValid(VP_PROFILE_TONE, VP_CSLAC_TONE_PROF_TABLE_SIZE,
pDevObj->profEntry.toneProfEntry,
pDevObj->devProfileTable.pToneProfileTable, pToneProfile, &pToneProf)) {
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880SetLineTone-"));
return VP_STATUS_ERR_PROFILE;
}
/* Verify a good profile (index or pointer) for the cadence */
#ifdef VP_CSLAC_SEQ_EN
/* Check the legality of the Tone Cadence profile */
if (!VpCSLACIsProfileValid(VP_PROFILE_TONECAD, VP_CSLAC_TONE_CADENCE_PROF_TABLE_SIZE,
pDevObj->profEntry.toneCadProfEntry,
pDevObj->devProfileTable.pToneCadProfileTable, pCadProfile, &pCadProf)) {
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880SetLineTone-"));
return VP_STATUS_ERR_PROFILE;
}
#endif
if (pDtmfControl != VP_NULL) {
digit = pDtmfControl->toneId;
if (VpIsDigit(digit) == FALSE) {
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880SetLineTone-"));
return VP_STATUS_INVALID_ARG;
}
direction = pDtmfControl->dir;
if (direction != VP_DIRECTION_DS) {
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880SetLineTone-"));
return VP_STATUS_INVALID_ARG;
}
}
/* All input parameters are valid. */
VpSysEnterCritical(deviceId, VP_CODE_CRITICAL_SEC);
if (pLineObj->status & VP880_BAD_LOOP_SUP) {
pLineObj->status &= ~(VP880_BAD_LOOP_SUP);
VpMpiCmdWrapper(deviceId, ecVal, VP880_LOOP_SUP_WRT,
VP880_LOOP_SUP_LEN, pLineObj->loopSup);
}
/*
* Disable signal generator A/B/C/D before making any changes and stop
* previous cadences
*/
sigGenCtrl = 0;
if (sigGenCtrl != pLineObj->sigGenCtrl[0]) {
pLineObj->sigGenCtrl[0] = sigGenCtrl;
VpMpiCmdWrapper(deviceId, ecVal, VP880_GEN_CTRL_WRT, VP880_GEN_CTRL_LEN,
pLineObj->sigGenCtrl);
}
#if defined (VP_CSLAC_SEQ_EN) && defined (VP880_FXS_SUPPORT)
/*
* Force the "tone type" (used to indicate which special howler tone is currently being used)
* back to no tone so when we set the line to High Gain Mode at some point later it won't use
* the wrong set of coefficients.
*/
pLineObj->cadence.toneType = 0;
if (!(pLineObj->callerId.status & VP_CID_IN_PROGRESS)) {
#endif
#ifdef VP_CSLAC_SEQ_EN
pLineObj->cadence.pActiveCadence = pCadProf;
pLineObj->cadence.status = VP_CADENCE_RESET_VALUE;
/* We're no longer in the middle of a time function */
pLineObj->cadence.status &= ~VP_CADENCE_STATUS_MID_TIMER;
pLineObj->cadence.timeRemain = 0;
#endif
#if defined (VP_CSLAC_SEQ_EN) && defined (VP880_FXS_SUPPORT)
}
#endif
/*
* If tone profile is NULL, and either the pDtmfControl is NULL or it's
* "digit" member is "Digit None", then shutoff the tone generators, stop
* any active cadencing and restore the filter coefficients if they need
* to be. Also, re-enable the audio path if it was disabled by a previous
* DTMF generation command
*/
if ((pToneProf == VP_PTABLE_NULL)
&& ((pDtmfControl == VP_NULL) || (digit == VP_DIG_NONE))) {
/*
* Update the TX/RX Path enable/disable ONLY if not running CID. The CID
* sequence itself manages TX/RX path control
*/
#if defined (VP_CSLAC_SEQ_EN) && defined (VP880_FXS_SUPPORT)
if (!(pLineObj->callerId.status & VP_CID_IN_PROGRESS)) {
#endif
/*
* Pre-Or the bits and get the correct values based on the current
* line state, then update the device.
*/
opCondTarget &= (uint8)(~(VP880_HIGH_PASS_DIS | VP880_OPCOND_RSVD_MASK));
opCondTarget &= (uint8)(~(VP880_CUT_TXPATH | VP880_CUT_RXPATH));
Vp880GetTxRxPcmMode(pLineObj, pLineObj->lineState.currentState, &mpiByte);
opCondTarget |= mpiByte;
if (opCondTarget != pLineObj->opCond[0]) {
pLineObj->opCond[0] = opCondTarget;
VP_LINE_STATE(VpLineCtxType, pLineCtx, ("4. Writing 0x%02X to Operating Conditions",
pLineObj->opCond[0]));
VpMpiCmdWrapper(deviceId, ecVal, VP880_OP_COND_WRT,
VP880_OP_COND_LEN, pLineObj->opCond);
}
#if defined (VP_CSLAC_SEQ_EN) && defined (VP880_FXS_SUPPORT)
}
#endif
VpSysExitCritical(deviceId, VP_CODE_CRITICAL_SEC);
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880SetLineTone-"));
return VP_STATUS_SUCCESS;
}
/*
* If we're here, we're sending some tone. If it's DTMF, we can stop the
* active cadencer, set the time to "always on" (since the application will
* tell us when to start/stop).
*
* If "direction" is some value other than the initialized value, then
* the dtmf structure is passed and not NULL
*/
if (direction != VP_DIRECTION_INVALID) {
#ifdef VP_CSLAC_SEQ_EN
/* Disable currently active cadence */
pLineObj->cadence.status = VP_CADENCE_RESET_VALUE;
#endif
/* Update the DTMF Generators and make the downstream connection */
Vp880SetDTMFGenerators(pLineCtx, VP_CID_NO_CHANGE, digit);
/*
* Disable only the receive path since disabling the transmit path
* also may generate noise upstream (e.g., an unterminated, but
* assigned timeslot
*/
opCondTarget &= (uint8)(~(VP880_HIGH_PASS_DIS | VP880_OPCOND_RSVD_MASK));
opCondTarget |= VP880_CUT_RXPATH;
if (opCondTarget != pLineObj->opCond[0]) {
pLineObj->opCond[0] = opCondTarget;
VP_LINE_STATE(VpLineCtxType, pLineCtx, ("5. Writing 0x%02X to Operating Conditions",
pLineObj->opCond[0]));
VpMpiCmdWrapper(deviceId, ecVal, VP880_OP_COND_WRT, VP880_OP_COND_LEN,
pLineObj->opCond);
}
/* Enable only generator A/B */
sigGenCtrl = (VP880_GENB_EN | VP880_GENA_EN);
if (sigGenCtrl != pLineObj->sigGenCtrl[0]) {
pLineObj->sigGenCtrl[0] = sigGenCtrl;
VpMpiCmdWrapper(deviceId, ecVal, VP880_GEN_CTRL_WRT, VP880_GEN_CTRL_LEN,
pLineObj->sigGenCtrl);
}
VpSysExitCritical(deviceId, VP_CODE_CRITICAL_SEC);
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880SetLineTone-"));
return VP_STATUS_SUCCESS;
}
#if defined (VP_CSLAC_SEQ_EN) && defined (VP880_FXS_SUPPORT)
/* If we're here, we're sending a Tone, not DTMF */
if ((pCadProf != VP_PTABLE_NULL)
&& (((pCadProf[VP_CSLAC_TONE_TYPE] & VP_CSLAC_SPECIAL_TONE_MASK) == VP_CSLAC_HOWLER_TONE)
|| ((pCadProf[VP_CSLAC_TONE_TYPE] & VP_CSLAC_SPECIAL_TONE_MASK) == VP_CSLAC_AUS_HOWLER_TONE)
|| ((pCadProf[VP_CSLAC_TONE_TYPE] & VP_CSLAC_SPECIAL_TONE_MASK) == VP_CSLAC_NTT_HOWLER_TONE))) {
uint8 sigGenCD[VP880_SIGCD_PARAMS_LEN] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
/* Return ERROR if the Special Howler Init function doesn't know what this Tone Type is */
pLineObj->cadence.toneType = (pCadProf[VP_CSLAC_TONE_TYPE] & VP_CSLAC_SPECIAL_TONE_MASK);
if (!(VpCSLACHowlerInit(&pLineObj->cadence, pDevObj->devProfileData.tickRate))) {
pLineObj->cadence.toneType = VP_CSLAC_STD_TONE;
VpSysExitCritical(deviceId, VP_CODE_CRITICAL_SEC);
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880SetLineTone-"));
return VP_STATUS_INVALID_ARG;
}
sigGenAB[3] = ((pLineObj->cadence.startFreq >> 8) & 0xFF);
sigGenAB[4] = (pLineObj->cadence.startFreq & 0xFF);
sigGenAB[5] = ((pLineObj->cadence.startLevel >> 8) & 0xFF);
sigGenAB[6] = (pLineObj->cadence.startLevel & 0xFF);
/* Make sure C/D are cleared */
mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, VP880_SIGCD_PARAMS_WRT,
VP880_SIGCD_PARAMS_LEN, sigGenCD);
/* Program A/B */
mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, VP880_SIGA_PARAMS_WRT,
VP880_SIGA_PARAMS_LEN, sigGenAB);
/* Clear flag to indicate the generators are NOT in a Ringing Mode */
pLineObj->status &= ~(VP880_RING_GEN_NORM | VP880_RING_GEN_REV);
VpMpiCmdWrapper(deviceId, pLineObj->ecVal, mpiBuffer[0], mpiIndex-1, &mpiBuffer[1]);
/*
* Set the parameters in the line object for cadence use. The Cadence operations use
* the cached values when determining frequency/level adjustments.
*/
VpMemCpy(pLineObj->cadence.regData, sigGenAB, VP880_SIGA_PARAMS_LEN);
#ifdef VP_HIGH_GAIN_MODE_SUPPORTED
/* Update the Filter Coefficients if in High Gain Mode */
if (pLineObj->howlerModeCache.isInHowlerMode) {
VpCLSACHighGainMode(pLineCtx, TRUE);
}
#endif
VP_SEQUENCER(VpLineCtxType, pLineCtx, ("Ramp started at time %d", pDevObj->timeStamp));
} else {
#endif
/*
* Send the signal generator parameters to the device and enable the
* Tone Generators -- add in the first 3 bytes (all 0x00)
*/
VpMemCpy(&sigGenAB[VP880_SIGAB_FREQ_START], &pToneProf[VP880_SIGGEN_AB_START],
VP880_SIGA_PARAMS_LEN);
mpiIndex = 0;
mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, VP880_SIGA_PARAMS_WRT,
VP880_SIGA_PARAMS_LEN, sigGenAB);
/* Clear flag to indicate the generators are NOT in a Ringing Mode */
pLineObj->status &= ~(VP880_RING_GEN_NORM | VP880_RING_GEN_REV);
mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, VP880_SIGCD_PARAMS_WRT,
VP880_SIGCD_PARAMS_LEN, (uint8 *)(&pToneProf[VP880_SIGGEN_CD_START]));
VpMpiCmdWrapper(deviceId, pLineObj->ecVal, mpiBuffer[0], mpiIndex-1,
&mpiBuffer[1]);
#if defined (VP_CSLAC_SEQ_EN) && defined (VP880_FXS_SUPPORT)
}
#endif /* VP_CSLAC_SEQ_EN && VP880_FXS_SUPPORT */
#ifdef VP_CSLAC_SEQ_EN
if (pCadProf == VP_PTABLE_NULL) {
/*
* If a tone is being actived due to caller ID, then do not stop the
* cadencer
*/
#ifdef VP880_FXS_SUPPORT
if (!(pLineObj->callerId.status & VP_CID_IN_PROGRESS)) {
#endif /* VP880_FXS_SUPPORT */
pLineObj->cadence.status = VP_CADENCE_RESET_VALUE;
pLineObj->cadence.index = VP_PROFILE_TYPE_SEQUENCER_START;
#ifdef VP880_FXS_SUPPORT
}
#endif /* VP880_FXS_SUPPORT */
#endif /* VP_CSLAC_SEQ_EN */
sigGenCtrl = VP880_GEN_ALLON;
if (sigGenCtrl != pLineObj->sigGenCtrl[0]) {
pLineObj->sigGenCtrl[0] = sigGenCtrl;
VpMpiCmdWrapper(deviceId, ecVal, VP880_GEN_CTRL_WRT, VP880_GEN_CTRL_LEN,
pLineObj->sigGenCtrl);
}
#ifdef VP_CSLAC_SEQ_EN
} else {
pLineObj->cadence.pCurrentPos =
&(pCadProf[VP_PROFILE_TYPE_SEQUENCER_START]);
pLineObj->cadence.status |= VP_CADENCE_STATUS_ACTIVE;
pLineObj->cadence.length = pCadProf[VP_PROFILE_LENGTH];
pLineObj->cadence.index = VP_PROFILE_TYPE_SEQUENCER_START;
pLineObj->cadence.status &= ~VP_CADENCE_STATUS_IGNORE_POLARITY;
pLineObj->cadence.status |= (pCadProf[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;
}
#endif /* VP_CSLAC_SEQ_EN */
VpSysExitCritical(deviceId, VP_CODE_CRITICAL_SEC);
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880SetLineTone-"));
return VP_STATUS_SUCCESS;
}
/**
* Vp880SetDTMFGenerators()
* This function sets signal generator A/B for DTMF tone generation.
*
* Preconditions:
* The line must first be initialized.
*
* Postconditions:
* The signal generators A/B are set to the DTMF frequencies and level required
* by the digit passed.
*/
VpStatusType
Vp880SetDTMFGenerators(
VpLineCtxType *pLineCtx,
VpCidGeneratorControlType mode,
VpDigitType digit)
{
VpStatusType status;
Vp880LineObjectType *pLineObj = pLineCtx->pLineObj;
VpDevCtxType *pDevCtx = pLineCtx->pDevCtx;
Vp880DeviceObjectType *pDevObj = pDevCtx->pDevObj;
uint8 ecVal = pLineObj->ecVal;
uint8 sigGenCtrl[VP880_GEN_CTRL_LEN] = {VP880_GEN_ALLOFF};
VpDeviceIdType deviceId = pDevObj->deviceId;
#if defined (VP_CSLAC_SEQ_EN) && defined (VP880_FXS_SUPPORT)
uint8 sigByteCount;
uint8 sigOffset = VP_CID_PROFILE_FSK_PARAM_LEN + 2;
#endif
uint8 sigGenABParams[] = {
0x00, 0x00, 0x00, /* RSVD */
0x00, 0x00, /* Replace with required column Frequency */
0x1C, 0x32, /* Level = -10dBm */
0x00, 0x00, /* Replace with required row Frequency */
0x1C, 0x32 /* Level = -10dBm */
};
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880SetDTMFGenerators+"));
#if defined (VP_CSLAC_SEQ_EN) && defined (VP880_FXS_SUPPORT)
/*
* If we're generating caller ID data set the levels based on the data in
* the CID profile
*/
if ((pLineObj->callerId.status & VP_CID_IN_PROGRESS) &&
(pLineObj->callerId.pCliProfile != VP_PTABLE_NULL)) {
for (sigByteCount = 0; sigByteCount < (VP880_SIGA_PARAMS_LEN - 3);
sigByteCount++) {
sigGenABParams[sigByteCount+3] =
pLineObj->callerId.pCliProfile[sigOffset + sigByteCount];
}
} else {
#endif
/*
* If it's an FXO line then the DTMF high and low frequency levels are
* specified in the FXO/Dialing Profile, cached in the line object.
*/
#if defined (VP_CSLAC_SEQ_EN) && defined (VP880_FXO_SUPPORT)
if (pLineObj->status & VP880_IS_FXO) {
sigGenABParams[5] = pLineObj->digitGenStruct.dtmfHighFreqLevel[0];
sigGenABParams[6] = pLineObj->digitGenStruct.dtmfHighFreqLevel[1];
sigGenABParams[9] = pLineObj->digitGenStruct.dtmfLowFreqLevel[0];
sigGenABParams[10] = pLineObj->digitGenStruct.dtmfLowFreqLevel[1];
}
#endif
#if defined (VP_CSLAC_SEQ_EN) && defined (VP880_FXS_SUPPORT)
}
#endif
/*
* Modify values sigGenABParams[3][4] and [7][8] with values required for the DTMF Frequencies
* using common VE880/890 computations
*/
status = VpCSLACSetDTMFGenValues(&sigGenABParams[3], digit);
if (status != VP_STATUS_SUCCESS) {
return status;
}
VpMpiCmdWrapper(deviceId, ecVal, VP880_SIGA_PARAMS_WRT,
VP880_SIGA_PARAMS_LEN, sigGenABParams);
/* Clear flag to indicate the generators are NOT in a Ringing Mode */
pLineObj->status &= ~(VP880_RING_GEN_NORM | VP880_RING_GEN_REV);
/*
* If there is no change to generator control required, it is assumed to be
* set properly prior to this function call.
*/
if (mode != VP_CID_NO_CHANGE) {
/*
* For DTMF CID, the data passed may be message data, a keyed character
* (e.g., Mark, Channel Seizure), or End of Transmission. If it's End
* of Transmission, disable the DTMF generators immediately. Otherwise,
* enable the DTMF generators
*/
#if defined (VP_CSLAC_SEQ_EN) && defined (VP880_FXS_SUPPORT)
if ((mode == VP_CID_GENERATOR_DATA)
|| (mode == VP_CID_GENERATOR_KEYED_CHAR)) {
sigGenCtrl[0] |= (VP880_GENA_EN | VP880_GENB_EN);
/* Setup the line timer for the on-time for DTMF CID */
pLineObj->lineTimers.timers.timer[VP_LINE_TIMER_CID_DTMF] =
MS_TO_TICKRATE(VP_CID_DTMF_ON_TIME,
pDevObj->devProfileData.tickRate);
pLineObj->lineTimers.timers.timer[VP_LINE_TIMER_CID_DTMF]
|= VP_ACTIVATE_TIMER;
pLineObj->callerId.dtmfStatus |= VP_CID_ACTIVE_ON_TIME;
}
#endif
if (sigGenCtrl[0] != pLineObj->sigGenCtrl[0]) {
pLineObj->sigGenCtrl[0] = sigGenCtrl[0];
VpMpiCmdWrapper(deviceId, ecVal, VP880_GEN_CTRL_WRT, VP880_GEN_CTRL_LEN,
pLineObj->sigGenCtrl);
}
}
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880SetDTMFGenerators-"));
return VP_STATUS_SUCCESS;
}
/**
* Vp880SetOption()
* This function determines how to process the Option based on pDevCtx,
* pLineCtx, and option type. The actual options are implemented in
* Vp880SetOptionInternal
*
* Preconditions:
* The line must first be initialized if a line context is passed, or the
* device must be initialized if a device context is passed.
*
* Postconditions:
* The option specified is implemented either on the line, or on the device, or
* on all lines associated with the device (see the API Reference Guide for
* details).
*/
VpStatusType
Vp880SetOption(
VpLineCtxType *pLineCtx,
VpDevCtxType *pDevCtx,
VpOptionIdType option,
void *value)
{
uint8 channelId;
Vp880DeviceObjectType *pDevObj;
VpStatusType status = VP_STATUS_INVALID_ARG;
VpDevCtxType *pDevCtxLocal;
VpLineCtxType *pLineCtxLocal;
Vp880LineObjectType *pLineObj;
VpDeviceIdType deviceId;
bool onlyFXO = TRUE;
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880SetOption+"));
if (pDevCtx != VP_NULL) {
pDevObj = pDevCtx->pDevObj;
deviceId = pDevObj->deviceId;
if (option != VP_OPTION_ID_DEBUG_SELECT) {
/* Proceed if device state is either in progress or complete */
if (pDevObj->state & (VP_DEV_INIT_CMP | VP_DEV_INIT_IN_PROGRESS)) {
} else {
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880SetOption-"));
return VP_STATUS_DEV_NOT_INITIALIZED;
}
/*
* Do not proceed if the device calibration is in progress. This could
* damage the device.
*/
if (pDevObj->state & VP_DEV_IN_CAL) {
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880SetOption-"));
return VP_STATUS_DEV_NOT_INITIALIZED;
}
}
VpSysEnterCritical(deviceId, VP_CODE_CRITICAL_SEC);
/*
* Valid Device Context, we already know Line context is NULL (higher
* layer SW, process on device if device option, or process on all lines
* associated with device if line option
*/
switch (option) {
case VP_OPTION_ID_EVENT_MASK: /* Line and Device */
Vp880SetOptionInternal(VP_NULL, pDevCtx, option, value);
/* Line Options */
case VP_OPTION_ID_ZERO_CROSS:
case VP_OPTION_ID_PULSE_MODE:
case VP_OPTION_ID_TIMESLOT:
case VP_OPTION_ID_CODEC:
case VP_OPTION_ID_PCM_HWY:
case VP_OPTION_ID_LOOPBACK:
case VP_OPTION_ID_LINE_STATE:
case VP_OPTION_ID_RING_CNTRL:
case VP_OPTION_ID_PCM_TXRX_CNTRL:
#ifdef CSLAC_GAIN_ABS
case VP_OPTION_ID_ABS_GAIN:
#endif
/*
* Loop through all of the valid channels associated with this
* device. Init status variable in case there are currently no
* line contexts associated with this device
*/
status = VP_STATUS_SUCCESS;
for (channelId = 0; channelId < pDevObj->staticInfo.maxChannels; channelId++) {
pLineCtxLocal = pDevCtx->pLineCtx[channelId];
if (pLineCtxLocal == VP_NULL) {
continue;
}
if ((option == VP_OPTION_ID_ZERO_CROSS) ||
(option == VP_OPTION_ID_PULSE_MODE) ||
(option == VP_OPTION_ID_LINE_STATE) ||
(option == VP_OPTION_ID_RING_CNTRL)){
uint8 lastChannel = (pDevObj->staticInfo.maxChannels - 1);
pLineObj = pLineCtxLocal->pLineObj;
/* This device has at least 1 FXS, SetOption will succeed */
if (!(pLineObj->status & VP880_IS_FXO)) {
onlyFXO = FALSE;
status = Vp880SetOptionInternal(pLineCtxLocal, VP_NULL, option, value);
/* Only FXO on this device */
} else if ((onlyFXO == TRUE) && (channelId == lastChannel)) {
status = VP_STATUS_OPTION_NOT_SUPPORTED;
/* Just bailout in case there is at least 1 FXS on this device */
} else {
break;
}
} else {
status = Vp880SetOptionInternal(pLineCtxLocal, VP_NULL, option, value);
}
if (VP_STATUS_SUCCESS != status) {
VpSysExitCritical(deviceId, VP_CODE_CRITICAL_SEC);
VP_API_FUNC_INT(VpLineCtxType, pLineCtxLocal, ("Vp880SetOption-"));
return status;
}
}
break;
default:
/*
* Device option, or option unknown option. Handle in lower
* layer
*/
status = Vp880SetOptionInternal(VP_NULL, pDevCtx, option, value);
break;
}
} else {
/*
* Line context must be valid, device context is NULL, proceed as
* normal
*/
pDevCtxLocal = pLineCtx->pDevCtx;
pDevObj = pDevCtxLocal->pDevObj;
deviceId = pDevObj->deviceId;
if (option != VP_OPTION_ID_DEBUG_SELECT) {
/* Proceed if device state is either in progress or complete */
if (pDevObj->state & (VP_DEV_INIT_CMP | VP_DEV_INIT_IN_PROGRESS)) {
} else {
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880SetOption-"));
return VP_STATUS_DEV_NOT_INITIALIZED;
}
/*
* Do not proceed if the device calibration is in progress. This could
* damage the device.
*/
if (pDevObj->state & VP_DEV_IN_CAL) {
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880SetOption-"));
return VP_STATUS_DEV_NOT_INITIALIZED;
}
}
VpSysEnterCritical(deviceId, VP_CODE_CRITICAL_SEC);
status = Vp880SetOptionInternal(pLineCtx, VP_NULL, option, value);
}
VpSysExitCritical(deviceId, VP_CODE_CRITICAL_SEC);
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880SetOption-"));
return status;
}
/**
* Vp880SetOptionInternal()
* This function implements on the Vp880 device the options specified from
* Vp880SetOption(). No other function should call this function.
*
* Preconditions:
* See Vp880SetOption()
*
* Postconditions:
* See Vp880SetOption()
*/
VpStatusType
Vp880SetOptionInternal(
VpLineCtxType *pLineCtx,
VpDevCtxType *pDevCtx,
VpOptionIdType option,
void *value)
{
VpDevCtxType *pDevCtxLocal;
VpLineCtxType *pLineCtxLocal;
VpStatusType status = VP_STATUS_SUCCESS;
Vp880LineObjectType *pLineObj;
Vp880DeviceObjectType *pDevObj;
uint8 tempData[VP880_INT_MASK_LEN], channelId, txSlot, rxSlot;
VpDeviceIdType deviceId;
VpOptionDeviceIoType deviceIo;
uint8 maxChan;
uint8 mpiByte = 0;
uint8 ioDirection[2] = {0x00, 0x00};
#ifdef VP880_FXS_SUPPORT
uint8 tempSysConfig[VP880_SS_CONFIG_LEN];
#endif
uint8 ecVal;
VpOptionEventMaskType *pEventsMask, *pNewEventsMask;
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880SetOptionInternal+"));
if (pLineCtx != VP_NULL) {
pDevCtxLocal = pLineCtx->pDevCtx;
pDevObj = pDevCtxLocal->pDevObj;
deviceId = pDevObj->deviceId;
pLineObj = pLineCtx->pLineObj;
channelId = pLineObj->channelId;
ecVal = pLineObj->ecVal;
switch (option) {
/* Line Options */
#ifdef CSLAC_GAIN_ABS
case VP_OPTION_ID_ABS_GAIN:
status = VpCSLACSetAbsGain(pLineCtx, ((VpOptionAbsGainType *)value));
break;
#endif
#ifdef VP880_FXS_SUPPORT
case VP_OPTION_ID_PULSE_MODE:
if (pLineObj->status & VP880_IS_FXO) {
status = VP_STATUS_OPTION_NOT_SUPPORTED;
break;
}
if (pLineObj->pulseMode != *((VpOptionPulseModeType *)value)) {
pLineObj->pulseMode = *((VpOptionPulseModeType *)value);
if (pLineObj->lineState.condition & VP_CSLAC_HOOK) {
pLineObj->dpStruct.hookSt = TRUE;
pLineObj->dpStruct2.hookSt = TRUE;
} else {
pLineObj->dpStruct.hookSt = FALSE;
pLineObj->dpStruct2.hookSt = FALSE;
}
VpInitDP(&pLineObj->dpStruct);
VpInitDP(&pLineObj->dpStruct2);
}
break;
#endif
case VP_OPTION_ID_TIMESLOT:
txSlot = ((VpOptionTimeslotType *)value)->tx;
rxSlot = ((VpOptionTimeslotType *)value)->rx;
status = Vp880SetTimeSlot(pLineCtx, txSlot, rxSlot);
break;
case VP_OPTION_ID_CODEC:
status = Vp880SetCodec(pLineCtx, *((VpOptionCodecType *)value));
break;
case VP_OPTION_ID_PCM_HWY:
if (*((VpOptionPcmHwyType *)value) != VP_OPTION_HWY_A) {
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880SetOptionInternal-"));
return VP_STATUS_INVALID_ARG;
}
break;
case VP_OPTION_ID_LOOPBACK:
/* Timeslot loopback via loopback register */
switch(*((VpOptionLoopbackType *)value)) {
case VP_OPTION_LB_TIMESLOT:
pLineObj->opCond[0] |= VP880_INTERFACE_LOOPBACK_EN;
break;
case VP_OPTION_LB_OFF:
pLineObj->opCond[0] &= ~(VP880_INTERFACE_LOOPBACK_EN);
break;
case VP_OPTION_LB_DIGITAL:
default:
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880SetOptionInternal-"));
return VP_STATUS_INVALID_ARG;
}
VP_LINE_STATE(VpLineCtxType, pLineCtx,("Writing Op Cond (Loopback) 0x%02X",
pLineObj->opCond[0]));
VpMpiCmdWrapper(deviceId, ecVal, VP880_LOOPBACK_WRT,
VP880_LOOPBACK_LEN, pLineObj->opCond);
break;
#ifdef VP880_FXS_SUPPORT
case VP_OPTION_ID_LINE_STATE:
/* Option does not apply to FXO */
if (pLineObj->status & VP880_IS_FXO) {
status = VP_STATUS_OPTION_NOT_SUPPORTED;
break;
}
/*
* Only supports one type of battery control, so make sure it
* is set correctly. If not, return error otherwise continue
*/
if (((VpOptionLineStateType *)value)->bat
!= VP_OPTION_BAT_AUTO) {
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880SetOptionInternal-"));
return VP_STATUS_INVALID_ARG;
}
VpMpiCmdWrapper(deviceId, ecVal, VP880_SS_CONFIG_RD,
VP880_SS_CONFIG_LEN, tempSysConfig);
if (((VpOptionLineStateType *)value)->battRev == TRUE) {
tempSysConfig[0] &= ~(VP880_SMOOTH_PR_EN);
} else {
tempSysConfig[0] |= VP880_SMOOTH_PR_EN;
}
VpMpiCmdWrapper(deviceId, ecVal, VP880_SS_CONFIG_WRT,
VP880_SS_CONFIG_LEN, tempSysConfig);
break;
#endif
case VP_OPTION_ID_EVENT_MASK:
pNewEventsMask = (VpOptionEventMaskType *)value;
/*
* Zero out the line-specific bits before setting the deviceEventsMask in the
* device object.
*/
pEventsMask = &pDevObj->deviceEventsMask;
pEventsMask->faults = pNewEventsMask->faults & VP_EVCAT_FAULT_DEV_EVENTS;
pEventsMask->signaling = pNewEventsMask->signaling & VP_EVCAT_SIGNALING_DEV_EVENTS;
pEventsMask->response = pNewEventsMask->response & VP_EVCAT_RESPONSE_DEV_EVENTS;
pEventsMask->test = pNewEventsMask->test & VP_EVCAT_TEST_DEV_EVENTS;
pEventsMask->process = pNewEventsMask->process & VP_EVCAT_PROCESS_DEV_EVENTS;
pEventsMask->fxo = pNewEventsMask->fxo & VP_EVCAT_FXO_DEV_EVENTS;
/*
* Zero out the device-specific bits before setting the lineEventsMask in the
* line object.
*/
pEventsMask = &pLineObj->lineEventsMask;
pEventsMask->faults = pNewEventsMask->faults & ~VP_EVCAT_FAULT_DEV_EVENTS;
pEventsMask->signaling = pNewEventsMask->signaling & ~VP_EVCAT_SIGNALING_DEV_EVENTS;
pEventsMask->response = pNewEventsMask->response & ~VP_EVCAT_RESPONSE_DEV_EVENTS;
pEventsMask->test = pNewEventsMask->test & ~VP_EVCAT_TEST_DEV_EVENTS;
pEventsMask->process = pNewEventsMask->process & ~VP_EVCAT_PROCESS_DEV_EVENTS;
pEventsMask->fxo = pNewEventsMask->fxo & ~VP_EVCAT_FXO_DEV_EVENTS;
/* Unmask the unmaskable */
VpImplementNonMaskEvents(&pLineObj->lineEventsMask, &pDevObj->deviceEventsMask);
/* Mask those events that the VP880 API-II cannot generate */
Vp880MaskNonSupportedEvents(&pLineObj->lineEventsMask, &pDevObj->deviceEventsMask);
/*
* The next code section prevents the device from interrupting
* the processor if all of the events associated with the
* specific hardware interrupt are masked
*/
VpMpiCmdWrapper(deviceId, ecVal, VP880_INT_MASK_RD, VP880_INT_MASK_LEN, tempData);
/* Keep Clock Fault Interrupt Enabled for auto-free run mode. */
tempData[0] &= ~VP880_CFAIL_MASK;
if (pDevObj->deviceEventsMask.faults & VP_DEV_EVID_CLK_FLT) {
tempData[0] &= ~VP880_CFAIL_MASK;
}
if (!(pLineObj->status & VP880_IS_FXO)) { /* Line is FXS */
#ifdef VP880_FXS_SUPPORT
/* Mask off the FXO events */
pLineObj->lineEventsMask.fxo |= VP_EVCAT_FXO_MASK_ALL;
/*
* Never mask the thermal fault interrupt otherwise the
* actual thermal fault may not be seen by the VP-API-II.
*/
tempData[channelId] &= ~VP880_TEMPA1_MASK;
/*
* Never mask the hook interrupt otherwise interrupt modes
* of the VP-API-II for LPM types won't work -- hook status
* is never updated, leaky line never properly detected.
*/
tempData[channelId] &= ~VP880_HOOK1_MASK;
/*
* Never mask the gkey interrupt otherwise interrupt modes
* of the VP-API-II won't support "get line status"
* correctly.
*/
tempData[channelId] &= ~VP880_GNK1_MASK;
/* Implement Operation Note 8 on errata notice V103 */
tempData[channelId] &= ~(VP880_OCALMY_MASK);
#endif
} else { /* Line is FXO */
#ifdef VP880_FXO_SUPPORT
/* Mask off the FXS events */
pLineObj->lineEventsMask.signaling |= VP880_FXS_SIGNALING_EVENTS;
/*
* Never mask the FXO interrupts otherwise interrupt modes of the VP-API-II
* won't support FXO "get line status" correctly.
*/
tempData[channelId] &= (uint8)(~(VP880_LIU1_MASK | VP880_RING1_DET_MASK
| VP880_POL1_MASK | VP880_DISC1_MASK));
#endif
}
VpMpiCmdWrapper(deviceId, ecVal, VP880_INT_MASK_WRT, VP880_INT_MASK_LEN, tempData);
break;
#ifdef VP880_FXS_SUPPORT
case VP_OPTION_ID_ZERO_CROSS:
/* Option does not apply to FXO */
if (pLineObj->status & VP880_IS_FXO) {
status = VP_STATUS_OPTION_NOT_SUPPORTED;
break;
}
VpMpiCmdWrapper(deviceId, ecVal, VP880_SS_CONFIG_RD,
VP880_SS_CONFIG_LEN, tempSysConfig);
if (*(VpOptionZeroCrossType *)value == VP_OPTION_ZC_NONE) {
tempSysConfig[0] |= VP880_ZXR_DIS;
} else {
tempSysConfig[0] &= ~(VP880_ZXR_DIS);
}
VpMpiCmdWrapper(deviceId, ecVal, VP880_SS_CONFIG_WRT,
VP880_SS_CONFIG_LEN, tempSysConfig);
pLineObj->ringCtrl.zeroCross = *((VpOptionZeroCrossType *)value);
break;
case VP_OPTION_ID_RING_CNTRL: {
VpOptionRingControlType TempRingCtrl = *((VpOptionRingControlType *)value);
/* Option does not apply to FXO */
if (pLineObj->status & VP880_IS_FXO) {
status = VP_STATUS_OPTION_NOT_SUPPORTED;
break;
}
if (!(VpCSLACIsSupportedFxsState(pDevCtxLocal->deviceType, TempRingCtrl.ringTripExitSt))) {
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880SetOptionInternal-"));
return VP_STATUS_INVALID_ARG;
}
pLineObj->ringCtrl = *((VpOptionRingControlType *)value);
VpMpiCmdWrapper(deviceId, ecVal, VP880_SS_CONFIG_RD,
VP880_SS_CONFIG_LEN, tempSysConfig);
if (pLineObj->ringCtrl.zeroCross == VP_OPTION_ZC_NONE) {
tempSysConfig[0] |= VP880_ZXR_DIS;
} else {
tempSysConfig[0] &= ~(VP880_ZXR_DIS);
}
VpMpiCmdWrapper(deviceId, ecVal, VP880_SS_CONFIG_WRT,
VP880_SS_CONFIG_LEN, tempSysConfig);
break;
}
#endif
case VP_OPTION_ID_PCM_TXRX_CNTRL: {
uint8 opCondTarget = pLineObj->opCond[0];
pLineObj->pcmTxRxCtrl = *((VpOptionPcmTxRxCntrlType *)value);
opCondTarget &= (uint8)(~(VP880_CUT_TXPATH | VP880_CUT_RXPATH));
opCondTarget &= (uint8)(~(VP880_HIGH_PASS_DIS | VP880_OPCOND_RSVD_MASK));
Vp880GetTxRxPcmMode(pLineObj, pLineObj->lineState.currentState, &mpiByte);
opCondTarget |= mpiByte;
if (opCondTarget != pLineObj->opCond[0]) {
pLineObj->opCond[0] = opCondTarget;
VP_LINE_STATE(VpLineCtxType, pLineCtx,
("6. Writing 0x%02X to Operating Conditions", pLineObj->opCond[0]));
VpMpiCmdWrapper(deviceId, ecVal, VP880_OP_COND_WRT,
VP880_OP_COND_LEN, pLineObj->opCond);
}
}
break;
#ifdef VP_DEBUG
case VP_OPTION_ID_DEBUG_SELECT:
/* Update the debugSelectMask in the Line Object. */
pLineObj->debugSelectMask = *(uint32 *)value;
break;
#endif
case VP_DEVICE_OPTION_ID_PULSE:
case VP_DEVICE_OPTION_ID_PULSE2:
case VP_DEVICE_OPTION_ID_CRITICAL_FLT:
case VP_DEVICE_OPTION_ID_DEVICE_IO:
status = VP_STATUS_INVALID_ARG;
break;
default:
status = VP_STATUS_OPTION_NOT_SUPPORTED;
break;
}
} else {
pDevObj = pDevCtx->pDevObj;
deviceId = pDevObj->deviceId;
maxChan = pDevObj->staticInfo.maxChannels;
ecVal = pDevObj->ecVal;
switch (option) {
#ifdef VP880_FXS_SUPPORT
case VP_DEVICE_OPTION_ID_PULSE:
if (pDevObj->stateInt & VP880_IS_FXO_ONLY) {
status = VP_STATUS_OPTION_NOT_SUPPORTED;
break;
}
pDevObj->pulseSpecs = *((VpOptionPulseType *)value);
break;
case VP_DEVICE_OPTION_ID_PULSE2:
if (pDevObj->stateInt & VP880_IS_FXO_ONLY) {
status = VP_STATUS_OPTION_NOT_SUPPORTED;
break;
}
pDevObj->pulseSpecs2 = *((VpOptionPulseType *)value);
break;
case VP_DEVICE_OPTION_ID_CRITICAL_FLT: {
VpOptionCriticalFltType criticalFault =
*((VpOptionCriticalFltType *)value);
if (pDevObj->stateInt & VP880_IS_FXO_ONLY) {
status = VP_STATUS_OPTION_NOT_SUPPORTED;
break;
}
if ((criticalFault.acFltDiscEn == TRUE)
|| (criticalFault.dcFltDiscEn == TRUE)) {
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880SetOptionInternal-"));
return VP_STATUS_INVALID_ARG;
}
pDevObj->criticalFault = *((VpOptionCriticalFltType *)value);
/*
* NOTE: NEVER enable the Auto-Thermal Fault disconnect in the silicon
* because the silicon is too fast for the VP-API-II. It would be possible
* to get a thermal fault, have the silicon disable the line, and have
* the thermal fault go away all before the VP-API-II sees it. In that
* condition, the line will be disabled without the application being
* aware of it.
*/
}
break;
#endif
case VP_DEVICE_OPTION_ID_DEVICE_IO: {
uint8 ioTypeReq[2] = {0x00, 0x00};
uint8 ecMod[] = {VP880_EC_CH1, VP880_EC_CH2};
uint8 maxPinsPerLine = VP880_MAX_PINS_PER_LINE;
uint8 pcn = pDevObj->staticInfo.rcnPcn[VP880_PCN_LOCATION];
/*
* This 'AND' mask is only used to check if the I/O pins on
* ZSI devices are being configured for open drain. Should be
* set to either 0x1 or 0x3
*/
uint32 andMask = 0x3;
deviceIo = *(VpOptionDeviceIoType *)(value);
if ((pcn == VP880_DEV_PCN_88536) || (pcn == VP880_DEV_PCN_88264)) {
/*
* Direction = '1' for output, Type = '1' for Open.
* So it's ok if the direction is NOT output OR if the
* output type is NOT Open for the pins supported.
*/
if (deviceIo.directionPins_31_0
& deviceIo.outputTypePins_31_0
& andMask) {
return VP_STATUS_INVALID_ARG;
}
/*
* VE8830 Chipset (VP880_DEV_PCN_88536) and 88264 both have
* only 1 I/O pin
*/
maxPinsPerLine = 1;
andMask = 0x1;
}
/*
* Read the current direction pins and create a local array
* that matches what the input is requesting.
*/
for (channelId = 0; channelId < maxChan; channelId++) {
uint8 pinCnt = 0;
VpMpiCmdWrapper(deviceId, (ecVal | ecMod[channelId]),
VP880_IODIR_REG_RD, VP880_IODIR_REG_LEN,
&ioDirection[channelId]);
for (pinCnt = 0; pinCnt < maxPinsPerLine; pinCnt++) {
if (deviceIo.directionPins_31_0 & (1 << (channelId + 2 * pinCnt))) {
if (pinCnt == 0) {
ioTypeReq[channelId] |=
((deviceIo.outputTypePins_31_0 & (1 << (channelId + 2 * pinCnt)))
? VP880_IODIR_IO1_OPEN_DRAIN : VP880_IODIR_IO1_OUTPUT);
} else {
ioTypeReq[channelId] |= (VP880_IODIR_IO2_OUTPUT << (pinCnt - 1));
}
} else {
/*
* This is here for show only. Input is 0, so no
* OR operation is needed.
*/
/* ioTypeReq[channelId] |= VP880_IODIR_IO1_INPUT; */
}
}
/* Protect the I/O lines dedictated to termination types */
pLineCtxLocal = pDevCtx->pLineCtx[channelId];
if (pLineCtxLocal != VP_NULL) {
uint8 fxoMask;
pLineObj = pLineCtxLocal->pLineObj;
switch (pLineObj->termType) {
case VP_TERM_FXO_GENERIC:
case VP_TERM_FXO_DISC:
fxoMask = (VP880_FXO_CID_LINE == VP880_IODATA_IO2)
? VP880_IODIR_IO2_MASK : VP880_IODIR_IO3_MASK;
ioTypeReq[channelId] &= ~fxoMask;
ioTypeReq[channelId] |= (ioDirection[channelId] & fxoMask);
/*
* No break required becuase FXO also has I/O1
* dedicated.
*/
case VP_TERM_FXS_ISOLATE:
case VP_TERM_FXS_ISOLATE_LP:
case VP_TERM_FXS_SPLITTER:
ioTypeReq[channelId] &= ~VP880_IODIR_IO1_MASK;
ioTypeReq[channelId] |= (ioDirection[channelId] & VP880_IODIR_IO1_MASK);
break;
default:
break;
}
}
}
/* Set the current device IO control information */
for (channelId = 0; channelId < maxChan; channelId++) {
VP_LINE_STATE(VpLineCtxType, pLineCtx, ("1. Write IODIR 0x%02X on Channel %d",
ioTypeReq[channelId], channelId));
VpMpiCmdWrapper(deviceId, (ecVal | ecMod[channelId]),
VP880_IODIR_REG_WRT, VP880_IODIR_REG_LEN,
&ioTypeReq[channelId]);
}
}
break;
#ifdef VP_DEBUG
case VP_OPTION_ID_DEBUG_SELECT:
/* Update the debugSelectMask in the Device Object. */
pDevObj->debugSelectMask = *(uint32 *)value;
break;
#endif
default:
status = VP_STATUS_OPTION_NOT_SUPPORTED;
break;
}
}
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("Vp880SetOptionInternal-"));
return status;
}
/**
* Vp880MaskNonSupportedEvents()
* This function masks the events that are not supported by the VP880 API-II.
* It should only be called by SetOptionInternal when event masks are being
* modified.
*
* Preconditions:
* None. Utility function to modify event structures only.
*
* Postconditions:
* Event structures passed are modified with masked bits for non-supported
* VP880 API-II events.
*/
void
Vp880MaskNonSupportedEvents(
VpOptionEventMaskType *pLineEventsMask, /**< Line Events Mask to modify for
* non-masking
*/
VpOptionEventMaskType *pDevEventsMask) /**< Device Events Mask to modify
* for non-masking
*/
{
VP_API_FUNC_INT(None, VP_NULL, ("+Vp880MaskNonSupportedEvents()"));
pLineEventsMask->faults |= VP880_NONSUPPORT_FAULT_EVENTS;
pLineEventsMask->signaling |= VP880_NONSUPPORT_SIGNALING_EVENTS;
pLineEventsMask->response |= VP880_NONSUPPORT_RESPONSE_EVENTS;
pLineEventsMask->test |= VP880_NONSUPPORT_TEST_EVENTS;
pLineEventsMask->process |= VP880_NONSUPPORT_PROCESS_EVENTS;
pLineEventsMask->fxo |= VP880_NONSUPPORT_FXO_EVENTS;
pDevEventsMask->faults |= VP880_NONSUPPORT_FAULT_EVENTS;
pDevEventsMask->signaling |= VP880_NONSUPPORT_SIGNALING_EVENTS;
pDevEventsMask->response |= VP880_NONSUPPORT_RESPONSE_EVENTS;
pDevEventsMask->test |= VP880_NONSUPPORT_TEST_EVENTS;
pDevEventsMask->process |= VP880_NONSUPPORT_PROCESS_EVENTS;
pDevEventsMask->fxo |= VP880_NONSUPPORT_FXO_EVENTS;
VP_API_FUNC_INT(None, VP_NULL, ("-Vp880MaskNonSupportedEvents()"));
return;
}
/**
* Vp880DeviceIoAccess()
* This function is used to access device IO pins of the Vp880. See API-II
* documentation for more information about this function.
*
* Preconditions:
* Device/Line context should be created and initialized. For applicable
* devices bootload should be performed before calling the function.
*
* Postconditions:
* Reads/Writes from device IO pins.
*/
VpStatusType
Vp880DeviceIoAccess(
VpDevCtxType *pDevCtx,
VpDeviceIoAccessDataType *pDeviceIoData)
{
Vp880DeviceObjectType *pDevObj = pDevCtx->pDevObj;
VpLineCtxType *pLineCtx;
Vp880LineObjectType *pLineObj;
bool isDedicatedPins = FALSE;
VpDeviceIdType deviceId = pDevObj->deviceId;
uint8 ecVal;
uint8 chanNum, maxChan;
uint8 ioDataReg[2] = {0x00, 0x00}; /* IO Status from each channel */
/*
* tempIoData and tempIoMask are representations of the device content to be
* written.
*/
uint8 tempIoData[2] = {0x00, 0x00};
uint8 tempIoMask[2] = {0x00, 0x00};
VpDeviceIoAccessDataType *pAccessData =
&(pDevObj->getResultsOption.optionData.deviceIoData);
VP_API_FUNC_INT(VpDevCtxType, pDevCtx, ("+Vp880DeviceIoAccess()"));
/* VE8830 Chip set does not have I/O pins */
if (pDevObj->staticInfo.rcnPcn[1] == VP880_DEV_PCN_88536) {
VP_API_FUNC_INT(VpDevCtxType, pDevCtx, ("-Vp880DeviceIoAccess()"));
return VP_STATUS_FUNC_NOT_SUPPORTED;
}
maxChan = pDevObj->staticInfo.maxChannels;
/* Proceed if device state is either in progress or complete */
if (pDevObj->state & (VP_DEV_INIT_CMP | VP_DEV_INIT_IN_PROGRESS)) {
} else {
VP_API_FUNC_INT(VpDevCtxType, pDevCtx, ("-Vp880DeviceIoAccess()"));
return VP_STATUS_DEV_NOT_INITIALIZED;
}
/*
* Do not proceed if the device calibration is in progress. This could
* damage the device.
*/
if (pDevObj->state & VP_DEV_IN_CAL) {
VP_API_FUNC_INT(VpDevCtxType, pDevCtx, ("-Vp880DeviceIoAccess()"));
return VP_STATUS_DEV_NOT_INITIALIZED;
}
VpSysEnterCritical(deviceId, VP_CODE_CRITICAL_SEC);
for (chanNum = 0; chanNum < maxChan; chanNum++) {
uint16 dataMask;
uint8 loopCnt;
uint16 tempData;
for (loopCnt = 0; loopCnt < 6; loopCnt++) {
dataMask = 0x01;
dataMask = (dataMask << (chanNum + 2 * loopCnt));
tempData = 0;
tempData = (uint16)(pDeviceIoData->accessMask_31_0 & dataMask);
tempIoMask[chanNum] |= (uint8)(tempData >> (chanNum + loopCnt));
tempData = 0;
tempData = (uint16)(pDeviceIoData->deviceIOData_31_0 & dataMask);
tempIoData[chanNum] |= (uint8)(tempData >> (chanNum + loopCnt));
}
}
/* Read the current state of the IO lines */
for (chanNum = 0; chanNum < maxChan; chanNum++) {
pLineCtx = pDevCtx->pLineCtx[chanNum];
if (pLineCtx != VP_NULL) {
pLineObj = pLineCtx->pLineObj;
ecVal = pLineObj->ecVal;
/* Protect the CID line for FXO type */
if ((pLineObj->status & VP880_IS_FXO)
|| (pLineObj->termType == VP_TERM_FXO_DISC)) {
if (tempIoMask[chanNum] & VP880_FXO_CID_LINE) {
VP_ERROR(VpLineCtxType, pLineCtx, ("Dedicated Pin Error"));
isDedicatedPins = TRUE;
}
tempIoMask[chanNum] &= ~VP880_FXO_CID_LINE;
} else { /* Force Data [2:4] to 0 for FXS */
tempIoData[chanNum] &= (VP880_IODATA_IO1 | VP880_IODATA_IO2);
}
/* Protect access to I/O1 if FXO or Relay Type terminations */
if ((pLineObj->status & VP880_IS_FXO)
|| (pLineObj->termType == VP_TERM_FXS_ISOLATE)
|| (pLineObj->termType == VP_TERM_FXS_ISOLATE_LP)
|| (pLineObj->termType == VP_TERM_FXS_SPLITTER)
|| (pLineObj->termType == VP_TERM_FXS_SPLITTER_LP)) {
if (tempIoMask[chanNum] & VP880_IODATA_IO1) {
VP_ERROR(VpLineCtxType, pLineCtx, ("Dedicated Pin Error"));
isDedicatedPins = TRUE;
}
tempIoMask[chanNum] &= ~VP880_IODATA_IO1;
}
} else {
VP_LINE_STATE(None, NULL, ("VpDeviceIoAccess: NULL Line Found on Ch %d",
chanNum));
ecVal = pDevObj->ecVal;
ecVal |= ((chanNum == 0) ? VP880_EC_CH1 : VP880_EC_CH2);
}
/* Read the IO Data, whether a line exists or not */
VpMpiCmdWrapper(deviceId, ecVal, VP880_IODATA_REG_RD,
VP880_IODATA_REG_LEN, &ioDataReg[chanNum]);
}
*pAccessData = *pDeviceIoData;
if (pDeviceIoData->accessType == VP_DEVICE_IO_WRITE) {
for (chanNum = 0; chanNum < maxChan; chanNum++) {
uint8 tempData = ioDataReg[chanNum];
ecVal = pDevObj->ecVal;
ecVal |= ((chanNum == 0) ? VP880_EC_CH1 : VP880_EC_CH2);
tempData &= ~tempIoMask[chanNum];
tempData |= (tempIoMask[chanNum] & tempIoData[chanNum]);
VP_LINE_STATE(None, NULL, ("VpDeviceIoAccess: Write IODATA 0x%02X on Ch %d",
tempData, chanNum));
VpMpiCmdWrapper(deviceId, ecVal, VP880_IODATA_REG_WRT,
VP880_IODATA_REG_LEN, &tempData);
}
} else { /* VP_DEVICE_IO_READ */
isDedicatedPins = FALSE;
pAccessData->deviceIOData_31_0 = 0;
pAccessData->deviceIOData_63_32 = 0;
for (chanNum = 0; chanNum < maxChan; chanNum++) {
uint8 loopCnt;
uint32 tempIoRdData;
uint16 dataMask;
for (loopCnt = 0; loopCnt < 6; loopCnt++) {
dataMask = 0x01;
dataMask = (dataMask << loopCnt);
/* Extract the bit we're after in this loop */
tempIoRdData = ioDataReg[chanNum];
/* This is the location per the device. Move to API location */
tempIoRdData &= dataMask;
tempIoRdData = (tempIoRdData << (chanNum + loopCnt));
/* Mask off ONLY the bit being provided in this loop */
dataMask = 0x01;
dataMask = (dataMask << (chanNum + 2 * loopCnt));
tempIoRdData &= dataMask;
pAccessData->deviceIOData_31_0 |= tempIoRdData;
}
}
pAccessData->deviceIOData_31_0 &= pDeviceIoData->accessMask_31_0;
}
pDevObj->deviceEvents.response |= VP_DEV_EVID_IO_ACCESS_CMP;
VpSysExitCritical(deviceId, VP_CODE_CRITICAL_SEC);
VP_API_FUNC_INT(VpDevCtxType, pDevCtx, ("-Vp880DeviceIoAccess()"));
return ((isDedicatedPins == TRUE) ? VP_STATUS_DEDICATED_PINS : VP_STATUS_SUCCESS);
}
/**
* Vp880SetCodec()
* This function sets the codec mode on the line specified.
*
* Preconditions:
* The line must first be initialized.
*
* Postconditions:
* The codec mode on the line is set. This function returns the success code
* if the codec mode specified is supported.
*/
VpStatusType
Vp880SetCodec(
VpLineCtxType *pLineCtx,
VpOptionCodecType codec) /* Encoding, as defined by LineCodec typedef */
{
Vp880LineObjectType *pLineObj = pLineCtx->pLineObj;
VpDevCtxType *pDevCtx = pLineCtx->pDevCtx;
Vp880DeviceObjectType *pDevObj = pDevCtx->pDevObj;
VpDeviceIdType deviceId = pDevObj->deviceId;
uint8 codecReg;
uint8 ecVal = pLineObj->ecVal;
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("+Vp880SetCodec()"));
/* Basic error checking */
if ((codec != VP_OPTION_LINEAR) && (codec != VP_OPTION_ALAW)
&& (codec != VP_OPTION_MLAW) && (codec != VP_OPTION_WIDEBAND)) {
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("-Vp880SetCodec()"));
return VP_STATUS_INVALID_ARG;
}
if ((codec == VP_OPTION_WIDEBAND)
&& (!(pDevObj->stateInt & VP880_WIDEBAND))) {
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("-Vp880SetCodec()"));
return VP_STATUS_INVALID_ARG;
}
/*
* Don't allow this change during calibration. Cache the target value that
* will be applied when calibration is complete.
*/
if (pLineObj->status & VP880_LINE_IN_CAL) {
pLineObj->calLineData.updateFlags |= CODEC_UPDATE_REQ;
pLineObj->codec = codec;
return VP_STATUS_SUCCESS;
}
/* Adjust the EC value for Wideband mode as needed */
ecVal &= ~VP880_WIDEBAND_MODE;
ecVal |= ((codec == VP_OPTION_WIDEBAND) ? VP880_WIDEBAND_MODE : 0);
/*
* Wideband requires 1/2 rate reduction in device programmed rate to
* maintain the same real sample rate.
*/
if(((pLineObj->codec == VP_OPTION_WIDEBAND) && (codec != VP_OPTION_WIDEBAND))
|| ((pLineObj->codec != VP_OPTION_WIDEBAND) && (codec == VP_OPTION_WIDEBAND))) {
uint8 converterCfg[VP880_CONV_CFG_LEN];
uint8 newValue;
pDevObj->devTimer[VP_DEV_TIMER_WB_MODE_CHANGE] =
MS_TO_TICKRATE(VP_WB_CHANGE_MASK_TIME,
pDevObj->devProfileData.tickRate) | VP_ACTIVATE_TIMER;
VpMpiCmdWrapper(deviceId, ecVal, VP880_CONV_CFG_RD, VP880_CONV_CFG_LEN,
converterCfg);
converterCfg[0] &= ~VP880_CC_RATE_MASK;
/* Adjust the pcm buffer update rate based on the tickrate and CODEC */
if(pDevObj->devProfileData.tickRate <=160) {
newValue = ((codec == VP_OPTION_WIDEBAND) ? VP880_CC_4KHZ_RATE : VP880_CC_8KHZ_RATE);
} else if(pDevObj->devProfileData.tickRate <=320){
newValue = ((codec == VP_OPTION_WIDEBAND) ? VP880_CC_2KHZ_RATE : VP880_CC_4KHZ_RATE);
} else if(pDevObj->devProfileData.tickRate <=640){
newValue = ((codec == VP_OPTION_WIDEBAND) ? VP880_CC_1KHZ_RATE : VP880_CC_2KHZ_RATE);
} else if(pDevObj->devProfileData.tickRate <=1280){
newValue = ((codec == VP_OPTION_WIDEBAND) ? VP880_CC_500HZ_RATE : VP880_CC_1KHZ_RATE);
} else {
newValue = VP880_CC_500HZ_RATE;
}
pDevObj->txBufferDataRate = newValue;
converterCfg[0] |= newValue;
/*
* If channel is going to Wideband mode, we can immediately update the
* device object. But if leaving Wideband mode, we have to let the tick
* manage it because the other line may still be in Wideband mode.
*/
if (codec == VP_OPTION_WIDEBAND) {
pDevObj->ecVal |= VP880_WIDEBAND_MODE;
}
VpMpiCmdWrapper(deviceId, ecVal, VP880_CONV_CFG_WRT, VP880_CONV_CFG_LEN,
converterCfg);
/*
* This value cannot be set to 0. "0" is used to indicate there has been
* no recent changes. So increment all values by 1.
*/
pDevObj->lastCodecChange = pLineObj->channelId+1;
}
/* Read the current state of the codec register */
VpMpiCmdWrapper(deviceId, ecVal, VP880_OP_FUNC_RD, VP880_OP_FUNC_LEN,
&codecReg);
/* Enable the desired CODEC mode */
switch(codec) {
case VP_OPTION_LINEAR: /* 16 bit linear PCM */
case VP_OPTION_WIDEBAND: /* Wideband asumes Linear PCM */
codecReg |= VP880_LINEAR_CODEC;
break;
case VP_OPTION_ALAW: /* A-law PCM */
codecReg &= (uint8)(~(VP880_LINEAR_CODEC | VP880_ULAW_CODEC));
break;
case VP_OPTION_MLAW: /* u-law PCM */
codecReg |= VP880_ULAW_CODEC;
codecReg &= ~(VP880_LINEAR_CODEC);
break;
default:
/* Cannot reach here. Error checking at top */
break;
} /* Switch */
VpMpiCmdWrapper(deviceId, ecVal, VP880_OP_FUNC_WRT, VP880_OP_FUNC_LEN,
&codecReg);
pLineObj->codec = codec;
pLineObj->ecVal = ecVal;
VP_LINE_STATE(VpLineCtxType, pLineCtx, ("Setting CODEC on line %d to %d ecVal 0x%02X",
pLineObj->channelId, pLineObj->codec, pLineObj->ecVal));
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("-Vp880SetCodec()"));
return VP_STATUS_SUCCESS;
} /* Vp880SetCodec() */
/**
* Vp880SetTimeSlot()
* This function set the RX and TX timeslot for a device channel. Valid
* timeslot numbers start at zero. The upper bound is system dependent.
*
* Preconditions:
* The line must first be initialized.
*
* Postconditions:
* The timeslots on the line are set. This function returns the success code
* if the timeslot numbers specified are within the range of the device based on
* the PCLK rate.
*/
VpStatusType
Vp880SetTimeSlot(
VpLineCtxType *pLineCtx,
uint8 txSlot, /**< The TX PCM timeslot */
uint8 rxSlot) /**< The RX PCM timeslot */
{
Vp880LineObjectType *pLineObj = pLineCtx->pLineObj;
VpDevCtxType *pDevCtx = pLineCtx->pDevCtx;
Vp880DeviceObjectType *pDevObj = pDevCtx->pDevObj;
VpDeviceIdType deviceId = pDevObj->deviceId;
uint8 ecVal = pLineObj->ecVal;
uint8 mpiBuffer[2 + VP880_TX_TS_LEN + VP880_RX_TS_LEN];
uint8 mpiIndex = 0;
uint8 pcn = pDevObj->staticInfo.rcnPcn[VP880_PCN_LOCATION];
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("+Vp880SetTimeSlot()"));
/* Validate the tx and rx time slot value */
if ((txSlot >= pDevObj->devProfileData.pcmClkRate/64) ||
(rxSlot >= pDevObj->devProfileData.pcmClkRate/64)) {
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("-Vp880SetTimeSlot()"));
return VP_STATUS_INPUT_PARAM_OOR;
}
if ((pcn == VP880_DEV_PCN_88536) || (pcn == VP880_DEV_PCN_88264)) {
if (txSlot == 0) {
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("-Vp880SetTimeSlot()"));
return VP_STATUS_INVALID_ARG;
}
txSlot--;
}
mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, VP880_TX_TS_WRT,
VP880_TX_TS_LEN, &txSlot);
mpiIndex = VpCSLACBuildMpiBuffer(mpiIndex, mpiBuffer, VP880_RX_TS_WRT,
VP880_RX_TS_LEN, &rxSlot);
VpMpiCmdWrapper(deviceId, ecVal, mpiBuffer[0], mpiIndex-1, &mpiBuffer[1]);
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("-Vp880SetTimeSlot()"));
return VP_STATUS_SUCCESS;
} /* Vp880SetTimeSlot() */
/**
* Vp880VirtualISR()
* This function is called everytime the device causes an interrupt
*
* Preconditions
* A device interrupt has just occured
*
* Postcondition
* This function should be called from the each device's ISR.
* This function could be inlined to improve ISR performance.
*/
#ifndef VP880_SIMPLE_POLLED_MODE
VpStatusType
Vp880VirtualISR(
VpDevCtxType *pDevCtx)
{
Vp880DeviceObjectType *pDevObj = pDevCtx->pDevObj;
VpDeviceIdType deviceId = pDevObj->deviceId;
VP_API_FUNC_INT(VpDevCtxType, pDevCtx, ("+Vp880VirtualISR()"));
#if defined (VP880_INTERRUPT_LEVTRIG_MODE)
VpSysDisableInt(deviceId);
#endif
/* Device Interrupt Received */
VpSysEnterCritical(deviceId, VP_CODE_CRITICAL_SEC);
pDevObj->state |= VP_DEV_PENDING_INT;
VpSysExitCritical(deviceId, VP_CODE_CRITICAL_SEC);
VP_API_FUNC_INT(VpDevCtxType, pDevCtx, ("-Vp880VirtualISR()"));
return VP_STATUS_SUCCESS;
} /* Vp880VirtualISR() */
#endif
/**
* Vp880LowLevelCmd()
* This function provides direct MPI access to the line/device.
*
* Preconditions:
* The device associated with the line, and the line must first be initialized.
*
* Postconditions:
* The command data is passed over the MPI bus and affects only the line passed
* if the command is line specific, and an event is generated. If a read
* command is performed, the user must read the results or flush events. This
* function returns the success code if the device is not already in a state
* where the results must be read.
*/
#if !defined(VP_REDUCED_API_IF) || defined(ZARLINK_CFG_INTERNAL)
VpStatusType
Vp880LowLevelCmd(
VpLineCtxType *pLineCtx,
uint8 *pCmdData,
uint8 len,
uint16 handle)
{
Vp880LineObjectType *pLineObj = pLineCtx->pLineObj;
VpDevCtxType *pDevCtx = pLineCtx->pDevCtx;
Vp880DeviceObjectType *pDevObj = pDevCtx->pDevObj;
VpDeviceIdType deviceId = pDevObj->deviceId;
uint8 ecVal = pLineObj->ecVal;
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("+Vp880LowLevelCmd()"));
if (pDevObj->deviceEvents.response & VP880_READ_RESPONSE_MASK) {
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("-Vp880LowLevelCmd()"));
return VP_STATUS_DEVICE_BUSY;
}
VpSysEnterCritical(deviceId, VP_CODE_CRITICAL_SEC);
if(pCmdData[0] & 0x01) { /* Read Command */
VpMpiCmdWrapper(deviceId, ecVal, pCmdData[0], len, &(pDevObj->mpiData[0]));
pDevObj->mpiLen = len;
pLineObj->lineEvents.response |= VP_LINE_EVID_LLCMD_RX_CMP;
} else {
VpMpiCmdWrapper(deviceId, ecVal, pCmdData[0], len, &pCmdData[1]);
VpMemCpy(pDevObj->mpiData, pCmdData, len);
pLineObj->lineEvents.response |= VP_LINE_EVID_LLCMD_TX_CMP;
}
pLineObj->lineEventHandle = handle;
VpSysExitCritical(deviceId, VP_CODE_CRITICAL_SEC);
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("-Vp880LowLevelCmd()"));
return VP_STATUS_SUCCESS;
} /* Vp880LowLevelCmd() */
#endif
/**
* Vp880UpdateBufferChanSel()
* This function sets the test buffer channel selection so that the hook bits
* in the test buffer are valid for any channel in the active state.
*
* In order for any of the hook bits to be valid, the codec must be activated
* for the channel selected by CBS in the device mode register. This function
* changes CBS so that if the given channel is being activated, that channel
* is selected. If the given channel is being deactivated, the other channel is
* selected.
*
* This function should be called just before activating or deactivating the
* codec for any channel.
*
* Arguments:
* channelId - The channel that is being changed
* sysState - Value of the system state register that is going to be
* programmed. Contains the codec activate/deactivate bit.
*/
void
Vp880UpdateBufferChanSel(
Vp880DeviceObjectType *pDevObj,
uint8 channelId,
uint8 sysState,
bool devWrite)
{
VpDeviceIdType deviceId = pDevObj->deviceId;
uint8 ecVal = ((channelId == 0) ? VP880_EC_CH1 : VP880_EC_CH2);
bool activated;
uint8 preDevMode = pDevObj->devMode[0];
VP_API_FUNC_INT(None, VP_NULL, ("+Vp880UpdateBufferChanSel()"));
if (pDevObj->staticInfo.rcnPcn[VP880_RCN_LOCATION] <= VP880_REV_VC) {
/* No test buffer to worry about in older revs */
VP_API_FUNC_INT(None, VP_NULL, ("-Vp880UpdateBufferChanSel()"));
return;
}
#ifdef VP880_INCLUDE_TESTLINE_CODE
/* Do nothing if either channel is under test */
if (Vp880IsChnlUndrTst(pDevObj, 0) || Vp880IsChnlUndrTst(pDevObj, 1)) {
VP_API_FUNC_INT(None, VP_NULL, ("-Vp880UpdateBufferChanSel()"));
return;
}
#endif
if ((sysState & VP880_SS_ACTIVATE_MASK) == VP880_SS_ACTIVATE_MASK) {
activated = TRUE;
} else {
activated = FALSE;
}
preDevMode &= ~VP880_DEV_MODE_CHAN_MASK;
if (channelId == 0 && activated == TRUE) {
/* If channel 0 was activated, select channel 0 */
preDevMode |= VP880_DEV_MODE_CHAN0_SEL;
} else if (channelId == 0 && activated == FALSE) {
/* If channel 0 was deactivated, select channel 1 */
preDevMode |= VP880_DEV_MODE_CHAN1_SEL;
} else if (channelId == 1 && activated == TRUE) {
/* If channel 1 was activated, select channel 1 */
preDevMode |= VP880_DEV_MODE_CHAN1_SEL;
} else if (channelId == 1 && activated == FALSE) {
/* If channel 1 was deactivated, select channel 0 */
preDevMode |= VP880_DEV_MODE_CHAN0_SEL;
}
if ((devWrite == TRUE) && (preDevMode != pDevObj->devMode[0])) {
pDevObj->devMode[0] = preDevMode;
VpMpiCmdWrapper(deviceId, (ecVal | pDevObj->ecVal), VP880_DEV_MODE_WRT,
VP880_DEV_MODE_LEN, pDevObj->devMode);
}
VP_API_FUNC_INT(None, VP_NULL, ("-Vp880UpdateBufferChanSel()"));
} /* Vp880UpdateBufferChanSel() */
#endif