blob: 3322db6e8fe77c768ea73df92250334f0fce1200 [file] [log] [blame]
/** \file vp880_seq.c
* vp880_seq.c
*
* This file contains the VP880 functions called by the API-II Caller ID or
* sequencer. It is seperated from "normal" API functions for users that want
* to remove this section of code from the API-II.
*
* Copyright (c) 2011, Microsemi
*
* $Revision: 1.1.2.1.8.3 $
* $LastChangedDate: 2011-12-06 19:33:48 -0600 (Tue, 06 Dec 2011) $
*/
#include "../includes/vp_api_cfg.h"
#if defined (VP_CC_880_SERIES)
#ifdef VP_CSLAC_SEQ_EN
/* 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"
/**< Function called by Send Signal only. Implements message waiting pulse. */
#if defined (VP880_FXS_SUPPORT)
static VpStatusType
Vp880SendMsgWaitPulse(
VpLineCtxType *pLineCtx,
VpSendMsgWaitType *pMsgWait);
/**< Function called by Send Signal only. Implements Polarity Reversal Pulse */
VpStatusType
Vp880SendPolRevPulse(
VpLineCtxType *pLineCtx,
uint16 timeIn1Ms);
static VpStatusType
Vp880SendPulse(
VpLineCtxType *pLineCtx,
VpSendSignalType type,
uint16 timeInMs);
#endif
/**< Function called by Send Signal only. Implements Forward Disconnect and
* Tip Open pulse.
*/
#if defined (VP880_FXO_SUPPORT)
/**< Function called by Send Signal only. Implements FXO digit generation */
static VpStatusType
Vp880SendDigit(
VpLineCtxType *pLineCtx,
VpDigitGenerationType digitType,
VpDigitType digit);
/**< Function called by Send Signal only. Implements FXO Momentary Loop Open */
static VpStatusType
Vp880MomentaryLoopOpen(
VpLineCtxType *pLineCtx);
#endif
/**
* Vp880CommandInstruction()
* This function implements the Sequencer Command instruction for the Vp880
* device type.
*
* Preconditions:
* The line must first be initialized and the sequencer data must be valid.
*
* Postconditions:
* The command instruction currently being pointed to by the sequencer
* instruction passed is acted upon. The sequencer may or may not be advanced,
* depending on the specific command instruction being executed.
*/
VpStatusType
Vp880CommandInstruction(
VpLineCtxType *pLineCtx,
VpProfilePtrType pSeqData)
{
Vp880LineObjectType *pLineObj = pLineCtx->pLineObj;
VpDevCtxType *pDevCtx = pLineCtx->pDevCtx;
Vp880DeviceObjectType *pDevObj = pDevCtx->pDevObj;
VpDeviceIdType deviceId = pDevObj->deviceId;
uint8 ecVal = pLineObj->ecVal;
uint8 channelId = pLineObj->channelId;
uint8 sigGenCtrl;
#ifdef VP880_FXS_SUPPORT
uint8 lineState;
uint8 lsConfig[VP880_LOOP_SUP_LEN];
#endif
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("+Vp880CommandInstruction()"));
/*
* We know the current value "pSeqData[0]" is 0, now we need to determine if
* the next command is generator control operator followed by time, or a
* Line state command -- No other options supported
*/
switch (pSeqData[0] & VP_SEQ_SUBTYPE_MASK) {
case VP_SEQ_SUBCMD_SIGGEN:
VP_SEQUENCER(VpLineCtxType, pLineCtx, ("Generator Control 0x%02X 0x%02X",
pSeqData[0], pSeqData[1]));
sigGenCtrl = VP880_GEN_ALLOFF;
/* Get the signal generator bits and set. */
sigGenCtrl |= ((pSeqData[1] & 0x01) ? VP880_GENA_EN : 0);
sigGenCtrl |= ((pSeqData[1] & 0x02) ? VP880_GENB_EN : 0);
sigGenCtrl |= ((pSeqData[1] & 0x04) ? VP880_GENC_EN : 0);
sigGenCtrl |= ((pSeqData[1] & 0x08) ? VP880_GEND_EN : 0);
if (sigGenCtrl != pLineObj->sigGenCtrl[0]) {
pLineObj->sigGenCtrl[0] = sigGenCtrl;
VpMpiCmdWrapper(deviceId, ecVal, VP880_GEN_CTRL_WRT,
VP880_GEN_CTRL_LEN, pLineObj->sigGenCtrl);
}
break;
case VP_SEQ_SUBCMD_LINE_STATE:
VP_SEQUENCER(VpLineCtxType, pLineCtx,
("Line State Control 0x%02X 0x%02X at Time %d",
pSeqData[0], pSeqData[1], pDevObj->timeStamp));
switch(pSeqData[1]) {
#ifdef VP880_FXS_SUPPORT
case VP_PROFILE_CADENCE_STATE_MSG_WAIT_NORM:
case VP_PROFILE_CADENCE_STATE_MSG_WAIT_POLREV:
VpMemCpy(lsConfig, pLineObj->loopSup, VP880_LOOP_SUP_LEN);
if (lsConfig[VP880_LOOP_SUP_RT_MODE_BYTE]
& VP880_RING_TRIP_AC) {
if (!(pLineObj->status & VP880_BAD_LOOP_SUP)) {
pLineObj->status |= VP880_BAD_LOOP_SUP;
}
/* Force DC Trip */
lsConfig[VP880_LOOP_SUP_RT_MODE_BYTE] &=
~VP880_RING_TRIP_AC;
VpMpiCmdWrapper(deviceId, ecVal, VP880_LOOP_SUP_WRT,
VP880_LOOP_SUP_LEN, lsConfig);
}
lineState =
(pSeqData[1] == VP_PROFILE_CADENCE_STATE_MSG_WAIT_NORM) ?
VP880_SS_BALANCED_RINGING :
VP880_SS_BALANCED_RINGING_PR;
Vp880LLSetSysState(deviceId, pLineCtx, lineState, TRUE);
break;
#endif
default:
Vp880SetLineStateInt(pLineCtx,
ConvertPrfWizState2ApiState(pSeqData[1]));
break;
}
break;
#ifdef VP880_FXS_SUPPORT
case VP_SEQ_SUBCMD_START_CID:
case VP_SEQ_SUBCMD_WAIT_ON:
if (pLineObj->pCidProfileType1 != VP_PTABLE_NULL) {
pLineObj->callerId.pCliProfile = pLineObj->pCidProfileType1;
VpCSLACInitCidStruct(&pLineObj->callerId, pSeqData[0]);
}
break;
case VP_SEQ_SUBCMD_RAMP_GENERATORS:
if(VpCSLACProcessRampGenerators(&pLineObj->cadence)) {
#if (VP_CC_DEBUG_SELECT & VP_DBG_SEQUENCER)
uint16 actualLevel = pLineObj->cadence.regData[5];
actualLevel = ((actualLevel << 8) & 0xFF00);
actualLevel |= pLineObj->cadence.regData[6];
VP_SEQUENCER(VpLineCtxType, pLineCtx,
("New Signal Generator Values: 0x%02X 0x%02X 0x%02X 0x%02X time %d Level (%d)",
pLineObj->cadence.regData[3], pLineObj->cadence.regData[4],
pLineObj->cadence.regData[5], pLineObj->cadence.regData[6],
pDevObj->timeStamp, actualLevel));
if (pLineObj->cadence.levelStep == 0) {
VP_SEQUENCER(VpLineCtxType, pLineCtx,
("Ramp complete at time %d", pDevObj->timeStamp));
}
#endif
VpMpiCmdWrapper(deviceId, ecVal, VP880_SIGA_PARAMS_WRT,
VP880_SIGA_PARAMS_LEN, pLineObj->cadence.regData);
/* Clear flag to indicate the generators are NOT in a Ringing Mode */
pLineObj->status &= ~(VP880_RING_GEN_NORM | VP880_RING_GEN_REV);
}
break;
case VP_SEQ_SUBCMD_METERING:
VP_SEQUENCER(VpLineCtxType, pLineCtx, ("Metering Control 0x%02X 0x%02X",
pSeqData[0], pSeqData[1]));
if (pSeqData[1]) { /* Metering On */
pLineObj->slicValueCache |= VP880_SS_METERING_MASK;
pLineObj->cadence.meteringBurst++;
} else { /* Metering Off */
pLineObj->slicValueCache &= ~VP880_SS_METERING_MASK;
if (pLineObj->cadence.meterPendingAbort) {
Vp880SetLineState(pLineCtx, pLineObj->cadence.meterAbortLineState);
}
}
VpMpiCmdWrapper(deviceId, ecVal, VP880_SLIC_STATE_WRT,
VP880_SLIC_STATE_LEN, &pLineObj->slicValueCache);
break;
#endif
default:
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("-Vp880CommandInstruction()"));
return VP_STATUS_INVALID_ARG;
}
/*
* Check to see if there is more sequence data, and if so, move the
* sequence pointer to the next command. Otherwise, end this cadence
*/
pLineObj->cadence.index+=2;
if (pLineObj->cadence.index <
(pLineObj->cadence.length + VP_PROFILE_LENGTH + 1)) {
pSeqData+=2;
pLineObj->cadence.pCurrentPos = pSeqData;
} else {
VP_SEQUENCER(VpLineCtxType, pLineCtx, ("Ending Cadence Length %d at Index %d",
pLineObj->cadence.length, pLineObj->cadence.index));
switch(pLineObj->cadence.pActiveCadence[VP_PROFILE_TYPE_LSB]) {
case VP_PRFWZ_PROFILE_METERING_GEN:
pLineObj->lineEvents.process |= VP_LINE_EVID_MTR_CMP;
break;
case VP_PRFWZ_PROFILE_RINGCAD:
pLineObj->lineEvents.process |= VP_LINE_EVID_RING_CAD;
pLineObj->processData = VP_RING_CAD_DONE;
break;
case VP_PRFWZ_PROFILE_TONECAD:
pLineObj->lineEvents.process |= VP_LINE_EVID_TONE_CAD;
break;
case VP_PRFWZ_PROFILE_HOOK_FLASH_DIG_GEN:
pLineObj->lineEvents.process |= VP_LINE_EVID_SIGNAL_CMP;
pLineObj->processData = VP_SENDSIG_HOOK_FLASH;
break;
case VP_PRFWZ_PROFILE_DIAL_PULSE_DIG_GEN:
pLineObj->lineEvents.process |= VP_LINE_EVID_SIGNAL_CMP;
pLineObj->processData = VP_SENDSIG_PULSE_DIGIT;
break;
case VP_PRFWZ_PROFILE_MOMENTARY_LOOP_OPEN_INT:
pLineObj->lineEvents.process |= VP_LINE_EVID_SIGNAL_CMP;
pLineObj->processData = VP_SENDSIG_MOMENTARY_LOOP_OPEN;
if (pDevObj->intReg[channelId] & VP880_LIU1_MASK) {
pLineObj->lineEventHandle = 1;
} else {
pLineObj->lineEventHandle = 0;
}
VpMpiCmdWrapper(deviceId, ecVal, VP880_LOOP_SUP_WRT,
VP880_LOOP_SUP_LEN, pLineObj->loopSup);
break;
case VP_PRFWZ_PROFILE_DTMF_DIG_GEN:
pLineObj->lineEvents.process |= VP_LINE_EVID_SIGNAL_CMP;
pLineObj->processData = VP_SENDSIG_DTMF_DIGIT;
Vp880MuteChannel(pLineCtx, FALSE);
break;
case VP_PRFWZ_PROFILE_MSG_WAIT_PULSE_INT:
pLineObj->lineEvents.process |= VP_LINE_EVID_SIGNAL_CMP;
pLineObj->processData = VP_SENDSIG_MSG_WAIT_PULSE;
VpSetLineState(pLineCtx, pLineObj->lineState.usrCurrent);
break;
default:
break;
}
pLineObj->cadence.status = VP_CADENCE_RESET_VALUE;
pLineObj->cadence.pActiveCadence = VP_PTABLE_NULL;
}
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("-Vp880CommandInstruction()"));
return VP_STATUS_SUCCESS;
}
#ifdef VP880_FXS_SUPPORT
/**
* Vp880InitRing()
* This function is used to initialize the ringing profile and caller ID
* cadence on a given line.
*
* Preconditions:
* The device associated with this line must be initialized.
*
* Postconditions:
* The line pointed to by the line context passed is initialized with the
* ringing and caller ID profile specified. The profiles may be specified as
* either an index into the devic profile table or by profile pointers. This
* function returns the success code if the device has been initialized and both
* indexes (if indexes are passed) are within the range of the device profile
* table.
*/
VpStatusType
Vp880InitRing(
VpLineCtxType *pLineCtx, /**< Line Context to modify Ringing
* Parameters for
*/
VpProfilePtrType pCadProfile, /**< Pointer of a Ringing Cadence profile,
* or the index into the Ringing Cadence
* profile table.
*/
VpProfilePtrType pCidProfile) /**< Pointer of a Caller ID profile, or the
* index into the Caller ID profile table.
*/
{
VpDevCtxType *pDevCtx = pLineCtx->pDevCtx;
Vp880LineObjectType *pLineObj = pLineCtx->pLineObj;
Vp880DeviceObjectType *pDevObj = pDevCtx->pDevObj;
VpDeviceIdType deviceId = pDevObj->deviceId;
VP_API_FUNC(VpLineCtxType, pLineCtx, ("+Vp880InitRing()"));
/* 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(VpLineCtxType, pLineCtx, ("-Vp880InitRing()"));
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(VpLineCtxType, pLineCtx, ("-Vp880InitRing()"));
return VP_STATUS_DEV_NOT_INITIALIZED;
}
if (pLineObj->status & VP880_IS_FXO) {
VP_API_FUNC(VpLineCtxType, pLineCtx, ("-Vp880InitRing()"));
return VP_STATUS_INVALID_ARG;
}
VpSysEnterCritical(deviceId, VP_CODE_CRITICAL_SEC);
/* Check the legality of the Ring CAD profile */
if (!VpCSLACIsProfileValid(VP_PROFILE_RINGCAD,
VP_CSLAC_RING_CADENCE_PROF_TABLE_SIZE,
pDevObj->profEntry.ringCadProfEntry,
pDevObj->devProfileTable.pRingingCadProfileTable,
pCadProfile, &pLineObj->pRingingCadence))
{
VpSysExitCritical(deviceId, VP_CODE_CRITICAL_SEC);
VP_API_FUNC(VpLineCtxType, pLineCtx, ("-Vp880InitRing()"));
return VP_STATUS_ERR_PROFILE;
}
/* Check the legality of the Ring CID profile */
if (!VpCSLACIsProfileValid(VP_PROFILE_CID,
VP_CSLAC_CALLERID_PROF_TABLE_SIZE,
pDevObj->profEntry.cidCadProfEntry,
pDevObj->devProfileTable.pCallerIdProfileTable,
pCidProfile, &pLineObj->pCidProfileType1))
{
VpSysExitCritical(deviceId, VP_CODE_CRITICAL_SEC);
VP_API_FUNC(VpLineCtxType, pLineCtx, ("-Vp880InitRing()"));
return VP_STATUS_ERR_PROFILE;
}
VpSysExitCritical(deviceId, VP_CODE_CRITICAL_SEC);
VP_API_FUNC(VpLineCtxType, pLineCtx, ("-Vp880InitRing()"));
return VP_STATUS_SUCCESS;
} /* Vp880InitRing */
/**
* Vp880InitCid()
* This function is used to send caller ID information. It accepts an amount of
* CID message data up to a "full" buffer amount (2 times the amount of the
* size used for ContinueCID). It low fills the primary buffer such that the
* application is interrupted at the earliest time when the API is ready to
* accept more data.
*
* Preconditions:
* The device and line context must be created and initialized before calling
* this function. This function needs to be called before placing the line in to
* ringing state.
*
* Postconditions:
* This function transmits the given CID information on the line (when the line
* is placed in the ringing state).
*/
VpStatusType
Vp880InitCid(
VpLineCtxType *pLineCtx,
uint8 length,
uint8p pCidData)
{
VpDevCtxType *pDevCtx = pLineCtx->pDevCtx;
Vp880LineObjectType *pLineObj = pLineCtx->pLineObj;
Vp880DeviceObjectType *pDevObj = pDevCtx->pDevObj;
VpDeviceIdType deviceId = pDevObj->deviceId;
uint8 primaryByteCount, secondaryByteCount;
VP_API_FUNC(VpLineCtxType, pLineCtx, ("+Vp880InitCid()"));
/* 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(VpLineCtxType, pLineCtx, ("-Vp880InitCid()"));
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(VpLineCtxType, pLineCtx, ("-Vp880InitCid()"));
return VP_STATUS_DEV_NOT_INITIALIZED;
}
if ((pLineObj->status & VP880_IS_FXO) || (length > (2 * VP_SIZEOF_CID_MSG_BUFFER))) {
VP_API_FUNC(VpLineCtxType, pLineCtx, ("-Vp880InitCid()"));
return VP_STATUS_INVALID_ARG;
}
if (length == 0) {
VP_API_FUNC(VpLineCtxType, pLineCtx, ("-Vp880InitCid()"));
return VP_STATUS_SUCCESS;
}
VpSysEnterCritical(deviceId, VP_CODE_CRITICAL_SEC);
pLineObj->callerId.cliIndex = 0;
pLineObj->callerId.cliMPIndex = 0;
pLineObj->callerId.cliMSIndex = 0;
/* Stop CID if it was in progress */
pLineObj->callerId.cliTimer = 0;
pLineObj->callerId.status = VP_CID_RESET_VALUE;
pLineObj->suspendCid = FALSE;
pLineObj->callerId.dtmfStatus = VP_CID_DTMF_RESET_VALUE;
pLineObj->callerId.cidCheckSum = 0;
pLineObj->callerId.messageDataRemain = length;
/*
* If length is within the size of just the primary buffer size, then only
* fill the primary buffer. Otherwise (the length exceeds the size of the
* primary buffer size) "low fill" the primary buffer and max fill the
* secondary buffer. This has the affect of causing a CID Data event
* quickly and giving the application a maximum amount of time to refill
* the message buffer
*/
if (length <= VP_SIZEOF_CID_MSG_BUFFER) {
pLineObj->callerId.primaryMsgLen = length;
pLineObj->callerId.secondaryMsgLen = 0;
} else {
pLineObj->callerId.primaryMsgLen = (length - VP_SIZEOF_CID_MSG_BUFFER);
pLineObj->callerId.secondaryMsgLen = VP_SIZEOF_CID_MSG_BUFFER;
}
/*
* Copy the message data to the primary API buffer. If we're here, there's
* at least one byte of primary message data. So a check is not necessary
*/
pLineObj->callerId.status |= VP_CID_PRIMARY_FULL;
for (primaryByteCount = 0;
(primaryByteCount < pLineObj->callerId.primaryMsgLen);
primaryByteCount++) {
pLineObj->callerId.primaryBuffer[primaryByteCount]
= pCidData[primaryByteCount];
pLineObj->callerId.cidCheckSum += pCidData[primaryByteCount];
pLineObj->callerId.cidCheckSum = pLineObj->callerId.cidCheckSum % 256;
}
/* Copy the message data to the secondary API buffer if there is any */
if (pLineObj->callerId.secondaryMsgLen > 0) {
pLineObj->callerId.status |= VP_CID_SECONDARY_FULL;
for (secondaryByteCount = 0;
(secondaryByteCount < pLineObj->callerId.secondaryMsgLen);
secondaryByteCount++) {
pLineObj->callerId.secondaryBuffer[secondaryByteCount] =
pCidData[secondaryByteCount + primaryByteCount];
pLineObj->callerId.cidCheckSum +=
pCidData[secondaryByteCount + primaryByteCount];
pLineObj->callerId.cidCheckSum =
pLineObj->callerId.cidCheckSum % 256;
}
}
VpSysExitCritical(deviceId, VP_CODE_CRITICAL_SEC);
VP_API_FUNC(VpLineCtxType, pLineCtx, ("-Vp880InitCid()"));
return VP_STATUS_SUCCESS;
}
/**
* Vp880SendCid()
* This function may be used to send Caller ID information on-demand. It
* accepts an amount of CID message data up to a "full" buffer amount (2 times
* the amount of the size used for ContinueCID). It low fills the primary buffer
* such that the application is interrupted at the earliest time when the API
* is ready to accept more data.
*
* Preconditions:
* Device/Line context should be created and initialized. The length of the
* message (indicated by the length field passed) must not exceed the buffer
* size.
*
* Postconditions:
* Caller ID information is transmitted on the line.
*/
VpStatusType
Vp880SendCid(
VpLineCtxType *pLineCtx, /**< Line to send CID on */
uint8 length, /**< Length of the current message data, not
* to exceed the buffer size
*/
VpProfilePtrType pCidProfile, /**< CID Profile or Profile index to use */
uint8p pCidData) /**< CID Message data */
{
VpDevCtxType *pDevCtx = pLineCtx->pDevCtx;
Vp880LineObjectType *pLineObj = pLineCtx->pLineObj;
Vp880DeviceObjectType *pDevObj = pDevCtx->pDevObj;
VpProfilePtrType pCidProfileLocal;
uint8 primaryByteCount, secondaryByteCount;
VpDeviceIdType deviceId = pDevObj->deviceId;
VP_API_FUNC(VpLineCtxType, pLineCtx, ("+Vp880SendCid()"));
/* Proceed only if line has been initialized */
if (!(pLineObj->status & VP880_INIT_COMPLETE)) {
VP_API_FUNC(VpLineCtxType, pLineCtx, ("-Vp880SendCid()"));
return VP_STATUS_LINE_NOT_CONFIG;
}
/*
* Do not proceed if the device calibration is in progress. This could
* damage the device.
*/
if (pDevObj->state & VP_DEV_IN_CAL) {
return VP_STATUS_DEV_NOT_INITIALIZED;
}
if ((pLineObj->status & VP880_IS_FXO) || (length > (2 * VP_SIZEOF_CID_MSG_BUFFER))) {
VP_API_FUNC(VpLineCtxType, pLineCtx, ("-Vp880SendCid()"));
return VP_STATUS_INVALID_ARG;
}
if (length == 0) {
VP_API_FUNC(VpLineCtxType, pLineCtx, ("-Vp880SendCid()"));
return VP_STATUS_SUCCESS;
}
/* Check the legality of the CID profile */
if (!VpCSLACIsProfileValid(VP_PROFILE_CID,
VP_CSLAC_CALLERID_PROF_TABLE_SIZE,
pDevObj->profEntry.cidCadProfEntry,
pDevObj->devProfileTable.pCallerIdProfileTable,
pCidProfile, &pCidProfileLocal))
{
VP_API_FUNC(VpLineCtxType, pLineCtx, ("-Vp880SendCid()"));
return VP_STATUS_ERR_PROFILE;
}
if (pCidProfileLocal == VP_PTABLE_NULL) {
VP_API_FUNC(VpLineCtxType, pLineCtx, ("-Vp880SendCid()"));
return VP_STATUS_ERR_PROFILE;
}
/* If we're here, all parameters passed are valid */
VpSysEnterCritical(deviceId, VP_CODE_CRITICAL_SEC);
VpCSLACInitCidStruct(&pLineObj->callerId, 0);
pLineObj->callerId.pCliProfile = pCidProfileLocal;
pLineObj->callerId.status &= ~VP_CID_REPEAT_MSG;
pLineObj->callerId.status &= ~VP_CID_END_OF_MSG;
pLineObj->callerId.cidCheckSum = 0;
pLineObj->callerId.messageDataRemain = length;
/*
* If length is within the size of just the primary buffer size, then only
* fill the primary buffer. Otherwise (the length exceeds the size of the
* primary buffer size) "low fill" the primary buffer and max fill the
* secondary buffer. This has the affect of causing a CID Data event
* quickly and giving the application a maximum amount of time to refill
* the message buffer
*/
if (length <= VP_SIZEOF_CID_MSG_BUFFER) {
pLineObj->callerId.primaryMsgLen = length;
pLineObj->callerId.secondaryMsgLen = 0;
} else {
pLineObj->callerId.primaryMsgLen = (length - VP_SIZEOF_CID_MSG_BUFFER);
pLineObj->callerId.secondaryMsgLen = VP_SIZEOF_CID_MSG_BUFFER;
}
/*
* Copy the message data to the primary API buffer. If we're here, there's
* at least one byte of primary message data. So a check is not necessary
*/
pLineObj->callerId.status |= VP_CID_PRIMARY_FULL;
for (primaryByteCount = 0;
(primaryByteCount < pLineObj->callerId.primaryMsgLen);
primaryByteCount++) {
pLineObj->callerId.primaryBuffer[primaryByteCount]
= pCidData[primaryByteCount];
pLineObj->callerId.cidCheckSum += pCidData[primaryByteCount];
pLineObj->callerId.cidCheckSum = pLineObj->callerId.cidCheckSum % 256;
}
/* Copy the message data to the secondary API buffer if there is any */
if (pLineObj->callerId.secondaryMsgLen > 0) {
pLineObj->callerId.status |= VP_CID_SECONDARY_FULL;
for (secondaryByteCount = 0;
(secondaryByteCount < pLineObj->callerId.secondaryMsgLen);
secondaryByteCount++) {
pLineObj->callerId.secondaryBuffer[secondaryByteCount] =
pCidData[secondaryByteCount + primaryByteCount];
pLineObj->callerId.cidCheckSum +=
pCidData[secondaryByteCount + primaryByteCount];
pLineObj->callerId.cidCheckSum =
pLineObj->callerId.cidCheckSum % 256;
}
}
VpSysExitCritical(deviceId, VP_CODE_CRITICAL_SEC);
VP_API_FUNC(VpLineCtxType, pLineCtx, ("-Vp880SendCid()"));
return VP_STATUS_SUCCESS;
}
/**
* Vp880ContinueCid()
* This function is called to provide more caller ID data (in response to
* Caller ID data event from the VP-API). See VP-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:
* Continues to transmit Caller ID information on the line.
*/
VpStatusType
Vp880ContinueCid(
VpLineCtxType *pLineCtx, /**< Line to continue CID on */
uint8 length, /**< Length of data passed not to exceed the
* buffer length
*/
uint8p pCidData) /**< CID message data */
{
VpDevCtxType *pDevCtx = pLineCtx->pDevCtx;
Vp880LineObjectType *pLineObj = pLineCtx->pLineObj;
Vp880DeviceObjectType *pDevObj = pDevCtx->pDevObj;
uint8 byteCount = 0;
uint8 *pMsgLen;
uint8 *pBuffer;
VpDeviceIdType deviceId = pDevObj->deviceId;
pLineObj->callerId.status &= ~VP_CID_END_OF_MSG;
VP_API_FUNC(VpLineCtxType, pLineCtx, ("+Vp880ContinueCid()"));
/* Proceed only if line has been initialized */
if (!(pLineObj->status & VP880_INIT_COMPLETE)) {
VP_API_FUNC(VpLineCtxType, pLineCtx, ("-Vp880ContinueCid()"));
return VP_STATUS_LINE_NOT_CONFIG;
}
/*
* 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(VpLineCtxType, pLineCtx, ("-Vp880ContinueCid()"));
return VP_STATUS_DEV_NOT_INITIALIZED;
}
if ((pLineObj->status & VP880_IS_FXO) || (length > VP_SIZEOF_CID_MSG_BUFFER)) {
VP_API_FUNC(VpLineCtxType, pLineCtx, ("-Vp880ContinueCid()"));
return VP_STATUS_INVALID_ARG;
}
if (length == 0) {
VP_API_FUNC(VpLineCtxType, pLineCtx, ("-Vp880ContinueCid()"));
return VP_STATUS_SUCCESS;
}
/*
* When this function is called, the buffer that is in use is flagged
* by the VpCliGetEncodeByte() function in vp_api_common.c file. That
* function implements the logic of when to switch between the primary
* and secondary buffer. This function just needs to fill the bufffer that
* is not currently in use, starting with the primary (because the primary
* buffer is also used first for the first part of the message).
*/
VpSysEnterCritical(deviceId, VP_CODE_CRITICAL_SEC);
if (!(pLineObj->callerId.status & VP_CID_PRIMARY_IN_USE)) {
/* Fill the primary buffer */
pLineObj->callerId.status |= VP_CID_PRIMARY_FULL;
pMsgLen = &(pLineObj->callerId.primaryMsgLen);
pBuffer = &(pLineObj->callerId.primaryBuffer[0]);
} else {
/* Fill the secondary buffer */
pLineObj->callerId.status |= VP_CID_SECONDARY_FULL;
pMsgLen = &(pLineObj->callerId.secondaryMsgLen);
pBuffer = &(pLineObj->callerId.secondaryBuffer[0]);
}
*pMsgLen = length;
pLineObj->callerId.messageDataRemain += length;
/* Copy the message data to the API buffer */
for (byteCount = 0; (byteCount < *pMsgLen); byteCount++) {
pBuffer[byteCount] = pCidData[byteCount];
pLineObj->callerId.cidCheckSum += pBuffer[byteCount];
pLineObj->callerId.cidCheckSum = pLineObj->callerId.cidCheckSum % 256;
}
VpSysExitCritical(deviceId, VP_CODE_CRITICAL_SEC);
VP_API_FUNC(VpLineCtxType, pLineCtx, ("-Vp880ContinueCid()"));
return VP_STATUS_SUCCESS;
} /* Vp880ContinueCid() */
/**
* Vp880CtrlSetCliTone()
* This function is called by the API internally to enable or disable the
* signal generator used for Caller ID.
*
* Preconditions:
* The line context must be valid (pointing to a Vp880 line object type
*
* Postconditions:
* The signal generator used for CID tones is enabled/disabled indicated by
* the mode parameter passed.
*/
VpStatusType
Vp880CtrlSetCliTone(
VpLineCtxType *pLineCtx, /**< Line affected by the CLI tones */
bool mode) /**< TRUE = enabled, FALSE = disable tones */
{
Vp880LineObjectType *pLineObj = pLineCtx->pLineObj;
VpDevCtxType *pDevCtx = pLineCtx->pDevCtx;
Vp880DeviceObjectType *pDevObj = pDevCtx->pDevObj;
VpDeviceIdType deviceId = pDevObj->deviceId;
uint8 ecVal = pLineObj->ecVal;
/* Pre-clear everything in the Signal Generator. Always set for Continuous */
uint8 sigGenCtrl = 0;
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("+Vp880CtrlSetCliTone()"));
/*
* This function should only be called when the Caller ID sequence is
* generating an alerting tone. We're using the C/D generators, so disable
* A/B and enable C/D only (if mode == TRUE).
*/
if (mode == TRUE) {
sigGenCtrl |= (VP880_GENC_EN | VP880_GEND_EN);
}
if (sigGenCtrl != pLineObj->sigGenCtrl[0]) {
pLineObj->sigGenCtrl[0] = sigGenCtrl;
VP_CID(VpLineCtxType, pLineCtx, ("Writing 0x%02X to SignGen Ctrl", sigGenCtrl));
VpMpiCmdWrapper(deviceId, ecVal, VP880_GEN_CTRL_WRT, VP880_GEN_CTRL_LEN,
pLineObj->sigGenCtrl);
}
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("-Vp880CtrlSetCliTone()"));
return VP_STATUS_SUCCESS;
}
/**
* Vp880CtrlSetFSKGen()
* This function is called by the CID sequencer executed internally by the API
*
* Preconditions:
* The line context must be valid (pointing to a VP880 line object type
*
* Postconditions:
* The data indicated by mode and data is applied to the line. Mode is used
* to indicate whether the data is "message", or a special character. The
* special characters are "channel siezure" (alt. 1/0), "mark" (all 1), or
* "end of transmission".
*/
bool
Vp880CtrlSetFSKGen(
VpLineCtxType *pLineCtx, /**< Line affected by the mode and data */
VpCidGeneratorControlType mode, /**< Indicates the type of data being sent.
* Affects the start and stop bit used
*/
uint8 data) /**< 8-bit message data */
{
Vp880LineObjectType *pLineObj = pLineCtx->pLineObj;
VpDevCtxType *pDevCtx = pLineCtx->pDevCtx;
Vp880DeviceObjectType *pDevObj = pDevCtx->pDevObj;
VpDeviceIdType deviceId = pDevObj->deviceId;
uint8 ecVal = pLineObj->ecVal;
bool moreData = TRUE;
bool initFsk = FALSE;
uint8 fskParam[VP880_CID_PARAM_LEN];
bool returnStatus = FALSE;
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("+Vp880CtrlSetFSKGen()"));
fskParam[0] = pLineObj->tickBeginState[0];
fskParam[0] &= ~(VP880_CID_FRAME_BITS);
if (fskParam[0] & VP880_CID_DIS) {
initFsk = TRUE;
}
switch(mode) {
case VP_CID_SIGGEN_EOT:
VP_CID(VpLineCtxType, pLineCtx,
("Vp880CtrlSetFSKGen(): VP_CID_SIGGEN_EOT %d at time %d with tickBegin 0x%02X",
data, pDevObj->timeStamp, pLineObj->tickBeginState[0]));
/*
* If CID State is in IDLE, can't write more CID Data. Otherwise, the line will
* show 1 more data byte. Check this and prevent CID Data Write
*/
if ((fskParam[0] & VP880_CID_STATE_MASK) == VP880_CID_STATE_IDLE) {
/* Prevent writing the CID Data Register. */
pLineObj->cidBytesRemain = 0;
data = 0;
initFsk = FALSE;
} else {
pLineObj->cidBytesRemain = 1; /* Force count to 0 after wrting the current byte */
}
if (data == 0) {
VP_CID(VpLineCtxType, pLineCtx,
("Vp880CtrlSetFSKGen(): Disabling FSK at time %d", pDevObj->timeStamp));
/* Stop Transmission Immediately */
Vp880MuteChannel(pLineCtx, FALSE);
fskParam[0] |= VP880_CID_DIS;
moreData = FALSE;
} else {
if ((pLineObj->suspendCid == FALSE)
&& (!(pLineObj->callerId.status & VP_CID_TERM_FSK))) {
/* Wait until the device is complete */
VP_CID(VpLineCtxType, pLineCtx,
("Vp880CtrlSetFSKGen(): Delaying FSK termination at time %d",
pDevObj->timeStamp));
pLineObj->suspendCid = TRUE;
/*
* Make sure the last byte is defined to be a "safe" mark
* signal and tell the silicon this is the last byte.
*/
fskParam[0] &= ~VP880_CID_DIS;
fskParam[0] |=
(VP880_CID_FB_START_1 | VP880_CID_FB_STOP_1 | VP880_CID_EOM);
data = 0xFF;
} else {
VP_CID(VpLineCtxType, pLineCtx,
("Vp880CtrlSetFSKGen(): FSK Termination already in progress at time %d",
pDevObj->timeStamp));
return returnStatus;
}
}
break;
case VP_CID_GENERATOR_DATA:
VP_CID(VpLineCtxType, pLineCtx,
("Vp880CtrlSetFSKGen(): VP_CID_GENERATOR_DATA 0x%02X at time %d with prior CID State: 0x%02X",
data, pDevObj->timeStamp, pLineObj->tickBeginState[0]));
pLineObj->callerId.status |= VP_CID_FSK_ACTIVE;
Vp880MuteChannel(pLineCtx, TRUE);
fskParam[0] |= (VP880_CID_FB_START_0 | VP880_CID_FB_STOP_1);
fskParam[0] &= ~(VP880_CID_DIS);
if ((pLineObj->callerId.status & VP_CID_END_OF_MSG)
&& (!(pLineObj->callerId.status & VP_CID_TERM_FSK))) {
VP_CID(VpLineCtxType, pLineCtx,
("Vp880CtrlSetFSKGen(): Sending EOM Signal to Silicon at time %d",
pDevObj->timeStamp));
fskParam[0] |= VP880_CID_EOM;
} else {
fskParam[0] &= ~(VP880_CID_EOM);
}
break;
case VP_CID_GENERATOR_KEYED_CHAR:
VP_CID(VpLineCtxType, pLineCtx,
("Vp880CtrlSetFSKGen(): VP_CID_GENERATOR_KEYED_CHAR 0x%02X at time %d with prior CID State: 0x%02X",
data, pDevObj->timeStamp, pLineObj->tickBeginState[0]));
pLineObj->callerId.status |= VP_CID_FSK_ACTIVE;
Vp880MuteChannel(pLineCtx, TRUE);
fskParam[0] &= (uint8)(~(VP880_CID_EOM | VP880_CID_DIS));
switch(data) {
case VP_FSK_CHAN_SEIZURE:
fskParam[0] |= (VP880_CID_FB_START_0 | VP880_CID_FB_STOP_1);
break;
case VP_FSK_MARK_SIGNAL:
fskParam[0] |= (VP880_CID_FB_START_1 | VP880_CID_FB_STOP_1);
if (pLineObj->callerId.markOutByteCount > 0) {
if (pLineObj->callerId.markOutByteRemain > 0) {
pLineObj->callerId.markOutByteRemain--;
VP_CID(VpLineCtxType, pLineCtx,
("Vp880CtrlSetFSKGen(): Set markOutByteRemain to (%d) on ch (%d) at time %d",
pLineObj->callerId.markOutByteRemain,
pLineObj->channelId, pDevObj->timeStamp));
/*
* If we just decremented markOutByteRemain AND it is now == 0, this is
* the end of the FSK + Mark-Out Byte Message Data.
*/
if (pLineObj->callerId.markOutByteRemain == 0) {
pLineObj->callerId.status |= VP_CID_END_OF_MSG;
}
}
if ((pLineObj->callerId.status & VP_CID_END_OF_MSG) &&
(!(pLineObj->callerId.status & VP_CID_TERM_FSK))) {
VP_CID(VpLineCtxType, pLineCtx,
("Vp880CtrlSetFSKGen(): Last Mark-Out - Sending EOM Signal to Silicon at time %d",
pDevObj->timeStamp));
fskParam[0] |= VP880_CID_EOM;
}
}
break;
default:
break;
}
break;
default:
break;
}
/*
* If the FSK generator was previously deactived (or never active) AND we're
* now trying to disable it, this is redundant and we can immediately return
*/
if (((pLineObj->callerId.status & VP_CID_FSK_ACTIVE) == 0)
&& (fskParam[0] & VP880_CID_DIS)) {
VP_CID(VpLineCtxType, pLineCtx,
("Vp880CtrlSetFSKGen(): Redundant Disable Operation at time %d - Skipping Write",
pDevObj->timeStamp));
pLineObj->tickBeginState[0] = fskParam[0];
return returnStatus;
}
if (fskParam[0] & VP880_CID_DIS) {
pLineObj->callerId.status &= ~VP_CID_FSK_ACTIVE;
}
if ((pLineObj->tickBeginState[0] & VP880_CID_EOM) == VP880_CID_EOM) {
VP_CID(VpLineCtxType, pLineCtx,
("Vp880CtrlSetFSKGen(): Identified EOM at time %d", pDevObj->timeStamp));
return FALSE;
}
if (fskParam[0] != pLineObj->tickBeginState[0]) {
pLineObj->tickBeginState[0] = fskParam[0];
VP_CID(VpLineCtxType, pLineCtx, ("Vp880CtrlSetFSKGen(): Writing 0x%02X to CID Params",
fskParam[0]));
VpMpiCmdWrapper(deviceId, ecVal, VP880_CID_PARAM_WRT, VP880_CID_PARAM_LEN,
fskParam);
}
if (moreData == TRUE) {
VP_CID(VpLineCtxType, pLineCtx,
("Vp880CtrlSetFSKGen(): Writing 0x%02X to CID Data at Time %d",
data, pDevObj->timeStamp));
pLineObj->cidBytesRemain--;
VpMpiCmdWrapper(deviceId, ecVal, VP880_CID_DATA_WRT, VP880_CID_DATA_LEN,
&data);
if (initFsk == TRUE) {
/*
* Special Init case. The State machine updates in ~18us, so we
* can't allow another byte write until we observe a change. It
* will change to Empty, so check for that state as successfull
* initialization.
*/
uint8 safeFail = VP_CID_NORMAL_INIT_CNT;
uint8 stateToExit;
if (pDevObj->staticInfo.rcnPcn[VP880_RCN_LOCATION] <= VP880_REV_VC) {
stateToExit = VP880_CID_STATE_RDY;
} else {
stateToExit = VP880_CID_STATE_EMPTY_D;
}
while (safeFail > 0) {
VpMpiCmdWrapper(deviceId, ecVal, VP880_CID_PARAM_RD, VP880_CID_PARAM_LEN,
pLineObj->tickBeginState);
if ((pLineObj->tickBeginState[0] & VP880_CID_STATE_MASK) == stateToExit) {
VP_CID(VpLineCtxType, pLineCtx,
("Vp880CtrlSetFSKGen(): State = 0x%02X at Count %d Time %d - Return Now",
stateToExit, safeFail, pDevObj->timeStamp));
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("-Vp880CtrlSetFSKGen()"));
return ((pLineObj->cidBytesRemain == 0) ? FALSE : TRUE);
} else {
VP_CID(VpLineCtxType, pLineCtx,
("Vp880CtrlSetFSKGen(): Not Empty (state 0x%02X) on Count %d Time %d",
(pLineObj->tickBeginState[0] & VP880_CID_STATE_MASK), safeFail,
pDevObj->timeStamp));
}
safeFail--;
}
/* If we're here it's because the initialization check failed. */
VP_CID(VpLineCtxType, pLineCtx,
("Vp880CtrlSetFSKGen(): Did not find Good Transition During Init Time %d",
pDevObj->timeStamp));
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("-Vp880CtrlSetFSKGen()"));
return FALSE;
} else {
#ifdef VP_CID_POLLING_ENABLED
if ((pDevObj->staticInfo.rcnPcn[VP880_RCN_LOCATION] > VP880_REV_VC) &&
(mode != VP_CID_SIGGEN_EOT)) {
uint8 tickEndState[VP880_CID_PARAM_LEN];
uint8 retryCount;
/*
* If the delay has been consumed prior to this point, means we already detected a
* state transition this tick. We should revert to using the number of bytes remain
* to determine whether we will return TRUE or FALSE.
*/
if (pLineObj->delayConsumed) {
return ((pLineObj->cidBytesRemain == 0) ? FALSE : TRUE);
}
/*
* If we're here, means we have not seen a state change this tick. Look for one.
* Note that we already wrote one byte, so the state should be higher than the
* state read at the beginning of the tick.
*/
pLineObj->delayConsumed = TRUE;
for(retryCount = 0; retryCount < VP_CID_NORMAL_MPI_CNT; retryCount++) {
VpMpiCmdWrapper(deviceId, ecVal, VP880_CID_PARAM_RD, VP880_CID_PARAM_LEN,
tickEndState);
VP_CID(VpLineCtxType, pLineCtx,
("Vp880CtrlSetFSKGen(): CID Param Read 0x%02X Time %d",
tickEndState[0], pDevObj->timeStamp));
tickEndState[0] &= VP880_CID_STATE_MASK;
/*
* We've seen a transition toward more empty if these are the same since
* we wrote one byte earlier in this function. It means we can write more bytes.
*/
if ((pLineObj->tickBeginState[0] & VP880_CID_STATE_MASK) == tickEndState[0]) {
pLineObj->cidBytesRemain++; /* Correct for state transition just observed */
VP_CID(VpLineCtxType, pLineCtx,
("Vp880CtrlSetFSKGen(): Observed State Change to 0x%02X Time %d",
tickEndState[0], pDevObj->timeStamp));
return TRUE;
}
}
/* If we're here, means we timed out and cannot write more bytes */
}
return ((pLineObj->cidBytesRemain == 0) ? FALSE : TRUE);
#else
return ((pLineObj->cidBytesRemain == 0) ? FALSE : TRUE);
#endif
}
}
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("-Vp880CtrlSetFSKGen()"));
return returnStatus;
}
#endif
/**
* Vp880SendSignal()
* This function sends a signal on the line. The type of signal is specified
* by the type parameter passed. The structure passed specifies the parameters
* associated with the signal.
*
* Preconditions:
* The line must first be initialized.
*
* Postconditions:
* The signal specified is applied to the line.
*/
VpStatusType
Vp880SendSignal(
VpLineCtxType *pLineCtx,
VpSendSignalType type,
void *pStruct)
{
Vp880LineObjectType *pLineObj = pLineCtx->pLineObj;
VpDigitType *pDigit;
VpDigitType digit = VP_DIG_NONE;
VpDevCtxType *pDevCtx = pLineCtx->pDevCtx;
Vp880DeviceObjectType *pDevObj = pDevCtx->pDevObj;
VpStatusType status;
VP_API_FUNC(VpLineCtxType, pLineCtx, ("+Vp880SendSignal()"));
/* Proceed only if line has been initialized */
if (!(pLineObj->status & VP880_INIT_COMPLETE)) {
VP_API_FUNC(VpLineCtxType, pLineCtx, ("-Vp880SendSignal()"));
return VP_STATUS_LINE_NOT_CONFIG;
}
/*
* 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(VpLineCtxType, pLineCtx, ("-Vp880SendSignal()"));
return VP_STATUS_DEV_NOT_INITIALIZED;
}
if (pStruct == VP_NULL) {
pDigit = &digit;
} else {
pDigit = pStruct;
}
switch(type) {
#if defined (VP880_FXO_SUPPORT)
case VP_SENDSIG_DTMF_DIGIT:
status = Vp880SendDigit(pLineCtx, VP_DIGIT_GENERATION_DTMF, *pDigit);
break;
case VP_SENDSIG_PULSE_DIGIT:
pDigit = (VpDigitType *)pStruct;
status = Vp880SendDigit(pLineCtx, VP_DIGIT_GENERATION_DIAL_PULSE,
*pDigit);
break;
case VP_SENDSIG_HOOK_FLASH:
/* prevent case of *pDigit when user passes VP_NULL */
status = Vp880SendDigit(pLineCtx, VP_DIGIT_GENERATION_DIAL_HOOK_FLASH,
VP_DIG_NONE);
break;
case VP_SENDSIG_MOMENTARY_LOOP_OPEN:
status = Vp880MomentaryLoopOpen(pLineCtx);
break;
#endif
#if defined (VP880_FXS_SUPPORT)
case VP_SENDSIG_MSG_WAIT_PULSE:
status = Vp880SendMsgWaitPulse(pLineCtx, pStruct);
break;
case VP_SENDSIG_FWD_DISCONNECT:
case VP_SENDSIG_TIP_OPEN_PULSE:
if (pStruct != VP_NULL) {
VP_SEQUENCER(VpLineCtxType, pLineCtx, ("Pulse Time %d", *((uint16 *)pStruct)));
status = Vp880SendPulse(pLineCtx, type, *((uint16 *)pStruct));
} else {
VP_API_FUNC(VpLineCtxType, pLineCtx, ("-Vp880SendSignal()"));
return VP_STATUS_INVALID_ARG;
}
break;
case VP_SENDSIG_POLREV_PULSE:
if (pStruct != VP_NULL) {
status = Vp880SendPolRevPulse(pLineCtx, *((uint16 *)pStruct));
} else {
VP_API_FUNC(VpLineCtxType, pLineCtx, ("-Vp880SendSignal()"));
return VP_STATUS_INVALID_ARG;
}
break;
#endif
default:
status = VP_STATUS_INVALID_ARG;
break;
}
VP_API_FUNC(VpLineCtxType, pLineCtx, ("-Vp880SendSignal()"));
return status;
}
#if defined (VP880_FXS_SUPPORT)
/**
* Vp880SendMsgWaitPulse()
* This function sends a message waiting pulse to the line specified by the
* by the pMsgWait parameter passed. The structure specifies a voltage, on-time,
* off-time, and number of pulses.
*
* Times in this profile are supported as:
* Line State Set to Message Wiating Signal Level = On, + tick
* Delay for On-Time (in 5ms step size - fixed per cadencer definition)
* Line State Set to Off-State, +tick
* Delay for Off-Time
* Branch - Adjust cadencer values
*
* Preconditions:
* The line must first be initialized.
*
* Postconditions:
* The message waiting signal specified is applied to the line.
*/
VpStatusType
Vp880SendMsgWaitPulse(
VpLineCtxType *pLineCtx,
VpSendMsgWaitType *pMsgWait)
{
Vp880LineObjectType *pLineObj = pLineCtx->pLineObj;
VpDevCtxType *pDevCtx = pLineCtx->pDevCtx;
Vp880DeviceObjectType *pDevObj = pDevCtx->pDevObj;
VpDeviceIdType deviceId = pDevObj->deviceId;
uint8 ecVal = pLineObj->ecVal;
uint16 tempTime, firstTimer, secondTimer, roundingError, tickRate_mS;
uint16 tickRate = pDevObj->devProfileData.tickRate;
VpLineStateType currentState = pLineObj->lineState.usrCurrent;
uint8 branchCount;
uint8 addStep = 0;
uint32 aVolt;
int32 userVolt;
uint8 cmdLen = 0x08; /* Minimum Cadence with infinite on */
/*
* Set the signal generator parameters to set the A amplitude and frequency
* "very low". We'll adjust the bias to the user defined MsgWait voltage
*/
uint8 sigGenBytes[VP880_SIGA_PARAMS_LEN] = {
0x00, 0x29, 0x73, 0x04, 0x44, 0x00, 0x15, 0x7F, 0xFD, 0x00, 0x00};
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("+Vp880SendMsgWaitPulse()"));
if (pLineObj->status & VP880_IS_FXO) {
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("-Vp880SendMsgWaitPulse()"));
return VP_STATUS_INVALID_ARG;
}
/*
* If we're already in Ringing, return a failure since we're using a
* shared resource to accomplish this function.
*/
if ((currentState == VP_LINE_RINGING) || (currentState == VP_LINE_RINGING_POLREV)) {
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("-Vp880SendMsgWaitPulse()"));
return VP_STATUS_DEVICE_BUSY;
}
/*
* If the voltage is 0, it (previously) meant to use the maximum voltage
* supported by the line. However, that function has been removed so instead
* of stopping Message Waiting, just return error code to maintain a bit of
* backward compatibility (max voltage isn't applied, but it isn't stopped
* either).
*/
if ((pMsgWait != VP_NULL) && (pMsgWait->voltage == 0)) {
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("-Vp880SendMsgWaitPulse()"));
return VP_STATUS_INVALID_ARG;
}
/* All parameters passed are good -- proceed */
VpSysEnterCritical(deviceId, VP_CODE_CRITICAL_SEC);
/*
* This is implemented with the cadencer so we have to stop all previous
* sequences first
*/
pLineObj->cadence.status = VP_CADENCE_RESET_VALUE; /* No active status */
VpMemSet(pLineObj->intSequence, 0, VP880_INT_SEQ_LEN);
/*
* If we were previously running a Message Waiting cadence, stop it and
* generate the event.
* If we were previously running another cadence, let it continue and
* return.
*/
if ((pMsgWait == VP_NULL) || (pMsgWait->onTime == 0)) {
VpSetLineState(pLineCtx, currentState);
pLineObj->cadence.status = VP_CADENCE_RESET_VALUE;
pLineObj->cadence.pActiveCadence = VP_PTABLE_NULL;
pLineObj->lineEvents.process |= VP_LINE_EVID_SIGNAL_CMP;
pLineObj->processData = VP_SENDSIG_MSG_WAIT_PULSE;
VpSysExitCritical(deviceId, VP_CODE_CRITICAL_SEC);
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("-Vp880SendMsgWaitPulse()"));
return VP_STATUS_SUCCESS;
}
/**************************************************************************
* BEGIN >> METERING SIGNAL/VOLTAGE (RING GEN) PROCESSING
**************************************************************************/
/*
* Compute the new signal generator A values from the voltage and set the
* line state that is used to apply the message waiting pulse
*/
pLineObj->intSequence[VP_PROFILE_TYPE_SEQUENCER_START]
= (VP_SEQ_SPRCMD_COMMAND_INSTRUCTION | VP_SEQ_SUBCMD_LINE_STATE);
if (pMsgWait->voltage > 0) {
userVolt = pMsgWait->voltage;
pLineObj->intSequence[9] = VP_PROFILE_CADENCE_STATE_MSG_WAIT_NORM;
} else {
userVolt = -pMsgWait->voltage;
pLineObj->intSequence[9] = VP_PROFILE_CADENCE_STATE_MSG_WAIT_POLREV;
}
/* Scale by same factor as bit resolution */
aVolt = userVolt * (uint32)VP880_RINGING_BIAS_FACTOR;
/* Scale down by the bit resolution of the device */
aVolt /= (uint32)VP880_RINGING_BIAS_SCALE;
sigGenBytes[VP880_SIGA_BIAS_MSB] = (aVolt >> 8) & 0xFF;
sigGenBytes[VP880_SIGA_BIAS_LSB] = (aVolt & 0xFF);
/* Write the new signal generator parameters */
VpMpiCmdWrapper(deviceId, ecVal, VP880_RINGER_PARAMS_WRT,
VP880_RINGER_PARAMS_LEN, sigGenBytes);
/* Clear flag to indicate the generators are NOT in a Ringing Mode */
pLineObj->status &= ~(VP880_RING_GEN_NORM | VP880_RING_GEN_REV);
/**************************************************************************
* END >> METERING SIGNAL/VOLTAGE (RING GEN) PROCESSING
**************************************************************************/
/*
* Build the rest of the cadence defined by the user input (message state
* set above). Start by setting the type of profile to an API Message Wait
* Pulse type
*/
pLineObj->intSequence[VP_PROFILE_TYPE_LSB] = VP_PRFWZ_PROFILE_MSG_WAIT_PULSE_INT;
/**************************************************************************
* BEGIN >> ON-TIME PROCESSING
**************************************************************************/
/*
* The cadencer process will add 1 times the tickRate to the on-time. So
* for an on-time of 10 (i.e., 50ms) and tickRate of 10ms, if we do nothing
* else then the actual on-time would be 50ms + 10ms = 60ms. Based on
* this, subtract off (tickRate) amount while not allowing the final
* value to be set to 0. "0" is a special on-time value meaning "infinite".
*
* In case of non-integer tickrate round down to nearest ms. Round down is
* preferred (from round, or round-up) because we're assuming the tick is
* providing this much time and we need to ensure the minimum on-time. So
* a lower (tickRate) value will cause this algorithm to provide a higher
* cadence specified on-time. OK if reality is higher due to this tickRate
* rounding.
*/
roundingError = (tickRate % VP_CSLAC_TICKSTEP_1MS);
tickRate_mS = (tickRate - roundingError); /* In Dev Profile scale... */
tickRate_mS /= VP_CSLAC_TICKSTEP_1MS; /* ...now in ms scale. */
VP_SEQUENCER(VpLineCtxType, pLineCtx, ("Rounding %d for TickRate %d (ms)",
roundingError, tickRate_mS));
/*
* The minimum possible time is (5ms + tickRate). If the input is less
* than this, set to the minimum. Otherwise, subtract (tickRate) value
* from the specified time. Rounding back to 5ms (i.e., cadencer steps) is
* done later.
*/
firstTimer = pMsgWait->onTime;
if (firstTimer <= tickRate_mS) {
/*
* User specified a value that is less than what can be supported.
* Set to the minimum while avoiding 0.
*/
firstTimer = 5;
} else {
/* Subtract the tick processing time */
firstTimer -= tickRate_mS;
}
VP_SEQUENCER(VpLineCtxType, pLineCtx,
("On-Time: %d (ms) after processing for min", firstTimer));
/*
* At this point, the "firstTimer" value is in mSec and may be less than
* the (5ms) cadence step size. Now need to round up the time to ensure a
* minimum on-time.
*/
/* Compute the rounding error to add that will result in integer of 5ms steps */
roundingError = (firstTimer % 5);
/* Round-up to nearest 5ms step */
if (roundingError > 0) {
firstTimer += (5 - roundingError);
}
/* Convert from user input range (1ms) to cadence range (5ms) */
firstTimer /= 5;
/*
* Special Handling for using 16-bit time in 14-bit data fields. This
* mini-algorithm breaks the total on-time into smaller pieces wrapped
* by an inside-loop branch operator.
*/
branchCount = 0;
if (firstTimer > 8192) {
for (; firstTimer > 8192; branchCount++) {
firstTimer = ((firstTimer >> 1) & 0x3FFF);
}
cmdLen+=2;
}
pLineObj->intSequence[10] = VP_SEQ_SPRCMD_TIME_INSTRUCTION;
tempTime = ((firstTimer >> 8) & 0x1F);
pLineObj->intSequence[10] |= tempTime;
tempTime = (firstTimer & 0x00FF);
pLineObj->intSequence[11] = tempTime;
if (branchCount) {
pLineObj->intSequence[12] = VP_SEQ_SPRCMD_BRANCH_INSTRUCTION;
pLineObj->intSequence[12] |= 0x01; /* On-Time is the step 1 (0 base) */
pLineObj->intSequence[13] = branchCount;
addStep+=2;
}
/**************************************************************************
* END >> ON-TIME PROCESSING
**************************************************************************/
/**************************************************************************
* BEGIN >> OFF-TIME PROCESSING
**************************************************************************/
/*
* If the off-time is 0, we will stay in the previous state forever so the
* cadencer needs to stop where it is
*/
if (pMsgWait->offTime == 0) {
pLineObj->intSequence[VP_PROFILE_LENGTH] = cmdLen;
pLineObj->intSequence[VP_PROFILE_TYPE_SEQUENCER_COUNT_LSB] =
(0x04 + addStep);
} else {
cmdLen+=4; /* Add two for the next state and two for the off-time */
/* In-between pulses we'll return to the current state */
pLineObj->intSequence[12+addStep]
= (VP_SEQ_SPRCMD_COMMAND_INSTRUCTION | VP_SEQ_SUBCMD_LINE_STATE);
pLineObj->intSequence[13+addStep] =
ConvertApiState2PrfWizState(currentState);
/*
* For an off-time of any meaning (i.e., non-zero off-time and non-zero
* number of message waiting pulses), the API affectively adds 2-tick
* intervals to the off-time. Make sure to compensate this from the
* user specified input.
*/
secondTimer = pMsgWait->offTime;
if (secondTimer <= (2 * tickRate_mS)) {
/*
* User specified a value that is less than what can be supported.
* Set to the minimum while avoiding 0.
*/
secondTimer = 5;
} else {
/* Subtract the tick processing time */
secondTimer -= (2 * tickRate_mS);
}
VP_SEQUENCER(VpLineCtxType, pLineCtx,
("Off-Time: %d (ms) after processing", secondTimer));
/*
* Compute the rounding error used to modify the off-time to the nearest
* 5ms step, avoid 0 after processing.
*/
roundingError = (secondTimer % 5);
if (roundingError > 3) {
/* Round up is closer to requested value. */
secondTimer += (5 - roundingError);
} else {
/* Round down is closer to requested value. */
secondTimer -= roundingError;
if (secondTimer == 0) {
secondTimer = 5;
}
}
/* Convert from user input range (1ms) to cadence range (5ms) */
secondTimer /= 5;
branchCount = 0;
if (secondTimer > 8192) {
cmdLen+=2; /* Add two for the off-time branch loop */
/* Special Handling for using 16-bit time in 14-bit data fields */
for (; secondTimer > 8192; branchCount++) {
secondTimer = ((secondTimer >> 1) & 0x3FFF);
}
}
pLineObj->intSequence[14+addStep] = VP_SEQ_SPRCMD_TIME_INSTRUCTION;
tempTime = ((secondTimer >> 8) & 0x1F);
pLineObj->intSequence[14+addStep] |= tempTime;
tempTime = (secondTimer & 0x00FF);
pLineObj->intSequence[15+addStep] = tempTime;
/**********************************************************************
* BEGIN >> NUMBER OF MSG WAIT PULSE PROCESSING
**********************************************************************/
if (branchCount) {
pLineObj->intSequence[16+addStep] = VP_SEQ_SPRCMD_BRANCH_INSTRUCTION;
pLineObj->intSequence[16+addStep] |= (0x03 + (addStep / 2));
pLineObj->intSequence[17+addStep] = branchCount;
addStep+=2;
}
/*
* If the number of cycles is 0, set the branch to repeat forever. If
* it's 1, don't add a branch statement because the sequence should end
* after the first cycle, otherwise subtract 1 from the total number of
* cycles to force the correct number of "repeats" (branch)
*/
if (pMsgWait->cycles != 1) {
cmdLen+=2; /* Two more for this last branch operator */
pLineObj->intSequence[16+addStep] = VP_SEQ_SPRCMD_BRANCH_INSTRUCTION;
pLineObj->intSequence[17+addStep] = (pMsgWait->cycles) ?
(pMsgWait->cycles - 1) : pMsgWait->cycles;
}
/**********************************************************************
* END >> NUMBER OF MSG WAIT PULSE PROCESSING
**********************************************************************/
}
/**********************************************************************
* END >> OFF-TIME PROCESSING
**********************************************************************/
/*
* Set the line object cadence variables to this sequence and activate the
* sequencer
*/
pLineObj->intSequence[VP_PROFILE_LENGTH] = cmdLen;
pLineObj->intSequence[VP_PROFILE_TYPE_SEQUENCER_COUNT_LSB] = cmdLen - 4;
pLineObj->cadence.index = VP_PROFILE_TYPE_SEQUENCER_START;
pLineObj->cadence.length = pLineObj->intSequence[VP_PROFILE_LENGTH];
{
uint8 cadenceIndex = 0;
VP_SEQUENCER(VpLineCtxType, pLineCtx,
("Starting Message Waiting Sequence:"));
for (cadenceIndex = 0;
cadenceIndex < (VP_PROFILE_LENGTH + 1 + cmdLen);
cadenceIndex++) {
VP_SEQUENCER(VpLineCtxType, pLineCtx,
(" 0x%02X", pLineObj->intSequence[cadenceIndex]));
}
}
pLineObj->cadence.pActiveCadence = &pLineObj->intSequence[0];
pLineObj->cadence.pCurrentPos = &pLineObj->intSequence[8];
pLineObj->cadence.status |= VP_CADENCE_STATUS_ACTIVE;
pLineObj->cadence.status |= VP_CADENCE_STATUS_SENDSIG;
VpSysExitCritical(deviceId, VP_CODE_CRITICAL_SEC);
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("-Vp880SendMsgWaitPulse()"));
return VP_STATUS_SUCCESS;
}
/**
* Vp880SendPulse()
* This function sends either a forward disconnect or Tip Open pulse to the
* line specified for a duration given in mS.
*
* Preconditions:
* The line must first be initialized.
*
* Postconditions:
* A disconnect or tip open has been applied to the line, the line state is
* restored to what it was prior to this function being called.
*/
VpStatusType
Vp880SendPulse(
VpLineCtxType *pLineCtx,
VpSendSignalType type,
uint16 timeInMs)
{
Vp880LineObjectType *pLineObj = pLineCtx->pLineObj;
VpLineStateType currentState = pLineObj->lineState.usrCurrent;
VpProfileCadencerStateTypes cadenceState;
VpDevCtxType *pDevCtx = pLineCtx->pDevCtx;
Vp880DeviceObjectType *pDevObj = pDevCtx->pDevObj;
VpDeviceIdType deviceId = pDevObj->deviceId;
uint8 index, targetState, tickTime;
uint16 timeIn5mS = 0;
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("+Vp880SendPulse()"));
if (pLineObj->status & VP880_IS_FXO) {
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("-Vp880SendPulse()"));
return VP_STATUS_INVALID_ARG;
}
cadenceState = ConvertApiState2PrfWizState(currentState);
VpSysEnterCritical(deviceId, VP_CODE_CRITICAL_SEC);
/*
* This is implemented with the cadencer so we have to stop all previous
* sequences first
*/
pLineObj->cadence.status = VP_CADENCE_RESET_VALUE; /* No active status */
VpMemSet(pLineObj->intSequence, 0, VP880_INT_SEQ_LEN);
/* Set the cadence type and target state */
if (type == VP_SENDSIG_FWD_DISCONNECT) {
pLineObj->intSequence[VP_PROFILE_TYPE_LSB] = VP_PRFWZ_PROFILE_FWD_DISC_INT;
targetState = VP_PROFILE_CADENCE_STATE_DISCONNECT;
} else {
pLineObj->intSequence[VP_PROFILE_TYPE_LSB] = VP_PRFWZ_PROFILE_TIP_OPEN_INT;
targetState = VP_PROFILE_CADENCE_STATE_TIP_OPEN;
}
/* First step is to go to target state */
index = 0;
pLineObj->intSequence[VP_PROFILE_TYPE_SEQUENCER_START + index]
= (VP_SEQ_SPRCMD_COMMAND_INSTRUCTION | VP_SEQ_SUBCMD_LINE_STATE);
index++;
pLineObj->intSequence[VP_PROFILE_TYPE_SEQUENCER_START + index]
= targetState;
/*
* The sequencer itself adds 1 tick of delay, so subtract that amount of
* time unless time remaining is <= tick time.
*/
tickTime = TICKS_TO_MS(1, pDevObj->devProfileData.tickRate);
if (timeInMs > tickTime) {
timeInMs-=tickTime;
}
/* Then wait for the time specified -- rounded to 5mS increments */
if (timeInMs < 5) {
timeIn5mS = 1;
} else {
timeIn5mS = timeInMs / 5;
}
index++;
pLineObj->intSequence[VP_PROFILE_TYPE_SEQUENCER_START + index]
= VP_SEQ_SPRCMD_TIME_INSTRUCTION;
pLineObj->intSequence[VP_PROFILE_TYPE_SEQUENCER_START + index]
|= (timeIn5mS >> 8) & 0x1F;
index++;
pLineObj->intSequence[VP_PROFILE_TYPE_SEQUENCER_START + index]
= (timeIn5mS & 0xFF);
/* Restore the line state */
index++;
pLineObj->intSequence[VP_PROFILE_TYPE_SEQUENCER_START + index]
= (VP_SEQ_SPRCMD_COMMAND_INSTRUCTION | VP_SEQ_SUBCMD_LINE_STATE);
index++;
pLineObj->intSequence[VP_PROFILE_TYPE_SEQUENCER_START + index]
= cadenceState;
/* Then wait for 100mS for the detector to become stable */
index++;
pLineObj->intSequence[VP_PROFILE_TYPE_SEQUENCER_START + index]
= VP_SEQ_SPRCMD_TIME_INSTRUCTION;
index++;
pLineObj->intSequence[VP_PROFILE_TYPE_SEQUENCER_START + index]
= 20; /* 5mS per increment */
index++; /* Adjust one more for length values */
/*
* Set the line object cadence variables to this sequence and activate the
* sequencer
*/
pLineObj->intSequence[VP_PROFILE_LENGTH] = index + 4;
pLineObj->intSequence[VP_PROFILE_TYPE_SEQUENCER_COUNT_LSB] = index;
pLineObj->cadence.index = VP_PROFILE_TYPE_SEQUENCER_START;
pLineObj->cadence.length = pLineObj->intSequence[VP_PROFILE_LENGTH];
pLineObj->cadence.pActiveCadence = &pLineObj->intSequence[0];
pLineObj->cadence.pCurrentPos =
&pLineObj->intSequence[VP_PROFILE_TYPE_SEQUENCER_START];
pLineObj->cadence.status |= VP_CADENCE_STATUS_ACTIVE;
pLineObj->cadence.status |= VP_CADENCE_STATUS_SENDSIG;
VpSysExitCritical(deviceId, VP_CODE_CRITICAL_SEC);
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("-Vp880SendPulse()"));
return VP_STATUS_SUCCESS;
}
/**
* Vp880SendPolRevPulse()
* This function sends a Pol Rev pulse to the line specified with a duration
* given in mS.
*
* Preconditions:
* The line must first be initialized.
*
* Postconditions:
* A Pol Rev pulse has been applied to the line, the line state is restored
* to what it was prior to this function being called.
*/
VpStatusType
Vp880SendPolRevPulse(
VpLineCtxType *pLineCtx,
uint16 timeInMs)
{
Vp880LineObjectType *pLineObj = pLineCtx->pLineObj;
VpLineStateType currentState = pLineObj->lineState.usrCurrent;
VpProfileCadencerStateTypes cadenceState, polRevState;
VpDevCtxType *pDevCtx = pLineCtx->pDevCtx;
Vp880DeviceObjectType *pDevObj = pDevCtx->pDevObj;
VpDeviceIdType deviceId = pDevObj->deviceId;
uint8 index, tickTime;
uint16 timeIn5mS = 0;
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("+Vp880SendPolRevPulse()"));
if ((pLineObj->status & VP880_IS_FXO) || (currentState == VP_LINE_DISCONNECT)) {
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("-Vp880SendPolRevPulse()"));
return VP_STATUS_INVALID_ARG;
}
cadenceState = ConvertApiState2PrfWizState(currentState);
polRevState = ConvertApiState2PrfWizState(VpGetReverseState(currentState));
VpSysEnterCritical(deviceId, VP_CODE_CRITICAL_SEC);
/*
* This is implemented with the cadencer so we have to stop all previous
* sequences first
*/
pLineObj->cadence.status = VP_CADENCE_RESET_VALUE; /* No active status */
VpMemSet(pLineObj->intSequence, 0, VP880_INT_SEQ_LEN);
/* Set the cadence type */
pLineObj->intSequence[VP_PROFILE_TYPE_LSB] =
VP_PRFWZ_PROFILE_POLREV_PULSE_INT;
/* First step is to go to polrev state */
index = 0;
pLineObj->intSequence[VP_PROFILE_TYPE_SEQUENCER_START + index]
= (VP_SEQ_SPRCMD_COMMAND_INSTRUCTION | VP_SEQ_SUBCMD_LINE_STATE);
index++;
pLineObj->intSequence[VP_PROFILE_TYPE_SEQUENCER_START + index]
= polRevState;
/*
* The sequencer itself adds 1 tick of delay, so subtract that amount of
* time unless time remaining is <= tick time.
*/
tickTime = TICKS_TO_MS(1, pDevObj->devProfileData.tickRate);
if (timeInMs > tickTime) {
timeInMs-=tickTime;
}
/* Then wait for the time specified -- rounded to 5mS increments */
if (timeInMs < 5) {
timeIn5mS = 1;
} else {
timeIn5mS = timeInMs / 5;
}
index++;
pLineObj->intSequence[VP_PROFILE_TYPE_SEQUENCER_START + index]
= VP_SEQ_SPRCMD_TIME_INSTRUCTION;
pLineObj->intSequence[VP_PROFILE_TYPE_SEQUENCER_START + index]
|= (timeIn5mS >> 8) & 0x1F;
index++;
pLineObj->intSequence[VP_PROFILE_TYPE_SEQUENCER_START + index]
= (timeIn5mS & 0xFF);
/* Restore the line state */
index++;
pLineObj->intSequence[VP_PROFILE_TYPE_SEQUENCER_START + index]
= (VP_SEQ_SPRCMD_COMMAND_INSTRUCTION | VP_SEQ_SUBCMD_LINE_STATE);
index++;
pLineObj->intSequence[VP_PROFILE_TYPE_SEQUENCER_START + index]
= cadenceState;
/* Then wait for 100mS for the detector to become stable */
index++;
pLineObj->intSequence[VP_PROFILE_TYPE_SEQUENCER_START + index]
= VP_SEQ_SPRCMD_TIME_INSTRUCTION;
index++;
pLineObj->intSequence[VP_PROFILE_TYPE_SEQUENCER_START + index]
= 20; /* 5mS per increment */
index++; /* Adjust one more for length values */
/*
* Set the line object cadence variables to this sequence and activate the
* sequencer
*/
pLineObj->intSequence[VP_PROFILE_LENGTH] = index + 4;
pLineObj->intSequence[VP_PROFILE_TYPE_SEQUENCER_COUNT_LSB] = index;
pLineObj->cadence.index = VP_PROFILE_TYPE_SEQUENCER_START;
pLineObj->cadence.length = pLineObj->intSequence[VP_PROFILE_LENGTH];
pLineObj->cadence.pActiveCadence = &pLineObj->intSequence[0];
pLineObj->cadence.pCurrentPos =
&pLineObj->intSequence[VP_PROFILE_TYPE_SEQUENCER_START];
pLineObj->cadence.status |= VP_CADENCE_STATUS_ACTIVE;
pLineObj->cadence.status |= VP_CADENCE_STATUS_SENDSIG;
VpSysExitCritical(deviceId, VP_CODE_CRITICAL_SEC);
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("-Vp880SendPolRevPulse()"));
return VP_STATUS_SUCCESS;
}
#endif
#if defined (VP880_FXO_SUPPORT)
/**
* Vp880MomentaryLoopOpen()
* This function applies a Momentary Loop Open to an FXO line and tests for
* a parallel off-hook.
*
* Preconditions:
* The line must first be initialized and must be of FXO type.
*
* Postconditions:
* A 10ms loop open is applied to the line and line state returns to previous
* condition. An event is generated indicating if there exists a parallel phone
* off-hook or not.
*/
VpStatusType
Vp880MomentaryLoopOpen(
VpLineCtxType *pLineCtx)
{
Vp880LineObjectType *pLineObj = pLineCtx->pLineObj;
VpLineStateType currentState = pLineObj->lineState.usrCurrent;
VpProfileCadencerStateTypes cadenceState;
VpDevCtxType *pDevCtx = pLineCtx->pDevCtx;
Vp880DeviceObjectType *pDevObj = pDevCtx->pDevObj;
VpDeviceIdType deviceId = pDevObj->deviceId;
uint8 ecVal = pLineObj->ecVal;
uint8 index;
uint16 timeIn5mS = 0;
uint8 loopSup[VP880_LOOP_SUP_LEN] = {0x18, 0xE1, 0x79, 0xEB};
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("+Vp880MomentaryLoopOpen()"));
if (!(pLineObj->status & VP880_IS_FXO)) {
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("-Vp880MomentaryLoopOpen()"));
return VP_STATUS_INVALID_ARG;
}
cadenceState = ConvertApiState2PrfWizState(currentState);
VpSysEnterCritical(deviceId, VP_CODE_CRITICAL_SEC);
/*
* This is implemented with the cadencer so we have to stop all previous
* sequences first
*/
pLineObj->cadence.status = VP_CADENCE_RESET_VALUE; /* No active status */
VpMemSet(pLineObj->intSequence, 0, VP880_INT_SEQ_LEN);
/* Set the cadence type */
pLineObj->intSequence[VP_PROFILE_TYPE_LSB] =
VP_PRFWZ_PROFILE_MOMENTARY_LOOP_OPEN_INT;
VpMpiCmdWrapper(deviceId, ecVal, VP880_LOOP_SUP_WRT, VP880_LOOP_SUP_LEN,
loopSup);
/* First step is to go to Loop Open */
index = 0;
pLineObj->intSequence[VP_PROFILE_TYPE_SEQUENCER_START + index]
= (VP_SEQ_SPRCMD_COMMAND_INSTRUCTION | VP_SEQ_SUBCMD_LINE_STATE);
index++;
pLineObj->intSequence[VP_PROFILE_TYPE_SEQUENCER_START + index]
= VP_PROFILE_CADENCE_STATE_FXO_LOOP_OPEN;
/* Then wait for at least 10ms. The time is higher by 2*ApiTick value */
timeIn5mS = 2; /* Cadencer Tick is 5ms increments */
index++;
pLineObj->intSequence[VP_PROFILE_TYPE_SEQUENCER_START + index]
= VP_SEQ_SPRCMD_TIME_INSTRUCTION;
pLineObj->intSequence[VP_PROFILE_TYPE_SEQUENCER_START + index]
|= (timeIn5mS >> 8) & 0x1F;
index++;
pLineObj->intSequence[VP_PROFILE_TYPE_SEQUENCER_START + index]
= (timeIn5mS & 0xFF);
/* Restore the line state */
index++;
pLineObj->intSequence[VP_PROFILE_TYPE_SEQUENCER_START + index]
= (VP_SEQ_SPRCMD_COMMAND_INSTRUCTION | VP_SEQ_SUBCMD_LINE_STATE);
index++;
pLineObj->intSequence[VP_PROFILE_TYPE_SEQUENCER_START + index]
= cadenceState;
index++; /* Adjust for length values */
/*
* Set the line object cadence variables to this sequence and activate the
* sequencer
*/
pLineObj->intSequence[VP_PROFILE_LENGTH] = index + 4;
pLineObj->intSequence[VP_PROFILE_TYPE_SEQUENCER_COUNT_LSB] = index;
pLineObj->cadence.index = VP_PROFILE_TYPE_SEQUENCER_START;
pLineObj->cadence.length = pLineObj->intSequence[VP_PROFILE_LENGTH];
pLineObj->cadence.pActiveCadence = &pLineObj->intSequence[0];
pLineObj->cadence.pCurrentPos =
&pLineObj->intSequence[VP_PROFILE_TYPE_SEQUENCER_START];
pLineObj->cadence.status |= VP_CADENCE_STATUS_ACTIVE;
pLineObj->cadence.status |= VP_CADENCE_STATUS_SENDSIG;
VpSysExitCritical(deviceId, VP_CODE_CRITICAL_SEC);
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("-Vp880MomentaryLoopOpen()"));
return VP_STATUS_SUCCESS;
}
/**
* Vp880SendDigit()
* This function sends a DTMF or Dial Pulse digit on an FXO line. It creates
* a sequencer compatible profile to control the FXO loop open, loop close, and
* time operators.
*
* Preconditions:
* The line must first be initialized and must be of FXO type.
*
* Postconditions:
* The digit specified is sent on the line in the form specified (DTMF or Dial
* Pulse). This function returns the success code if the line is an FXO type of
* line, if the digit is between 0 - 9, and if the digit type is either DTMF or
* Dial Pulse.
*/
VpStatusType
Vp880SendDigit(
VpLineCtxType *pLineCtx, /**< Line to send a digit on */
VpDigitGenerationType digitType, /**< Type of digit to send. May indicate
* DTMF, Dial Pulse, or Hook Flash
*/
VpDigitType digit) /**< The digit to send. Used if type of
* digit is DTMF or Dial Pulse
*/
{
Vp880LineObjectType *pLineObj = pLineCtx->pLineObj;
VpDevCtxType *pDevCtx = pLineCtx->pDevCtx;
Vp880DeviceObjectType *pDevObj = pDevCtx->pDevObj;
uint16 tempTime, firstTimer, secondTimer;
uint16 tickAdjustment;
VpDeviceIdType deviceId = pDevObj->deviceId;
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("+Vp880SendDigit()"));
if (!(pLineObj->status & VP880_IS_FXO)) {
VP_ERROR(VpLineCtxType, pLineCtx, ("SendDigit() - Function invalid for FXS"));
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("-Vp880SendDigit()"));
return VP_STATUS_INVALID_ARG;
}
switch(digitType) {
case VP_DIGIT_GENERATION_DIAL_PULSE:
if ((pLineObj->lineState.currentState != VP_LINE_FXO_TALK)
&& (pLineObj->lineState.currentState != VP_LINE_FXO_LOOP_CLOSE)) {
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("-Vp880SendDigit()"));
return VP_STATUS_INVALID_ARG;
}
case VP_DIGIT_GENERATION_DTMF:
if ((VpIsDigit(digit) == FALSE) || (digit == VP_DIG_NONE)) {
VP_ERROR(VpLineCtxType, pLineCtx, ("SendDigit() - Invalid digit"));
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("-Vp880SendDigit()"));
return VP_STATUS_INVALID_ARG;
}
break;
case VP_DIGIT_GENERATION_DIAL_HOOK_FLASH:
if ((pLineObj->lineState.currentState != VP_LINE_FXO_TALK)
&& (pLineObj->lineState.currentState != VP_LINE_FXO_LOOP_CLOSE)) {
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("-Vp880SendDigit()"));
return VP_STATUS_INVALID_ARG;
}
break;
default:
VP_ERROR(VpLineCtxType, pLineCtx, ("SendDigit() - Invalid digitType"));
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("-Vp880SendDigit()"));
return VP_STATUS_INVALID_ARG;
}
/* Parameters passed are good -- proceed */
VpSysEnterCritical(deviceId, VP_CODE_CRITICAL_SEC);
/*
* This is implemented with the cadencer so we have to stop all previous
* sequences first
*/
pLineObj->cadence.status = VP_CADENCE_RESET_VALUE; /* No active status */
VpMemSet(pLineObj->intSequence, 0, VP880_INT_SEQ_LEN);
/* Tick adjustment in 5ms cadence time units */
tickAdjustment = TICKS_TO_MS(1, pDevObj->devProfileData.tickRate) / 5;
switch(digitType) {
case VP_DIGIT_GENERATION_DTMF:
Vp880MuteChannel(pLineCtx, TRUE);
Vp880SetDTMFGenerators(pLineCtx, VP_CID_NO_CHANGE, digit);
/* Fixed total length and sequence length for DTMF generation */
pLineObj->intSequence[VP_PROFILE_LENGTH] = 0x0C;
pLineObj->intSequence[VP_PROFILE_TYPE_SEQUENCER_COUNT_LSB] = 0x08;
pLineObj->intSequence[VP_PROFILE_TYPE_SEQUENCER_START]
= (VP_SEQ_SPRCMD_COMMAND_INSTRUCTION | VP_SEQ_SUBCMD_SIGGEN);
pLineObj->intSequence[12]
= (VP_SEQ_SPRCMD_COMMAND_INSTRUCTION | VP_SEQ_SUBCMD_SIGGEN);
pLineObj->intSequence[9] =
(VP_SEQ_SIGGEN_A_EN | VP_SEQ_SIGGEN_B_EN);
pLineObj->intSequence[13] = VP_SEQ_SIGGEN_ALL_DISABLED;
firstTimer = pLineObj->digitGenStruct.dtmfOnTime;
if (firstTimer > tickAdjustment) {
firstTimer -= tickAdjustment;
} else {
firstTimer = 1;
}
pLineObj->intSequence[10] = VP_SEQ_SPRCMD_TIME_INSTRUCTION;
tempTime = (firstTimer >> 8) & 0x03;
pLineObj->intSequence[10] |= tempTime;
tempTime = (firstTimer & 0x00FF);
pLineObj->intSequence[11] |= tempTime;
secondTimer = pLineObj->digitGenStruct.dtmfOffTime;
if (secondTimer > tickAdjustment) {
secondTimer -= tickAdjustment;
} else {
secondTimer = 1;
}
pLineObj->intSequence[14] = VP_SEQ_SPRCMD_TIME_INSTRUCTION;
tempTime = (secondTimer >> 8) & 0x03;
pLineObj->intSequence[14] |= tempTime;
tempTime = (secondTimer & 0x00FF);
pLineObj->intSequence[15] |= tempTime;
pLineObj->intSequence[VP_PROFILE_TYPE_LSB] =
VP_PRFWZ_PROFILE_DTMF_DIG_GEN;
break;
case VP_DIGIT_GENERATION_DIAL_PULSE:
/* Fixed total length and sequence length for DP generation */
pLineObj->intSequence[VP_PROFILE_LENGTH] = 0x10;
pLineObj->intSequence[VP_PROFILE_TYPE_SEQUENCER_COUNT_LSB] = 0x0C;
pLineObj->intSequence[VP_PROFILE_TYPE_SEQUENCER_START]
= (VP_SEQ_SPRCMD_COMMAND_INSTRUCTION | VP_SEQ_SUBCMD_LINE_STATE);
pLineObj->intSequence[12]
= (VP_SEQ_SPRCMD_COMMAND_INSTRUCTION | VP_SEQ_SUBCMD_LINE_STATE);
if (pLineObj->lineState.currentState == VP_LINE_FXO_TALK) {
pLineObj->intSequence[9] =
VP_PROFILE_CADENCE_STATE_FXO_OHT;
pLineObj->intSequence[13] =
VP_PROFILE_CADENCE_STATE_FXO_LOOP_TALK;
} else {
pLineObj->intSequence[9] =
VP_PROFILE_CADENCE_STATE_FXO_LOOP_OPEN;
pLineObj->intSequence[13] =
VP_PROFILE_CADENCE_STATE_FXO_LOOP_CLOSE;
}
firstTimer = pLineObj->digitGenStruct.breakTime;
if (firstTimer > tickAdjustment) {
firstTimer -= tickAdjustment;
} else {
firstTimer = 1;
}
pLineObj->intSequence[10] = VP_SEQ_SPRCMD_TIME_INSTRUCTION;
tempTime = (firstTimer >> 8) & 0x03;
pLineObj->intSequence[10] |= tempTime;
tempTime = (firstTimer & 0x00FF);
pLineObj->intSequence[11] |= tempTime;
secondTimer = pLineObj->digitGenStruct.makeTime;
if (secondTimer > tickAdjustment * 2) {
secondTimer -= tickAdjustment * 2;
} else {
secondTimer = 1;
}
pLineObj->intSequence[14] = VP_SEQ_SPRCMD_TIME_INSTRUCTION;
tempTime = (secondTimer >> 8) & 0x03;
pLineObj->intSequence[14] |= tempTime;
tempTime = (secondTimer & 0x00FF);
pLineObj->intSequence[15] |= tempTime;
firstTimer = pLineObj->digitGenStruct.dpInterDigitTime;
if (digit > 1) {
pLineObj->intSequence[16] = VP_SEQ_SPRCMD_BRANCH_INSTRUCTION;
pLineObj->intSequence[17] = digit - 1;
pLineObj->intSequence[18] = VP_SEQ_SPRCMD_TIME_INSTRUCTION;
tempTime = (firstTimer >> 8) & 0x03;
pLineObj->intSequence[18] |= tempTime;
tempTime = (firstTimer & 0x00FF);
pLineObj->intSequence[19] |= tempTime;
} else {
pLineObj->intSequence[16] = VP_SEQ_SPRCMD_TIME_INSTRUCTION;
tempTime = (firstTimer >> 8) & 0x03;
pLineObj->intSequence[16] |= tempTime;
tempTime = (firstTimer & 0x00FF);
pLineObj->intSequence[17] |= tempTime;
pLineObj->intSequence[VP_PROFILE_LENGTH] = 0x0E;
pLineObj->intSequence[VP_PROFILE_TYPE_SEQUENCER_COUNT_LSB]
= 0x0A;
}
pLineObj->intSequence[VP_PROFILE_TYPE_LSB] =
VP_PRFWZ_PROFILE_DIAL_PULSE_DIG_GEN;
break;
case VP_DIGIT_GENERATION_DIAL_HOOK_FLASH:
/* Fixed total length and sequence length for FLASH generation */
pLineObj->intSequence[VP_PROFILE_LENGTH] = 0x0A;
pLineObj->intSequence[VP_PROFILE_TYPE_SEQUENCER_COUNT_LSB] = 0x06;
pLineObj->intSequence[VP_PROFILE_TYPE_SEQUENCER_START]
= (VP_SEQ_SPRCMD_COMMAND_INSTRUCTION | VP_SEQ_SUBCMD_LINE_STATE);
pLineObj->intSequence[12]
= (VP_SEQ_SPRCMD_COMMAND_INSTRUCTION | VP_SEQ_SUBCMD_LINE_STATE);
if (pLineObj->lineState.currentState == VP_LINE_FXO_TALK) {
pLineObj->intSequence[9] =
VP_PROFILE_CADENCE_STATE_FXO_OHT;
pLineObj->intSequence[13] =
VP_PROFILE_CADENCE_STATE_FXO_LOOP_TALK;
} else {
pLineObj->intSequence[9] =
VP_PROFILE_CADENCE_STATE_FXO_LOOP_OPEN;
pLineObj->intSequence[13] =
VP_PROFILE_CADENCE_STATE_FXO_LOOP_CLOSE;
}
firstTimer = pLineObj->digitGenStruct.flashTime;
if (firstTimer > tickAdjustment) {
firstTimer -= tickAdjustment;
} else {
firstTimer = 1;
}
pLineObj->intSequence[10] = VP_SEQ_SPRCMD_TIME_INSTRUCTION;
tempTime = (firstTimer >> 8) & 0x03;
pLineObj->intSequence[10] |= tempTime;
tempTime = (firstTimer & 0x00FF);
pLineObj->intSequence[11] |= tempTime;
pLineObj->intSequence[VP_PROFILE_TYPE_LSB] =
VP_PRFWZ_PROFILE_HOOK_FLASH_DIG_GEN;
break;
default:
/*
* This can only occur if there is an error in the error checking
* above.
*/
VpSysExitCritical(deviceId, VP_CODE_CRITICAL_SEC);
return VP_STATUS_INVALID_ARG;
}
pLineObj->cadence.index = VP_PROFILE_TYPE_SEQUENCER_START;
pLineObj->cadence.length = pLineObj->intSequence[VP_PROFILE_LENGTH];
pLineObj->cadence.pActiveCadence = &pLineObj->intSequence[0];
pLineObj->cadence.pCurrentPos = &pLineObj->intSequence[8];
pLineObj->cadence.status |= VP_CADENCE_STATUS_ACTIVE;
pLineObj->cadence.status |= VP_CADENCE_STATUS_SENDSIG;
VpSysExitCritical(deviceId, VP_CODE_CRITICAL_SEC);
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("-Vp880SendDigit()"));
return VP_STATUS_SUCCESS;
}
#endif
#ifdef VP880_FXS_SUPPORT
/**
* Vp880FSKGeneratorReady()
* This function is used for Caller ID to determine if the FSK generator is
* ready to accept another byte. It uses the device caller ID state machine
* and signaling (caller ID status) register. This function should be called
* from an API internal function only.
*
* Returns:
* TRUE if the FSK generator for Caller ID can accept a byte, FALSE otherwise.
*/
bool
Vp880FSKGeneratorReady(
VpLineCtxType *pLineCtx)
{
VpDevCtxType *pDevCtx = pLineCtx->pDevCtx;
Vp880DeviceObjectType *pDevObj = pDevCtx->pDevObj;
Vp880LineObjectType *pLineObj = pLineCtx->pLineObj;
#ifdef VP_CID_POLLING_ENABLED
VpDeviceIdType deviceId = pDevObj->deviceId;
uint8 ecVal = pLineObj->ecVal;
uint8 stateRetry;
#endif
uint8 stateIndex;
uint8 devRev = pDevObj->staticInfo.rcnPcn[VP880_RCN_LOCATION];
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("+Vp880FSKGeneratorReady()"));
if (devRev <= VP880_REV_VC) {
uint8 numBytes[] = {
/*
* NOTE: The #bytes in IDLE state is 2 for this array init purposes,
* but this should never be used. Why? Because the state machine is
* only operating in the IDLE state with API CID State Machine
* running and operating on FSK Data Type when the silicon level
* state machine is disabled. In this case, the return value below
* is forced to 3. So only if the state is IDLE and NOT Disabled is
* this return value used - which should be impossible.
* In other words, this is a safety mechanism only .. hopefully.
*/
2, /* 0 = #define VP880_CID_STATE_IDLE 0x00 */
1, /* 1 = #define VP880_CID_STATE_RDY 0x20 */
0, /* 2 = #define VP880_CID_STATE_FULL 0x40 */
1, /* 3 = #define VP880_CID_STATE_LBYTE 0x60 */
0, /* 4 = #define VP880_CID_STATE_L2BYTE 0x80 */
2, /* 5 = #define VP880_CID_STATE_URUN 0xA0 */
0, /* 6 = RSVD */
0 /* 7 = RSVD */
};
/* Check the Generator State */
stateIndex = (((pLineObj->tickBeginState[0] & VP880_CID_STATE_MASK) >> 5) & 0x7);
pLineObj->cidBytesRemain = numBytes[stateIndex];
VP_CID(VpLineCtxType, pLineCtx, ("CID Param Read 0x%02X", pLineObj->tickBeginState[0]));
} else {
uint8 cidState[VP880_CID_PARAM_LEN];
uint8 numBytes[] = {
3, /* 0 = Idle #define VP880_CID_STATE_IDLE 0x00 */
2, /* 1 = Empty #define VP880_CID_STATE_EMPTY_D 0x20 */
1, /* 2 = Half-Full #define VP880_CID_STATE_HALF_FULL_D 0x40 */
2, /* 3 = Last Byte - EOM #define VP880_CID_STATE_LBYTE_D 0x60 */
1, /* 4 = Last 2 Byte - EOM #define VP880_CID_STATE_L2BYTE_D 0x80 */
2, /* 5 = Underrun */
0, /* 6 = Full #define VP880_CID_STATE_FULL_D 0xC0 */
0 /* 7 = Last 3 Byte - EOM #define VP880_CID_STATE_L3BYTE_D 0xE0
* There should be no way to get here. It occurs when "FUll" state and
* write + EOM, which the VP-API-II does not do.
*/
};
#ifdef VP_CID_POLLING_ENABLED
if ((pLineObj->tickBeginState[0] & VP880_CID_STATE_MASK) == VP880_CID_STATE_FULL_D) {
pLineObj->delayConsumed = TRUE;
stateRetry = VP_CID_NORMAL_MPI_CNT;
do {
stateRetry--;
/* Check the Generator State until it is no longer full */
VpMpiCmdWrapper(deviceId, ecVal, VP880_CID_PARAM_RD, VP880_CID_PARAM_LEN,
cidState);
cidState[0] &= VP880_CID_STATE_MASK;
VP_CID(VpLineCtxType, pLineCtx, ("CID State 0x%02X at time %d",
cidState[0], pDevObj->timeStamp));
if (cidState[0] != VP880_CID_STATE_FULL_D) {
stateRetry = 0;
}
} while (stateRetry != 0);
} else {
#endif
cidState[0] = (pLineObj->tickBeginState[0] & VP880_CID_STATE_MASK);
#ifdef VP_CID_POLLING_ENABLED
}
#endif
stateIndex = ((cidState[0] >> 5) & 0x7);
pLineObj->cidBytesRemain = numBytes[stateIndex];
}
VP_CID(VpLineCtxType, pLineCtx,
("Vp880FSKGeneratorReady() - return %s with cidBytes remain %d",
((pLineObj->cidBytesRemain == 0) ? "FALSE" : "TRUE"), pLineObj->cidBytesRemain));
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("-Vp880FSKGeneratorReady()"));
return ((pLineObj->cidBytesRemain == 0) ? FALSE : TRUE);
} /* Vp880FSKGeneratorReady() */
/**
* Vp880CliGetEncodedByte()
* This function returns an encoded byte of data that is suitable for writing
* the FSK generator (device dependent).
*
* Preconditions
* Must have a valid CLI packet in to work from.
*
* Postconditions
* The per-channel caller ID buffer will be updated with encoded data.
*
*/
VpCliEncodedDataType
Vp880CliGetEncodedByte(
VpLineCtxType *pLineCtx,
uint8 *pByte)
{
Vp880LineObjectType *pLineObj = pLineCtx->pLineObj;
VpOptionEventMaskType *pLineEvents = &(pLineObj->lineEvents);
VpCallerIdType *pCidStruct = &(pLineObj->callerId);
uint8 checkSumIndex = VP_CID_PROFILE_FSK_PARAM_LEN +
pLineObj->callerId.pCliProfile[VP_CID_PROFILE_FSK_PARAM_LEN] +
VP_CID_PROFILE_CHECKSUM_OFFSET_LSB;
if (pLineObj->suspendCid == TRUE) {
*pByte = '\0';
VP_CID(VpLineCtxType, pLineCtx, ("VE880 EOM In Progress..."));
return VP_CLI_ENCODE_END;
}
return VpCSLACCliGetEncodedByte(pByte, pCidStruct, &pLineObj->processData,
pLineEvents, checkSumIndex);
} /* Vp880CliGetEncodedByte() */
#endif /* VP880_FXS_SUPPORT */
#endif /* VP_CSLAC_SEQ_EN */
#endif /* VP_CC_880_SERIES */