blob: df61fa197d57617d6340c25ec8b10d23b3d41ce5 [file] [log] [blame]
/** \file vp_api_cslac_seq.c
* vp_api_cslac_seq.c
*
* This file contains functions that are required to run the CSLAC sequencer.
*
* Copyright (c) 2011, Microsemi
*
* $Revision: 1.1.2.1.14.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) || defined (VP_CC_890_SERIES) || \
defined (VP_CC_580_SERIES) || defined (VP_CC_790_SERIES)) && defined (VP_CSLAC_SEQ_EN)
/* INCLUDES */
#include "../includes/vp_api.h" /* Typedefs and function prototypes for API */
#include "../includes/vp_api_cslac_seq.h"
#include "../includes/vp_api_int.h" /* Device specific typedefs and function prototypes */
#include "../../arch/uvb/sys_service.h"
#if defined (VP_CC_790_SERIES) || \
(defined (VP_CC_880_SERIES) && defined (VP880_FXS_SUPPORT)) || \
(defined (VP_CC_890_SERIES) && defined (VP890_FXS_SUPPORT))
static bool
VpFSKGeneratorReady(
VpLineCtxType *pLineCtx);
static bool
VpDTMFGeneratorReady(
VpLineCtxType *pLineCtx);
static VpStatusType
VpCtrlSetCliTone(
VpLineCtxType *pLineCtx,
bool mode);
static VpCliEncodedDataType
VpCliGetEncodedByte(
VpLineCtxType *pLineCtx,
uint8 *pByte);
static bool
VpCtrlSetFSKGen(
VpLineCtxType *pLineCtx,
VpCidGeneratorControlType mode,
uint8 digit);
static VpDigitType
VpConvertCharToDigitType(
char digit);
static void
VpCtrlSetDTMFGen(
VpLineCtxType *pLineCtx,
VpCidGeneratorControlType mode,
VpDigitType digit);
static VpStatusType
VpCtrlDetectDTMF(
VpLineCtxType *pLineCtx,
bool mode);
#endif
#if defined(VP_CC_790_SERIES) || (defined (VP_CC_880_SERIES) && defined (VP880_FXS_SUPPORT))
static VpStatusType
AddMeteringSection(
VpLineCtxType *pLineCtx,
uint8 *pIntSequence,
uint8 *pIndex,
uint16 tickRate,
uint16 onTime,
uint16 offTime,
uint16 numMeters);
#endif
#if (defined (VP_CC_880_SERIES) && defined (VP880_FXS_SUPPORT)) || \
(defined (VP_CC_890_SERIES) && defined (VP890_FXS_SUPPORT))
static void
VpCSLACComputeHowlerFreqStep(
uint32 sweepInterval,
VpSeqDataType *cadence,
uint16 updateInterval);
#endif
static void
VpCtrlMuteChannel(
VpLineCtxType *pLineCtx,
bool mode);
/**
* VpSeq()
* This function calls the appropriate sequencer function based on the current
* position in the sequencer and the device type.
*
* Preconditions:
* The profile passed must be pointing to an instruction that is supported by
* the device type.
*
* Postconditions:
* The instruction specified by the profile data is called. The line context
* is passed to the called function. Note: The line context may be valid or
* VP_NULL, this function is not affected. This function returns the success
* code as long as the pointer is pointing to an instruction that is supported
* by the device.
*/
VpStatusType
VpSeq(
VpLineCtxType *pLineCtx, /**< Line that has an active sequencer */
VpProfilePtrType pProfile) /**< Sequence profile, pointing to current
* location in sequence, not typically the
* starting address
*/
{
VpDevCtxType *pDevCtx = pLineCtx->pDevCtx;
VP_SEQUENCER(VpLineCtxType, pLineCtx, ("+VpSeq()"));
/*
* This function is passed a pointer that starts at the current position of
* the cadence sequence, controlled by the API
*/
if (pProfile == VP_NULL) {
VP_SEQUENCER(VpLineCtxType, pLineCtx, ("NULL Sequence Profile"));
return VP_STATUS_INVALID_ARG;
}
VP_SEQUENCER(VpLineCtxType, pLineCtx, ("Sequence Command 0x%02X 0x%02X",
pProfile[0], pProfile[1]));
switch(pProfile[0] & VP_SEQ_OPERATOR_MASK) {
case VP_SEQ_SPRCMD_COMMAND_INSTRUCTION:
switch(pDevCtx->deviceType) {
#if defined (VP_CC_880_SERIES)
case VP_DEV_880_SERIES:
return Vp880CommandInstruction(pLineCtx, pProfile);
#endif
#if defined (VP_CC_790_SERIES)
case VP_DEV_790_SERIES:
return Vp790CommandInstruction(pLineCtx, pProfile);
#endif
#if defined (VP_CC_580_SERIES)
case VP_DEV_580_SERIES:
return Vp580CommandInstruction(pLineCtx, pProfile);
#endif
default:
return VP_STATUS_INVALID_ARG;
}
case VP_SEQ_SPRCMD_TIME_INSTRUCTION:
return VpTimeInstruction(pLineCtx, pProfile);
case VP_SEQ_SPRCMD_BRANCH_INSTRUCTION:
return VpBranchInstruction(pLineCtx, pProfile);
default:
return VP_STATUS_INVALID_ARG;
}
} /* VpSeq() */
/**
* VpServiceSeq()
* This function tests the line status for an active sequence, and calls the
* VpSeq function if there is an active sequence. However, this function does
* not check to see if the operation being pointed to by the current sequence is
* supported.
*
* Preconditions:
* The device context cannot be VP_NULL and only CSLAC devices supported.
*
* Postconditions:
* If there is an active cadence that is supported by the line, then it is
* called (via VpSeq). If the current operation is not supported, the line
* object active cadence is removed (i.e., set to inactive).
*/
bool
VpServiceSeq(
VpDevCtxType *pDevCtx) /**< Device that has a sequence. The sequence may
* not be active
*/
{
uint8 channelId, maxChannels;
VpDeviceInfoType deviceInfo;
VpLineCtxType *pLineCtx;
void *pLineObj;
/*
* pCadence is initialized to VP_NULL to remove compiler warnings
* (.. pCadence might be used uninitialized..), but this function is called
* only from API functions for devices that require cadence support.
* Therefore, pCadence is initialized by the line object association below
*/
VpSeqDataType *pCadence = VP_NULL;
deviceInfo.pDevCtx = pDevCtx;
deviceInfo.pLineCtx = VP_NULL;
VpGetDeviceInfo(&deviceInfo);
maxChannels = deviceInfo.numLines;
for (channelId = 0; channelId < maxChannels; channelId++) {
pLineCtx = pDevCtx->pLineCtx[channelId];
if (pLineCtx != VP_NULL) {
pLineObj = pLineCtx->pLineObj;
switch(pDevCtx->deviceType) {
#if defined (VP_CC_880_SERIES)
case VP_DEV_880_SERIES:
pCadence = &((Vp880LineObjectType *)pLineObj)->cadence;
break;
#endif
#if defined (VP_CC_790_SERIES)
case VP_DEV_790_SERIES:
pCadence = &((Vp790LineObjectType *)pLineObj)->cadence;
break;
#endif
#if defined (VP_CC_580_SERIES)
case VP_DEV_580_SERIES:
pCadence = &((Vp580LineObjectType *)pLineObj)->cadence;
break;
#endif
default:
return FALSE;
}
if((pCadence->status & VP_CADENCE_STATUS_ACTIVE) == VP_CADENCE_STATUS_ACTIVE ) {
VP_SEQUENCER(VpLineCtxType, pLineCtx, ("Line Object 0x%04X on Channel %d",
pCadence->status, channelId));
if(VpSeq(pLineCtx, pCadence->pCurrentPos) != VP_STATUS_SUCCESS) {
pCadence->status &= ~VP_CADENCE_STATUS_ACTIVE;
pCadence->pActiveCadence = VP_PTABLE_NULL;
}
}
}
}
return TRUE;
} /* VpServiceSeq() */
/**
* VpBranchInstruction()
* This function implements the Sequencer Branch instruction for the CSLAC
* device types.
*
* Preconditions:
* The line must first be initialized and the sequencer data must be valid.
*
* Postconditions:
* The branch count is either set if this is the first time for this branch, or
* the branch count is decremented if this branch instruction has been executed
* before. If the branch count is decremented to 0, the sequencer index is
* increased. If the branch count is 0 at the first time the particular branch
* is executed, the branch is repeated forever. If the branch count is not 0,
* the sequencer is set back to the instruction specified in the profile. This
* function can only return VP_SUCCESS since any valid combination of "branch
* to" and "branch count" is valid.
*/
VpStatusType
VpBranchInstruction(
VpLineCtxType *pLineCtx,
VpProfilePtrType pSeqData)
{
VpDevCtxType *pDevCtx = pLineCtx->pDevCtx;
VpDeviceType deviceType = pDevCtx->deviceType;
VpSeqDataType *pCadence;
VpOptionEventMaskType *pLineEvents;
uint8 length, index;
uint16 *pEventData;
VpLineStateType lineState;
uint8 branchDepth;
void *pLineObj = pLineCtx->pLineObj;
void *pDevObj = pDevCtx->pDevObj;
uint8 *pIntSeqType;
#if (VP_CC_DEBUG_SELECT & VP_DBG_SEQUENCER)
uint16 timeStamp = 0;
#endif
switch (deviceType) {
#if defined (VP_CC_790_SERIES)
case VP_DEV_790_SERIES:
if (!(((Vp790DeviceObjectType *)pDevObj)->status.state & VP_DEV_INIT_CMP)) {
return VP_STATUS_DEV_NOT_INITIALIZED;
}
pCadence = &((Vp790LineObjectType *)pLineObj)->cadence;
pLineEvents = &((Vp790LineObjectType *)pLineObj)->lineEvents;
pEventData = &((Vp790LineObjectType *)pLineObj)->processData;
lineState = ((Vp790LineObjectType *)pLineObj)->lineState.usrCurrent;
pIntSeqType = &((Vp790LineObjectType *)pLineObj)->intSequence[VP_PROFILE_TYPE_LSB];
#if (VP_CC_DEBUG_SELECT & VP_DBG_SEQUENCER)
timeStamp = ((Vp790DeviceObjectType *)pDevObj)->timeStamp;
#endif
break;
#endif
#if defined (VP_CC_880_SERIES)
case VP_DEV_880_SERIES:
if (!(((Vp880DeviceObjectType *)pDevObj)->state & VP_DEV_INIT_CMP)) {
return VP_STATUS_DEV_NOT_INITIALIZED;
}
/*
* Do not proceed if the device calibration is in progress. This could
* damage the device.
*/
if (((Vp880DeviceObjectType *)pDevObj)->state & VP_DEV_IN_CAL) {
return VP_STATUS_DEV_NOT_INITIALIZED;
}
pCadence = &((Vp880LineObjectType *)pLineObj)->cadence;
pLineEvents = &((Vp880LineObjectType *)pLineObj)->lineEvents;
pEventData = &((Vp880LineObjectType *)pLineObj)->processData;
lineState = ((Vp880LineObjectType *)pLineObj)->lineState.usrCurrent;
pIntSeqType = &((Vp880LineObjectType *)pLineObj)->intSequence[VP_PROFILE_TYPE_LSB];
#if (VP_CC_DEBUG_SELECT & VP_DBG_SEQUENCER)
timeStamp = ((Vp880DeviceObjectType *)pDevObj)->timeStamp;
#endif
break;
#endif
#if defined (VP_CC_580_SERIES)
case VP_DEV_580_SERIES:
if (!(((Vp580DeviceObjectType *)pDevObj)->status.state & VP_DEV_INIT_CMP)) {
return VP_STATUS_DEV_NOT_INITIALIZED;
}
pCadence = &((Vp580LineObjectType *)pLineObj)->cadence;
pLineEvents = &((Vp580LineObjectType *)pLineObj)->lineEvents;
pEventData = &((Vp580LineObjectType *)pLineObj)->processData;
lineState = ((Vp580LineObjectType *)pLineObj)->lineState.usrCurrent;
pIntSeqType = &((Vp580LineObjectType *)pLineObj)->intSequence[VP_PROFILE_TYPE_LSB];
#if (VP_CC_DEBUG_SELECT & VP_DBG_SEQUENCER)
timeStamp = ((Vp580DeviceObjectType *)pDevObj)->timeStamp;
#endif
break;
#endif
default:
return VP_STATUS_INVALID_ARG;
}
length = pCadence->length;
index = pCadence->index;
if (pCadence->status & VP_CADENCE_STATUS_BRANCHING) {
VP_SEQUENCER(VpLineCtxType, pLineCtx, ("Branching Length %d Index %d At %d Device Time %d",
length, index, pCadence->branchAt, timeStamp));
/*
* We're already branching, but possibly at a step after this point. In
* other words, we may have been branched back to a branch step
* Determine if we are repeating this step, or if we are being branched
* back from a later step
*/
if (index < pCadence->branchAt) {
/*
* We're at an earlier step in the branch loop, so use the second
* set of branch timers
*/
branchDepth = VP_CSLAC_BRANCH_LVL_1;
if (!(pCadence->status & VP_CADENCE_STATUS_BRANCHING_LVL2)) {
pCadence->status |= VP_CADENCE_STATUS_BRANCHING_LVL2;
pCadence->count[branchDepth] = pSeqData[1];
}
} else {
/* This is a continuation from this branch */
branchDepth = VP_CSLAC_BRANCH_LVL_0;
}
if (pCadence->count[branchDepth] > 0) {
/*
* If the repeat value set in the profile is = 0, this means repeat
* forever. Therefore, don't decrement the actual count value
*/
if (pSeqData[1] != 0) {
pCadence->count[branchDepth]--;
}
/* Send the profile pointer back to the branch location */
/* Account for header offset */
pCadence->index = (((pSeqData[0] & 0x1F) * 2)
+ VP_PROFILE_TYPE_SEQUENCER_START);
pCadence->pCurrentPos =
&(pCadence->pActiveCadence[pCadence->index]);
} else {
/*
* We don't need to repeat this branch. Just see if the profile is
* complete
*/
index+=2;
if (index < (length + VP_PROFILE_LENGTH + 1)) {
pCadence->index = index;
pCadence->pCurrentPos+=2;
if (pCadence->status & VP_CADENCE_STATUS_BRANCHING_LVL2) {
pCadence->status &= ~VP_CADENCE_STATUS_BRANCHING_LVL2;
} else {
pCadence->status &= ~VP_CADENCE_STATUS_BRANCHING;
}
} else { /* The profile is complete. */
switch(pCadence->pActiveCadence[VP_PROFILE_TYPE_LSB]) {
case VP_PRFWZ_PROFILE_METERING_GEN:
pLineEvents->process |= VP_LINE_EVID_MTR_CMP;
break;
case VP_PRFWZ_PROFILE_RINGCAD:
pLineEvents->process |= VP_LINE_EVID_RING_CAD;
*pEventData = VP_RING_CAD_DONE;
break;
case VP_PRFWZ_PROFILE_TONECAD:
pLineEvents->process |= VP_LINE_EVID_TONE_CAD;
break;
case VP_PRFWZ_PROFILE_HOOK_FLASH_DIG_GEN:
pLineEvents->process |= VP_LINE_EVID_SIGNAL_CMP;
*pEventData = VP_SENDSIG_HOOK_FLASH;
break;
case VP_PRFWZ_PROFILE_DIAL_PULSE_DIG_GEN:
pLineEvents->process |= VP_LINE_EVID_SIGNAL_CMP;
*pEventData = VP_SENDSIG_PULSE_DIGIT;
break;
case VP_PRFWZ_PROFILE_DTMF_DIG_GEN:
pLineEvents->process |= VP_LINE_EVID_SIGNAL_CMP;
*pEventData = VP_SENDSIG_DTMF_DIGIT;
VpCtrlMuteChannel(pLineCtx, FALSE);
break;
case VP_PRFWZ_PROFILE_MSG_WAIT_PULSE_INT:
pLineEvents->process |= VP_LINE_EVID_SIGNAL_CMP;
*pEventData = VP_SENDSIG_MSG_WAIT_PULSE;
VpSetLineState(pLineCtx, lineState);
*pIntSeqType = 0;
break;
default:
break;
}
pCadence->status = VP_CADENCE_RESET_VALUE;
}
}
} else {
/*
* We are not branching, so this is the first branching loop. Set the
* parameter to indicate this step is branching.
*/
branchDepth = VP_CSLAC_BRANCH_LVL_0;
pCadence->count[branchDepth] = pSeqData[1];
pCadence->branchAt = index;
VP_SEQUENCER(VpLineCtxType, pLineCtx, ("Branch To %d, Count %d",
pCadence->branchAt, pCadence->count[branchDepth]));
if(pCadence->count[branchDepth] == 0) {
/*
* This means branch forever. Implement by setting to max value
* here, and not decreasing in steps above
*/
pCadence->count[branchDepth] = 0xFF;
} else {
/* Repeat already (following lines) */
pCadence->count[branchDepth]--;
}
/* Account for header offset */
pCadence->index =
(((pSeqData[0] & 0x1F) * 2) + VP_PROFILE_TYPE_SEQUENCER_START);
pCadence->pCurrentPos = &(pCadence->pActiveCadence[pCadence->index]);
pCadence->status |= VP_CADENCE_STATUS_BRANCHING;
}
/* If we've disabled the cadence, clear the active cadence pointer */
if (!(pCadence->status & VP_CADENCE_STATUS_ACTIVE)) {
pCadence->pActiveCadence = VP_PTABLE_NULL;
}
return VP_STATUS_SUCCESS;
}
/**
* VpTimeInstruction()
* This function implements the Sequencer Time instruction for the CSLAC device
* types.
*
* Preconditions:
* The line must first be initialized and the sequencer data must be valid.
*
* Postconditions:
* The timer is decremented and when it decreases to 0, the pointer in the
* sequence profile (passed) is updated to the next command past the time
* operator currently being executed. If there are no more operators, then
* the cadence is stopped.
*/
VpStatusType
VpTimeInstruction(
VpLineCtxType *pLineCtx,
VpProfilePtrType pSeqData)
{
VpDevCtxType *pDevCtx = pLineCtx->pDevCtx;
VpDeviceType deviceType = pDevCtx->deviceType;
VpSeqDataType *pCadence;
#if defined (VP_CC_790_SERIES) \
|| (defined (VP_CC_880_SERIES) && defined (VP880_FXS_SUPPORT)) \
|| (defined (VP_CC_890_SERIES) && defined (VP890_FXS_SUPPORT))
VpCallerIdType *pCid = VP_NULL;
VpLineStateType lineState = VP_LINE_DISCONNECT;
#endif
VpOptionEventMaskType *pLineEvents;
uint16 *pEventData;
uint16 tickRate;
#if (VP_CC_DEBUG_SELECT & VP_DBG_SEQUENCER)
uint16 timeStamp = 0;
#endif
bool forever = FALSE;
void *pLineObj = pLineCtx->pLineObj;
void *pDevObj = pDevCtx->pDevObj;
uint8 *pIntSeqType;
uint16 msInTick;
/* Time in sequence is in 5mS incremements. We need to convert to TICKS */
uint16 timeInSeq = ( (( (uint16)pSeqData[0] & 0x1F) << 8) | (uint16)pSeqData[1]);
switch (deviceType) {
#if defined (VP_CC_790_SERIES)
case VP_DEV_790_SERIES:
if (!(((Vp790DeviceObjectType *)pDevObj)->status.state & VP_DEV_INIT_CMP)) {
return VP_STATUS_DEV_NOT_INITIALIZED;
}
pCadence = &((Vp790LineObjectType *)pLineObj)->cadence;
tickRate =
((Vp790DeviceObjectType *)pDevObj)->devProfileData.tickRate;
#if (VP_CC_DEBUG_SELECT & VP_DBG_SEQUENCER)
timeStamp = ((Vp790DeviceObjectType *)pDevObj)->timeStamp;
#endif
pCid = &((Vp790LineObjectType *)pLineObj)->callerId;
pLineEvents = &((Vp790LineObjectType *)pLineObj)->lineEvents;
pEventData = &((Vp790LineObjectType *)pLineObj)->processData;
lineState = ((Vp790LineObjectType *)pLineObj)->lineState.usrCurrent;
pIntSeqType = &((Vp790LineObjectType *)pLineObj)->intSequence[VP_PROFILE_TYPE_LSB];
break;
#endif
#if defined (VP_CC_880_SERIES)
case VP_DEV_880_SERIES:
if (!(((Vp880DeviceObjectType *)pDevObj)->state & VP_DEV_INIT_CMP)) {
return VP_STATUS_DEV_NOT_INITIALIZED;
}
/*
* Do not proceed if the device calibration is in progress. This could
* damage the device.
*/
if (((Vp880DeviceObjectType *)pDevObj)->state & VP_DEV_IN_CAL) {
return VP_STATUS_DEV_NOT_INITIALIZED;
}
pCadence = &((Vp880LineObjectType *)pLineObj)->cadence;
tickRate =
((Vp880DeviceObjectType *)pDevObj)->devProfileData.tickRate;
#if (VP_CC_DEBUG_SELECT & VP_DBG_SEQUENCER)
timeStamp = ((Vp880DeviceObjectType *)pDevObj)->timeStamp;
#endif
#if defined (VP_CC_880_SERIES) && defined (VP880_FXS_SUPPORT)
pCid = &((Vp880LineObjectType *)pLineObj)->callerId;
lineState = ((Vp880LineObjectType *)pLineObj)->lineState.usrCurrent;
#endif
pLineEvents = &((Vp880LineObjectType *)pLineObj)->lineEvents;
pEventData = &((Vp880LineObjectType *)pLineObj)->processData;
pIntSeqType = &((Vp880LineObjectType *)pLineObj)->intSequence[VP_PROFILE_TYPE_LSB];
break;
#endif
#if defined (VP_CC_580_SERIES)
case VP_DEV_580_SERIES:
if (!(((Vp580DeviceObjectType *)pDevObj)->status.state & VP_DEV_INIT_CMP)) {
return VP_STATUS_DEV_NOT_INITIALIZED;
}
pCadence = &((Vp580LineObjectType *)pLineObj)->cadence;
tickRate =
((Vp580DeviceObjectType *)pDevObj)->devProfileData.tickRate;
#if (VP_CC_DEBUG_SELECT & VP_DBG_SEQUENCER)
timeStamp = ((Vp580DeviceObjectType *)pDevObj)->timeStamp;
#endif
pLineEvents = &((Vp580LineObjectType *)pLineObj)->lineEvents;
pEventData = &((Vp580LineObjectType *)pLineObj)->processData;
pIntSeqType = &((Vp580LineObjectType *)pLineObj)->intSequence[VP_PROFILE_TYPE_LSB];
break;
#endif
default:
return VP_STATUS_INVALID_ARG;
}
VP_SEQUENCER(VpLineCtxType, pLineCtx,
("Time Operator: Pre-Processed Time Remain: %d from Starting Time %d",
pCadence->timeRemain, timeInSeq));
if (pCadence->status & VP_CADENCE_STATUS_MID_TIMER) {
if (pCadence->timeRemain) {
pCadence->timeRemain--;
VP_SEQUENCER(VpLineCtxType, pLineCtx,
("Time Operator: Decreasing time remain to %d", pCadence->timeRemain));
}
} else {
/*
* This operation truncates times rather than rounds them off. Algorithms that use this
* timer need to take that into account.
*/
pCadence->status |= VP_CADENCE_STATUS_MID_TIMER;
pCadence->timeRemain = MS_TO_TICKRATE((timeInSeq * 5), tickRate);
VP_SEQUENCER(VpLineCtxType, pLineCtx,
("Time Operator: Conversion MS_TO_TICKRATE Time Remain: %d from Starting Time %d",
pCadence->timeRemain, timeInSeq));
if (pCadence->timeRemain == 0) {
/* Always is selected. End the cadence and leave the state as is */
pCadence->status = VP_CADENCE_RESET_VALUE;
forever = TRUE;
} else {
/*
* Find out how long in ms 1 "tick" is, then subtract that amount
* from the time required in the cadence. Lower limit 1 tick.
*/
msInTick = TICKS_TO_MS(1, tickRate);
VP_SEQUENCER(VpLineCtxType, pLineCtx,
("Time Operator: msInTick (%d) based on tickRate (%d)", msInTick, tickRate));
/*
* If the time specified in the sequence can be executed with at
* least one tick, then subtract one "tick" worth of time
*/
if ((timeInSeq * 5) >= msInTick) {
pCadence->timeRemain =
MS_TO_TICKRATE(((timeInSeq * 5) - msInTick), tickRate);
VP_SEQUENCER(VpLineCtxType, pLineCtx,
("Time Operator: Adjusting timRemain to (%d)", pCadence->timeRemain));
}
}
}
VP_SEQUENCER(VpLineCtxType, pLineCtx,
("Time Operator: Post-Processed Time Remain: %d from Starting Time %d",
pCadence->timeRemain, timeInSeq));
/* If the time is over, move on to the next sequence if there is one */
if (pCadence->timeRemain == 0) {
pCadence->index+=2;
VP_SEQUENCER(VpLineCtxType, pLineCtx, ("1. Time Operator Increment Index to %d",
pCadence->index));
if (pCadence->index < (pCadence->length + VP_PROFILE_LENGTH + 1)) {
VP_SEQUENCER(VpLineCtxType, pLineCtx, ("Time Operator -- Current Data 0x%02X 0x%02X pCad 0x%02X 0x%02X",
pSeqData[0], pSeqData[1], pCadence->pCurrentPos[0], pCadence->pCurrentPos[1]));
pSeqData+=2;
pCadence->pCurrentPos = pSeqData;
VP_SEQUENCER(VpLineCtxType, pLineCtx, ("Time Operator -- Next Data 0x%02X 0x%02X pCad 0x%02X 0x%02X",
pSeqData[0], pSeqData[1], pCadence->pCurrentPos[0], pCadence->pCurrentPos[1]));
} else { /* The profile is complete. */
switch(pCadence->pActiveCadence[VP_PROFILE_TYPE_LSB]) {
case VP_PRFWZ_PROFILE_METERING_GEN:
pLineEvents->process |= VP_LINE_EVID_MTR_CMP;
break;
case VP_PRFWZ_PROFILE_RINGCAD:
if (forever == FALSE) {
pLineEvents->process |= VP_LINE_EVID_RING_CAD;
*pEventData = VP_RING_CAD_DONE;
}
break;
case VP_PRFWZ_PROFILE_TONECAD:
pLineEvents->process |= VP_LINE_EVID_TONE_CAD;
break;
case VP_PRFWZ_PROFILE_HOOK_FLASH_DIG_GEN:
pLineEvents->process |= VP_LINE_EVID_SIGNAL_CMP;
*pEventData = VP_SENDSIG_HOOK_FLASH;
break;
case VP_PRFWZ_PROFILE_DIAL_PULSE_DIG_GEN:
pLineEvents->process |= VP_LINE_EVID_SIGNAL_CMP;
*pEventData = VP_SENDSIG_PULSE_DIGIT;
break;
case VP_PRFWZ_PROFILE_DTMF_DIG_GEN:
pLineEvents->process |= VP_LINE_EVID_SIGNAL_CMP;
*pEventData = VP_SENDSIG_DTMF_DIGIT;
VpCtrlMuteChannel(pLineCtx, FALSE);
break;
case VP_PRFWZ_PROFILE_MSG_WAIT_PULSE_INT:
pLineEvents->process |= VP_LINE_EVID_SIGNAL_CMP;
*pEventData = VP_SENDSIG_MSG_WAIT_PULSE;
*pIntSeqType = 0;
break;
case VP_PRFWZ_PROFILE_FWD_DISC_INT:
pLineEvents->process |= VP_LINE_EVID_SIGNAL_CMP;
*pEventData = VP_SENDSIG_FWD_DISCONNECT;
*pIntSeqType = 0;
break;
case VP_PRFWZ_PROFILE_TIP_OPEN_INT:
pLineEvents->process |= VP_LINE_EVID_SIGNAL_CMP;
*pEventData = VP_SENDSIG_TIP_OPEN_PULSE;
*pIntSeqType = 0;
break;
case VP_PRFWZ_PROFILE_POLREV_PULSE_INT:
pLineEvents->process |= VP_LINE_EVID_SIGNAL_CMP;
*pEventData = VP_SENDSIG_POLREV_PULSE;
*pIntSeqType = 0;
break;
default:
break;
}
pCadence->status = VP_CADENCE_RESET_VALUE;
}
pCadence->status &= ~VP_CADENCE_STATUS_MID_TIMER;
} else {
/* Check to see if we're in the middle of a Wait on function. If so,
* check to see if we still need to wait on CID (only supported wait
* on operator). If CID is complete, terminate the timer function.
* If not, continue..
*/
#if defined (VP_CC_790_SERIES) \
|| (defined (VP_CC_880_SERIES) && defined (VP880_FXS_SUPPORT)) \
|| (defined (VP_CC_890_SERIES) && defined (VP890_FXS_SUPPORT))
if ((pCid != VP_NULL) && (pCid->status & VP_CID_WAIT_ON_ACTIVE)) {
VP_CID(VpLineCtxType, pLineCtx, ("CID Status 0x%08lX", pCid->status));
if (pCid->status & VP_CID_IN_PROGRESS) {
/* Do nothing */
VP_CID(VpLineCtxType, pLineCtx, ("CID In Progress"));
} else {
VP_CID(VpLineCtxType, pLineCtx, ("Terminating Timer"));
/* Terminate this timer operation and the wait on */
pCid->status &= ~ VP_CID_WAIT_ON_ACTIVE;
pCadence->status &= ~VP_CADENCE_STATUS_MID_TIMER;
pCadence->timeRemain = 0;
VP_SEQUENCER(VpLineCtxType, pLineCtx, ("2. Time Operator Increment Index to %d",
pCadence->index));
pCadence->index+=2;
if (pCadence->index <
(pCadence->length + VP_PROFILE_LENGTH + 1)) {
pSeqData+=2;
pCadence->pCurrentPos = pSeqData;
} else { /* The profile is complete. */
switch(pCadence->pActiveCadence[VP_PROFILE_TYPE_LSB]) {
case VP_PRFWZ_PROFILE_METERING_GEN:
pLineEvents->process |= VP_LINE_EVID_MTR_CMP;
break;
case VP_PRFWZ_PROFILE_RINGCAD:
pLineEvents->process |= VP_LINE_EVID_RING_CAD;
*pEventData = VP_RING_CAD_DONE;
break;
case VP_PRFWZ_PROFILE_TONECAD:
pLineEvents->process |= VP_LINE_EVID_TONE_CAD;
break;
case VP_PRFWZ_PROFILE_HOOK_FLASH_DIG_GEN:
pLineEvents->process |= VP_LINE_EVID_SIGNAL_CMP;
*pEventData = VP_SENDSIG_HOOK_FLASH;
break;
case VP_PRFWZ_PROFILE_DIAL_PULSE_DIG_GEN:
pLineEvents->process |= VP_LINE_EVID_SIGNAL_CMP;
*pEventData = VP_SENDSIG_PULSE_DIGIT;
break;
case VP_PRFWZ_PROFILE_DTMF_DIG_GEN:
pLineEvents->process |= VP_LINE_EVID_SIGNAL_CMP;
*pEventData = VP_SENDSIG_DTMF_DIGIT;
VpCtrlMuteChannel(pLineCtx, FALSE);
break;
case VP_PRFWZ_PROFILE_MSG_WAIT_PULSE_INT:
pLineEvents->process |= VP_LINE_EVID_SIGNAL_CMP;
*pEventData = VP_SENDSIG_MSG_WAIT_PULSE;
VpSetLineState(pLineCtx, lineState);
*pIntSeqType = 0;
break;
case VP_PRFWZ_PROFILE_FWD_DISC_INT:
pLineEvents->process |= VP_LINE_EVID_SIGNAL_CMP;
*pEventData = VP_SENDSIG_FWD_DISCONNECT;
*pIntSeqType = 0;
break;
case VP_PRFWZ_PROFILE_TIP_OPEN_INT:
pLineEvents->process |= VP_LINE_EVID_SIGNAL_CMP;
*pEventData = VP_SENDSIG_TIP_OPEN_PULSE;
*pIntSeqType = 0;
break;
case VP_PRFWZ_PROFILE_POLREV_PULSE_INT:
pLineEvents->process |= VP_LINE_EVID_SIGNAL_CMP;
*pEventData = VP_SENDSIG_POLREV_PULSE;
*pIntSeqType = 0;
break;
default:
break;
}
pCadence->status = VP_CADENCE_RESET_VALUE;
}
}
}
#endif
}
/* If we've disabled the cadence, clear the active cadence pointer */
if (!(pCadence->status & VP_CADENCE_STATUS_ACTIVE)) {
pCadence->pActiveCadence = VP_PTABLE_NULL;
}
return VP_STATUS_SUCCESS;
}
#if (defined (VP_CC_890_SERIES) && defined (VP890_FXS_SUPPORT)) || \
(defined (VP_CC_880_SERIES) && defined (VP880_FXS_SUPPORT))
/**
* VpCSLACHowlerInit()
* This function fills in the cadence structure for special howler tones. Special Howler Tones
* are those howler tones which require frequency, amplitude or both increasing or decreasing
* during the cadencing operation. Supporting these in the VP-API-II may require updates to the
* Signal Generators at some regular interval. The Special Howler Tones supported by this function
* are:
*
* #define VP_CSLAC_HOWLER_TONE (0x04) UK BTNR 1080 Specification
* #define VP_CSLAC_AUS_HOWLER_TONE (0x08)
* #define VP_CSLAC_NTT_HOWLER_TONE (0x0C) NTT Edition 5
*
* The Profile Wizard generates a special tone cadence when one of these howler tone types are
* required. A bit-field indicates the type of tone, but more important is the cadence is as
* follows:
*
* 0. Generator On
* 1. Generator Ramp
* 2. Delay 10ms
* 3. Repeat Forever from step 1.
*
* This creates an updated interval of 10ms + 2 x tickrate, where the tick duration occurs
* between step 2-3, and step 3-1. The "Repeat" operation only adjusts the cadence pointers, it
* does not perform the next operation until the next tick. To maintain precise sweep interval,
* the step values need to account for the tickrate.
*
* Preconditions:
* The calling device specific function should pre-mask the PW output and check if the result
* is one that is supported prior to calling this function. The input to this function that
* specifies the Special Howler Tone required MUST be exact.
*
* Postconditions:
* The cadence structure is filled with start, stop, and step information required to implement
* the special howler cadence passed. The Howler Tone itself is started outside this function.
*/
bool
VpCSLACHowlerInit(
VpSeqDataType *cadence, /**< I/O:
* INPUT: The type of Howler Tone being performed.
* OUTPUT: Frequency and Level Paramaters necessary to perform the
* the specified Howler tone assuming a specific cadence
* profile format
*/
uint16 tickRate) /**< INPUT: Used to determine step sizes since the adjustment intervals
* are a function of the tickrate.
*/
{
bool returnValue = TRUE;
uint16 updateInterval = (2 * tickRate); /* In scale of the device profile */
uint16 tickCount = MS_TO_TICKRATE(10, tickRate);
if (tickRate > 0xA00) {
updateInterval += (tickRate * 2 * tickCount);
} else {
updateInterval += (tickRate * tickCount);
}
VP_SEQUENCER(None, VP_NULL,
("TickCount %d Update Interval 0x%04X", tickCount, updateInterval));
/*
* "levelStep" is a bit-mask of add/shift to the previous level in 1.15 format.
* +3dB per step is 1.414215 mutltiple which works out to 0xB505. Values < 0x8000 are
* level reductions and will be implemented as subtract/shift.
*/
switch(cadence->toneType) {
case VP_CSLAC_HOWLER_TONE: /* UK Howler from BTNR 1080 */
/*
* Frequency Sweep Rate = 1 second period (500ms each sweep direction)
*
* Issue 15: Frequency Range = [800Hz to 3.2kHz]
* startFreq = 0x0888 (800Hz)
* stopFreq = 0x2222 (3200Hz)
*
* Draft 960-G: Frequency Range = [800Hz to 2.5kHz]
* startFreq = 0x0888 (800Hz)
* stopFreq = 0x1AA9 (2500Hz)
*/
#if (VP_UK_HOWLER_IN_USE == VP_UK_HOWLER_BTNR_VER15)
cadence->startFreq = 0x0888;
cadence->stopFreq = 0x2222;
#elif (VP_UK_HOWLER_IN_USE == VP_UK_HOWLER_BTNR_DRAFT_G)
cadence->startFreq = 0x0888;
cadence->stopFreq = 0x1AA9;
#else
#error "VP_UK_HOWLER_IN_USE must be set to either VP_UK_HOWLER_BTNR_VER15 or VP_UK_HOWLER_BTNR_DRAFT_G"
#endif
/* Sweep interval passed to this funciton is 500ms in device profile tickrate scale */
VpCSLACComputeHowlerFreqStep(0x1F400, cadence, updateInterval);
/*
* Level Sweep is over a 36dB range. The maximum level is 0x7FFF, so the minimum value
* is 36dB less: (0x7FFF * 0.015849 = 519.32 (0x207)).
*
* startLevel = 0x0207;
* stopLevel = 0x7FFF;
*/
cadence->startLevel = 0x0207;
cadence->stopLevel = 0x7FFF;
/*
* The entire level sweep for UK Howler Tone is 12+/-2 seconds so if we want to use the
* freuency transition point as an indicator when to make the level change, then we
* need to make each level step 36dB/12 = 3dB ideally. However, the frequency steps
* are not ideally 1 second intervals (genally 1.02 seconds) so don't divide by 12.
* Instead, divide by 12/1.02 = 11.7647 for 36dB/11.7647 = 3.06dB. Converting gives:
* 1.422328787 which 1.15-bit format is 1.422332764 using 0xB60F.
*/
cadence->levelStep = 0xB60F;
break;
case VP_CSLAC_AUS_HOWLER_TONE: /* Australian Standard - ACIF S002:2001 */
/*
* Frequency Sweep Rate = 1 second period (500ms each sweep direction)
*
* Frequency Range = [1500Hz to 3.2kHz]
* startFreq = 0x1000 (1500Hz)
* stopFreq = 0x2222 (3200Hz)
*/
cadence->startFreq = 0x1000; /* 1500Hz */
cadence->stopFreq = 0x2222; /* 3200Hz */
/* Sweep interval passed to this funciton is 500ms in device profile tickrate scale */
VpCSLACComputeHowlerFreqStep(0x1F400, cadence, updateInterval);
/*
* Level Sweep is over a 30dB range from -10dBm to +20dBm. Max is = 0x7FFF (by
* definition), so min being 30dB less is (32,767 * 0.0316227 = 1036.184 (0x40D rounded
* up)).
*
* startLevel = 0x040D;
* stopLevel = 0x7FFF;
*/
cadence->startLevel = 0x040D;
cadence->stopLevel = 0x7FFF;
/*
* The entire level sweep for AUS Howler Tone is 20+/-5 seconds so if we want to use the
* freuency transition point as an indicator when to make the level change, then we
* need to make each level step 30dB/20 = 1.5dB ideally. However, the frequency steps
* are not ideally 1 second intervals (genally 1.02 seconds) so don't divide by 20.
* Instead, divide by 20/1.02 = 19.6078 for 30dB/19.6078 = 1.53dB. Converting gives:
* 1.19261 which 1.15-bit format is 1.530092402 using 0x98A8.
*/
cadence->levelStep = 0x98A8;
break;
case VP_CSLAC_NTT_HOWLER_TONE: /* NTT Edition 5 */
/*
* NTT Howler is specified as a single frequency (400Hz) with continuously increasing
* level over a period of [3-15 seconds] up to <= 36dBm and is output for 10 - 22
* seconds. This algorithm manages the frequency programming and level sweep portion
* only. It is up to the application to stop NTT Howler tone in <= 22 seconds in order
* to meet Edition 5 Requirements.
*
* The requirements mentioned above are shown in NTT Edition 5, Table 3.3.8 Electrical
* Conditions for Audible Tones sent by the Network. The requirement for the total level
* sweep is unclear. The API implements a nominal 30dB increase.
*
* Due to rounding errors (for using 16-bit fixed point math), the API does not target
* the nominal 15 second sweep limit. Instead, it targets 14.7 seconds which has been
* computed (from tickrates 5 - 10ms in 0.5ms step sizes) and measured (5, 6, 7, 8,
* 8.33ms, 9ms, and 10ms tickrate) to meet the <= 15 second sweep requirement.
*/
/*
* Frequency = 400Hz fixed
* startFreq = 0x0444 (400Hz)
* stopFreq = 0x0444 (400Hz)
* freqStep = 0
*/
cadence->startFreq = 0x0444; /* 400Hz */
cadence->freqStep = 0x0000;
cadence->stopFreq = 0x0444; /* 400Hz */
/*
* Level Sweep is over a 30dB range from min to max. Maximum level is specified only to
* <= 36dBm per NTT Edition 5. It would be good to see if a more precise requirement
* exists. The max sillicon program level is = 0x7FFF, so with the min being 30dB less
* means the starting level is (32,767 * 0.0316227 = 1036.184 (0x40C)).
*
* startLevel = 0x040C;
* stopLevel = 0x7FFF;
*/
cadence->startLevel = 0x040C;
cadence->stopLevel = 0x7FFF;
/*
* The level is ramped continuously in dB over a 14.7 second interval (NTT Edition 5
* states 3-15 second interval). If we were to compute this at run-time, we would have
* to divide 30dB by the number of available adjustment steps in a 14.7-second window
* then convert the result from dB to voltage gain. As of P2.19.0 which use this
* function only for VE880 and VE890 API, both of which require tickrate <= 12ms and
* in no known customer use have tickrate < 5ms, it's more efficient to simply use a
* lookup table. This also removes all tickrate related uncertainty and can be 100%
* validated. Note that for tickrate values not exactly at the 0.5ms point (i.e., the
* steps used to create the lookup table), the algorithm will select a level increase
* that will reduce the total ramp duration.
*/
/*
* Initialize to the value that will reach max amplitude the fastest in case of
* algorithm failure below. This ensures that the Howler Tone will be heard at peak
* volume by the customer before being disabled.
*/
cadence->levelStep = 33148;
{
#define NTT_HOWLER_LUT_MAX (17)
uint8 loopCount;
uint16 levelStepLut[NTT_HOWLER_LUT_MAX][2] = {
{1280, 32923}, /* Use for tickrates <= 5ms */
{1408, 32938}, /* Use for tickrates (5ms < tickrate <= 5.5ms) */
{1536, 32954}, /* Use for tickrates (5.5ms < tickrate <= 6ms) */
{1664, 32969}, /* Use for tickrates (6ms < tickrate <= 6.5ms) */
/*
* Transition from 7ms to just under 7ms is important because the API cadence
* time steps are 5ms. So at 7ms it starts to use a second tick to meet the
* time required. This has a significant impact on the desired step size
*/
{1791, 32985}, /* Use for tickrates (6.5ms < tickrate < 7ms) */
{1792, 32931}, /* Use for tickrates = 7ms */
{1920, 32942}, /* Use for tickrates (7ms < tickrate <= 7.5ms) */
{2048, 32954}, /* Use for tickrates (7.5ms < tickrate <= 8ms) */
{2176, 32966}, /* Use for tickrates (8ms < tickrate <= 8.5ms) */
{2304, 32977}, /* Use for tickrates (8.5ms < tickrate <= 9ms) */
{2432, 32989}, /* Use for tickrates (9ms < tickrate <= 9.5ms) */
{2560, 33000}, /* Use for tickrates (9.5ms < tickrate <= 10ms) */
{2688, 33012}, /* Use for tickrates (10ms < tickrate <= 10.5ms) */
{2816, 33024}, /* Use for tickrates (10.5ms < tickrate <= 11ms) */
{2944, 33035}, /* Use for tickrates (11ms < tickrate <= 11.5ms) */
{3072, 33047}, /* Use for tickrates (11.5ms < tickrate <= 12ms) */
{0xFFFF, 33224} /* Use for tickrates > 12ms */
};
for (loopCount = 0; loopCount < NTT_HOWLER_LUT_MAX; loopCount++) {
if (tickRate <= levelStepLut[loopCount][0]) {
cadence->levelStep = levelStepLut[loopCount][1];
break;
}
}
}
break;
default:
returnValue = FALSE;
break;
}
/*
* Configure the starting direction for frequency changes. The Howler state machine uses the
* transition from frequency decrease to frequency increase as the indicator for end of sweep.
* This is the point in UK and AUS Howler Tones where the level is increased (doesn't matter
* for NTT). So if this is set incorrectly, the level adjustments will be off by one full sweep.
*/
cadence->isFreqIncrease = TRUE;
VP_SEQUENCER(None, VP_NULL,
("Special Howler Paramaters: Freq Start: 0x%04X, Freq Stop: 0x%04X, Freq Step: 0x%04X",
cadence->startFreq, cadence->stopFreq, cadence->freqStep));
VP_SEQUENCER(None, VP_NULL,
("Special Howler Paramaters: Level Start: 0x%04X, Level Stop: 0x%04X, Level Step: 0x%04X",
cadence->startLevel, cadence->stopLevel, cadence->levelStep));
return returnValue;
}
/**
* VpCSLACComputeHowlerFreqStep()
* This is a helper function for VpCSLACHowlerInit() used to determine the frequency step value
* (used for UK and AUS Howler Tones) that will provide a sweep frequency duration of approximately
* that specified by "sweepInterval".
*/
void
VpCSLACComputeHowlerFreqStep(
uint32 sweepInterval, /**< INPUT: Specifies time to sweep from freqStart to freqStop */
VpSeqDataType *cadence, /**< I/O: Provides the start/stop frequencies as input. Fills in the
* frequency step parameter as output.
*/
uint16 updateInterval) /**< INPUT: Specifies the sequencer update rate (based on tickrate) */
{
uint16 numUpdateSteps = 0;
/*
* In case the sweep interval is not an exact multiple of the update interval, compute
* the error that will be used to adjust for the actual sweepInterval.
*/
uint16 stepError = (uint16)(sweepInterval % (uint32)updateInterval);
/*
* To offset the rounding down of the frequency steps below, always round down on the
* sweepInterval.
*/
sweepInterval -= stepError;
/* Computation for number of steps should now be an exact integer value.*/
numUpdateSteps = (uint16)(sweepInterval / (uint32)updateInterval);
VP_SEQUENCER(None, VP_NULL,
("Num Steps %d From sweepInteval 0x%08lX and updateInterval 0x%04X",
numUpdateSteps, sweepInterval, updateInterval));
/*
* Round down to the nearest frequency step. The total time should be around nonminal
* since we rounded down the sweep interval as well.
*/
stepError = ((cadence->stopFreq - cadence->startFreq) % numUpdateSteps);
VP_SEQUENCER(None, VP_NULL,
("Frequency Step Error 0x%04X",
((cadence->stopFreq - cadence->startFreq) % numUpdateSteps)));
cadence->freqStep = ((cadence->stopFreq - cadence->startFreq - stepError) / numUpdateSteps);
}
/**
* VpDecimalMultiply()
* This function returns the result of: (value * byteMask) where: value is in 16-bit format, and
* byteMask is the 1.15-bit multiplier. It is a helper function for VE880 and VE890 used in case
* of generating Special Howler Tones. These tones are special in that the levels of these tones
* increase "regularly" throughout the tone generation sequence.
*
* Input arguements are:
*
* value (16.0-bit format)
* bitMask (1.15-bit format)
*
* where:
* byteMask" comes from the "levelStep" cadence value initialized in CSLACHowlerInit()
* "value" comes from the silicon Signal Generator
*/
uint16 /**< Result of value * bitMask (note the bit representations) */
VpDecimalMultiply(
uint16 value, /**< INPUT: First multiplication value in 16.0-bit format */
uint16 byteMask) /**< INPUT: Second multiplication value in 1.15-bit format */
{
uint32 multiplyResult = (value * byteMask);
uint16 errorResult = (multiplyResult % 32768);
VP_SEQUENCER(None, VP_NULL,
("Converting 0x%04X times byteMask 0x%04X (1.15 format)", value, byteMask));
/* Scale the 16 x 16 result to 1.15 format */
multiplyResult = (multiplyResult / 32768);
/* Round off */
if (errorResult > 0x4000) {
multiplyResult+=1;
}
VP_SEQUENCER(None, VP_NULL,
("Result of 0x%04X times byteMask 0x%04X (1.15 format) with error (0x%04X) is 0x%08lX",
value, byteMask, errorResult, multiplyResult));
return (uint16)multiplyResult;
}
/**
* VpCSLACProcessRampGenerators()
* This function manages the tone generators for the SPecial Howler Tones in the VE880/890 API.
* The special howler tones are those that ramp the frequency, amplitude, or both.
*/
bool
VpCSLACProcessRampGenerators(
VpSeqDataType *cadence)
{
uint16 tempLevel;
bool freqCycleComplete = FALSE;
bool updateLevel = FALSE;
bool updateFreq = FALSE;
if (cadence->freqStep != 0) {
uint16 tempFreq = cadence->regData[3];
tempFreq = ((tempFreq << 8) & 0xFF00);
tempFreq |= cadence->regData[4];
/*
* Non-Zero frequency steps means we're always changing the frequency. Set
* flag to indicate that the Signal Generator WILL be updated at the end of
* this case condition.
*/
updateFreq = TRUE;
if (cadence->isFreqIncrease == TRUE) {
/* Check if we're about to exceed the max frequency */
if ((tempFreq + cadence->freqStep) > cadence->stopFreq) {
tempFreq = cadence->stopFreq;
cadence->isFreqIncrease = FALSE;
} else {
tempFreq += cadence->freqStep;
}
} else {
/* Check if we're about to exceed the min frequency */
if ((tempFreq - cadence->freqStep) < cadence->startFreq) {
tempFreq = cadence->startFreq;
cadence->isFreqIncrease = TRUE;
freqCycleComplete = TRUE; /* Indicate that a full cycle has completed */
} else {
tempFreq -= cadence->freqStep;
}
}
cadence->regData[3] = (tempFreq >> 8) & 0xFF;
cadence->regData[4] = tempFreq & 0xFF;
}
/* Start work on the level adjustments required -- if any */
tempLevel = (cadence->regData[5] << 8);
tempLevel |= cadence->regData[6];
/*
* Criteria 1 for making a level increase: Current levels in the signal generator
* must be less than the specified maximum level. Also, the value of the level step
* must be non-zero.
*/
if ((tempLevel < cadence->stopLevel) && (cadence->levelStep > 0)) {
/* Just because we're not at max AND have a tone that specifies a level
* increase at some point in the sequence, doesn't mean it's ready to be
* adjusted. For UK and AUS Howler Tones the level adjustments occur only at
* the 1 second frequency sweep points. For all other tones, the level steps
* occur everytime this operation is performed.
*/
if ((cadence->toneType == VP_CSLAC_HOWLER_TONE) ||
(cadence->toneType == VP_CSLAC_AUS_HOWLER_TONE)) {
/*
* For UK and AUS Howler Tones, update when a frequency sweep cycle has
* just been completed.
*/
updateLevel = freqCycleComplete;
} else { /* cadence->toneType == VP_CSLAC_NTT_HOWLER_TONE and all other */
/*
* NTT uses a fixed frequency with Linear level increases. Update every
* chance we get. For all other tones where we don't know any better, level
* step is applied at every command step.
*/
updateLevel = TRUE;
}
/*
* If making an update, compute the new value and make sure not to exceed the
* maximum level specified.
*/
if (updateLevel) {
tempLevel = VpDecimalMultiply(tempLevel, cadence->levelStep);
if (tempLevel > cadence->stopLevel) {
/* We're at the max, no updates required */
cadence->levelStep = 0;
tempLevel = cadence->stopLevel;
}
cadence->regData[5] = (tempLevel >> 8) & 0xFF;
cadence->regData[6] = tempLevel & 0xFF;
}
}
if ((updateLevel) || (updateFreq)) {
return TRUE;
} else {
return FALSE;
}
}
#endif
#if defined (VP_CC_790_SERIES) || \
(defined (VP_CC_880_SERIES) && defined (VP880_FXS_SUPPORT)) || \
(defined (VP_CC_890_SERIES) && defined (VP890_FXS_SUPPORT))
/**
* VpCSLACInitCidStruct()
* This function initializes the Caller ID structure to start CID.
*
* Preconditions:
* None.
*
* Postconditions:
* The caller ID struct is initialized. Caller ID will start on the next tick.
*/
void
VpCSLACInitCidStruct(
VpCallerIdType *pCidStruct,
uint8 sequenceData)
{
pCidStruct->status |= VP_CID_IN_PROGRESS;
if ((sequenceData & VP_SEQ_SUBTYPE_MASK) == VP_SEQ_SUBCMD_WAIT_ON) {
pCidStruct->status |= VP_CID_WAIT_ON_ACTIVE;
}
pCidStruct->cliTimer = 1;
pCidStruct->cliIndex = 0;
pCidStruct->cliMPIndex = 0;
pCidStruct->cliMSIndex = 0;
pCidStruct->status |= VP_CID_PRIMARY_IN_USE;
pCidStruct->status &=
(uint16)(~(VP_CID_FSK_GEN_VALID | VP_CID_TERM_FSK | VP_CID_SECONDARY_IN_USE));
pCidStruct->currentData = VP_FSK_NONE;
}
/**
* VpCidSeq()
* This function services an active Caller ID Timer. This function runs when
* the CLI timer for the passed channel is active. The function reads the
* current caller ID sequence that is pointed to in the caller ID structure for
* the given channel. This pointer is assigned when ever the function
* CliStartCli is called. Additionally, the CliStartCli function sets the timer
* to 1 to seed the CLI process.
*
* This routine is broken up into two functional stages. The first stage
* handles CLI tasks that are not time related while the second stage handles
* time related task. Non-time related tasks include things such as muting a
* channel, pol-rev and EOT (end of transmission). Time related tasks include
* timing of the MARK signal, the SEIZURE signal and ACK detection as well as
* timing for sending encoded data bytes to the device FSK generator.
*
* Preconditions:
* This Function must be called from the ApiTick function.
*
* Postconditions:
* The Caller ID State Machine is updated. Returns TRUE, if a user defined
* event was encountered
*/
VpStatusType
VpCidSeq(
VpLineCtxType *pLineCtx) /**< Line that has an active CID sequence */
{
VpStatusType retFlag = VP_STATUS_SUCCESS;
VpDevCtxType *pDevCtx = pLineCtx->pDevCtx;
VpDeviceType deviceType = pDevCtx->deviceType;
VpSetLineStateFuncPtrType SetLineState;
VpDeviceIdType deviceId;
uint16 tickRate;
uint8 ecVal;
uint8 indexVal; /* Use this when several index vals are needed */
void *pLineObj = pLineCtx->pLineObj;
void *pDevObj = pDevCtx->pDevObj;
VpCallerIdType *pCidStruct;
VpLineStateType lineState;
VpDigitType digit;
uint16 uiCliOpCode;
uint8 startOfCliData, mpiLen;
uint16 cliTimer = 0;
uint16 tempDebounceTime = 0;
uint16 index;
uint8 scratchData[1];
#if (VP_CC_DEBUG_SELECT & VP_DBG_CID)
uint16 timeStamp = 0;
#endif
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("VpCidSeq()+"));
switch (deviceType) {
#if defined (VP_CC_790_SERIES)
case VP_DEV_790_SERIES:
SetLineState = Vp790SetLineStateInt;
lineState = ((Vp790LineObjectType *)pLineObj)->lineState.currentState;
pCidStruct = &((Vp790LineObjectType *)pLineObj)->callerId;
if (!(((Vp790DeviceObjectType *)pDevObj)->status.state & VP_DEV_INIT_CMP)) {
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("VpCidSeq()-"));
return VP_STATUS_DEV_NOT_INITIALIZED;
}
tickRate =
((Vp790DeviceObjectType *)pDevObj)->devProfileData.tickRate;
deviceId = ((Vp790DeviceObjectType *)pDevObj)->deviceId;
#if (VP_CC_DEBUG_SELECT & VP_DBG_CID)
timeStamp = ((Vp790DeviceObjectType *)pDevObj)->timeStamp;
#endif
switch(((Vp790LineObjectType *)pLineObj)->channelId) {
case 0: ecVal = VP790_EC_CH1; break;
case 1: ecVal = VP790_EC_CH2; break;
case 2: ecVal = VP790_EC_CH3; break;
case 3: ecVal = VP790_EC_CH4; break;
default:
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("VpCidSeq()-"));
return VP_STATUS_FAILURE;
}
break;
#endif
#if defined (VP_CC_880_SERIES) && defined (VP880_FXS_SUPPORT)
case VP_DEV_880_SERIES:
SetLineState = Vp880SetLineStateInt;
lineState = ((Vp880LineObjectType *)pLineObj)->lineState.currentState;
pCidStruct = &((Vp880LineObjectType *)pLineObj)->callerId;
if (!(((Vp880DeviceObjectType *)pDevObj)->state & VP_DEV_INIT_CMP)) {
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("VpCidSeq()-"));
return VP_STATUS_DEV_NOT_INITIALIZED;
}
/*
* Do not proceed if the device calibration is in progress. This could
* damage the device.
*/
if (((Vp880DeviceObjectType *)pDevObj)->state & VP_DEV_IN_CAL) {
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("VpCidSeq()-"));
return VP_STATUS_DEV_NOT_INITIALIZED;
}
tickRate =
((Vp880DeviceObjectType *)pDevObj)->devProfileData.tickRate;
#if (VP_CC_DEBUG_SELECT & VP_DBG_CID)
timeStamp = ((Vp880DeviceObjectType *)pDevObj)->timeStamp;
#endif
deviceId = ((Vp880DeviceObjectType *)pDevObj)->deviceId;
ecVal = ((Vp880LineObjectType *)pLineObj)->ecVal;
break;
#endif
default:
return VP_STATUS_INVALID_ARG;
}
/* Determine if the timer is running. */
if(pCidStruct->cliTimer > 0) {
pCidStruct->cliTimer--;
if (pCidStruct->status & VP_CID_REPEAT_MSG) {
if(VpFSKGeneratorReady(pLineCtx)) {
while(VpCtrlSetFSKGen(pLineCtx, VP_CID_GENERATOR_KEYED_CHAR,
pCidStruct->currentData) != 0);
};
}
if(pCidStruct->cliTimer != 0) {
VP_CID(VpLineCtxType, pLineCtx, ("1. VpCidSeq() -- Running Timer %d at time %d",
pCidStruct->cliTimer, timeStamp));
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("VpCidSeq()-"));
return VP_STATUS_SUCCESS;
} else {
/*
* The CLI tone generators are enabled for Alerting Tone which uses
* the caller id timer to control on-time (as opposed to using the
* sequencer timers). So we need to disable these generators at the
* end of Alterting tone time. For both VE880 and VE890, if the
* Alerting Tone generators were previously disabled the silicon is
* not accessed by this function. The required values are cached in
* the line objects. Only for VE790 will a read occur, but a write
* will not if the "previous" and "new" control values are the same.
*/
VpCtrlSetCliTone(pLineCtx, FALSE);
}
} else {
VP_CID(VpLineCtxType, pLineCtx,
("VpCidSeq() -- Timer NOT running at time %d", timeStamp));
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("VpCidSeq()-"));
}
pCidStruct->status &= ~VP_CID_REPEAT_MSG;
/*
* Find where the start of the CLI command data is (excluding the MPI
* command/data used to set the tone generator(s)
*/
mpiLen = pCidStruct->pCliProfile[VP_CID_PROFILE_FSK_PARAM_LEN];
/*
* Start of CLI commands on the LSB (word aligned). Exact location found
* by adding the start elements offset to the end of the mpi command data
* (found from the location of the mpi command length + the actual length
* of the mpi data).
*/
startOfCliData = VP_CID_PROFILE_FSK_PARAM_LEN + mpiLen +
VP_CID_PROFILE_START_OF_ELEMENTS_LSB;
/* Get the current index for the CLI profile. */
index = pCidStruct->cliIndex;
pCidStruct->cliDebounceTime = 0;
/*
* This section of code tests to see if a CPE ACK was received.
* If the variable cliAwaitTone is TRUE, then test to see if the ACK
* was received prior to the timeout specified in the CLI_DETECT portion
* of the CLI profile. If the ACK was not received, you can not send the
* CID information so terminate the CLI sequence.
*/
if (pCidStruct->status & VP_CID_AWAIT_TONE) {
VpCtrlDetectDTMF(pLineCtx, FALSE);
/*
* This would have been set to VP_DIG_NONE prior to starting the DTMF
* digit detection. So any other value (whether in make or break
* interval) is indication of DTMF digit detected during detection
* interval.
*/
digit = pCidStruct->digitDet;
if ((digit == pCidStruct->cliDetectTone1)
|| (digit == pCidStruct->cliDetectTone2)) {
/* The ACK tone was detected, continue with Caller ID */
} else {
/* Ack tone not detected, stop Caller ID */
VpCliStopCli(pLineCtx);
VP_CID(VpLineCtxType, pLineCtx, ("Ack Tone Not Detected"));
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("VpCidSeq()-"));
return VP_STATUS_SUCCESS;
}
}
/*
* If previously started a termination sequence, see if the FSK generator
* is needed for the next step before disabling.
*/
if (pCidStruct->status & VP_CID_TERM_FSK) {
pCidStruct->status &= ~VP_CID_TERM_FSK;
pCidStruct->cliTimer = MS_TO_TICKRATE((tickRate >> 8), tickRate);
switch(pCidStruct->pCliProfile[startOfCliData + index]) {
case VP_CLI_MESSAGE:
case VP_CLI_CHANSEIZURE:
case VP_CLI_MARKSIGNAL:
break;
default:
VP_CID(VpLineCtxType, pLineCtx,
("VpCidSeq(): Timeout complete. Terminating FSK at time %d", timeStamp));
VpCtrlSetFSKGen(pLineCtx, VP_CID_SIGGEN_EOT, 1);
break;
}
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("VpCidSeq()-"));
return VP_STATUS_SUCCESS;
}
/*
* Handle zero-time-length codes. Zero time codes are op-codes in the
* profile that are executed but have no time associated with them. In
* other words the CLI sequence immediately moves on to the next state.
* they include POL-REV, Channel mute, and EOT. The while loop churns
* through the profile until a time related element is encountered.
*/
uiCliOpCode = pCidStruct->pCliProfile[startOfCliData + index];
VP_CID(VpLineCtxType, pLineCtx, ("CID Op-Code 0x%02X at index %d time %d",
uiCliOpCode, index, timeStamp));
while ( (index <= pCidStruct->pCliProfile[startOfCliData - 2]) &&
( (uiCliOpCode == VP_CLI_POLREV) ||
(uiCliOpCode == VP_CLI_EOT) ||
(uiCliOpCode == VP_CLI_MUTEON) ||
(uiCliOpCode == VP_CLI_MUTEOFF)) ) {
switch (uiCliOpCode) {
/* Mute both the upstream and down stream transmission paths. */
case VP_CLI_MUTEON:
VP_CID(VpLineCtxType, pLineCtx, ("CID Op-Code VP_CLI_MUTEON"));
index+=2;
pCidStruct->status |= VP_CID_MUTE_ON;
VpCtrlMuteChannel(pLineCtx, TRUE);
break;
/*
* Re-enable audio transmission in both the upstream and downstream
* directions.
*/
case VP_CLI_MUTEOFF:
VP_CID(VpLineCtxType, pLineCtx, ("CID Op-Code VP_CLI_MUTEOFF"));
index+=2;
pCidStruct->status &= ~(VP_CID_MUTE_ON);
VpCtrlMuteChannel(pLineCtx, FALSE);
break;
/* Invert the polarity of the line. */
case VP_CLI_POLREV:
VP_CID(VpLineCtxType, pLineCtx, ("CID Op-Code VP_CLI_POLREV"));
index+=2;
/*
* VpGetReverseState() returns the reversal polarity equivalent of the state
* passe if it recognizes it, or returns the same state passed if it does not
* recognize it.
*/
SetLineState(pLineCtx, VpGetReverseState(lineState));
break;
/* Indicates the End Of Transmission for the CLI sequence */
case VP_CLI_EOT:
VP_CID(VpLineCtxType, pLineCtx, ("CID Op-Code VP_CLI_EOT at time %d",
timeStamp));
VpCliStopCli(pLineCtx);
return VP_STATUS_SUCCESS;
default:
index+=2;
break;
}
uiCliOpCode = pCidStruct->pCliProfile[startOfCliData + index];
}
/*
* Process all time based CLI profile codes. This includes timing of
* channel seizure, ACK detect, MARK, and the message data.
*/
if (index <= pCidStruct->pCliProfile[startOfCliData - 2]) {
/* Switch on CLI Profile Element type at the current index. */
switch (pCidStruct->pCliProfile[startOfCliData + index]) {
/*
* Set up the CLI sequence for detection of the CPE ACK. This state
* will stop any running sequence, disable the sig gen, stop any
* FSK activity, and set a time out for the tone detection. If
* the time-out time is reached, the CPE did not ACK and the CLI
* sequence will be aborted.
*/
case VP_CLI_DETECT:
VP_CID(VpLineCtxType, pLineCtx, ("CID Op-Code VP_CLI_DETECT"));
/* Turn off the tone generator and turn FSK off */
VpSetLineTone(pLineCtx, VP_PTABLE_NULL, VP_PTABLE_NULL, VP_NULL);
/* Read the timeout time */
index++;
cliTimer = pCidStruct->pCliProfile[startOfCliData + index];
cliTimer = ((cliTimer << 8) & 0xFF00);
index++;
cliTimer |=
(pCidStruct->pCliProfile[startOfCliData + index] & 0x00FF);
cliTimer *= VP_CID_TIMESCALE;
/* Read which tones to detect. */
index+=2;
/*
* The data from profile wizard can be shifted 4 right and be
* interpreted directly as a VpDigitType (other than 0xFF for
* Digit Type None)
*/
pCidStruct->cliDetectTone1 =
(VpDigitType)(pCidStruct->pCliProfile[startOfCliData + index]);
if (pCidStruct->cliDetectTone1 != VP_DIG_NONE) {
pCidStruct->cliDetectTone1 =
(VpDigitType)((pCidStruct->cliDetectTone1 >> 4) & 0xFF);
}
if (VpIsDigit(pCidStruct->cliDetectTone1) == FALSE) {
VpCliStopCli(pLineCtx);
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("VpCidSeq()-"));
return VP_STATUS_INVALID_ARG;
}
index+=2;
pCidStruct->cliDetectTone2 =
(VpDigitType)(pCidStruct->pCliProfile[startOfCliData + index]);
if (pCidStruct->cliDetectTone2 != VP_DIG_NONE) {
pCidStruct->cliDetectTone2 =
(VpDigitType)((pCidStruct->cliDetectTone2 >> 4) & 0xFF);
}
if (VpIsDigit(pCidStruct->cliDetectTone2) == FALSE) {
VpCliStopCli(pLineCtx);
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("VpCidSeq()-"));
return VP_STATUS_INVALID_ARG;
}
/* Start the tone detector. */
index+=2;
VpCtrlDetectDTMF(pLineCtx, TRUE);
break;
/*
* This case sets up the signal generator to generate tones that
* are defined in the profile. This will include things like the call
* waiting beep. This case does not actually start the signal generator
* it only sets it up.
*/
case VP_CLI_ALERTTONE:
VP_CID(VpLineCtxType, pLineCtx, ("CID Op-Code VP_CLI_ALERTTONE"));
/* Stop any running sequences and disable any FSK activity. */
VpSetLineTone(pLineCtx, VP_PTABLE_NULL, VP_PTABLE_NULL, VP_NULL);
/*
* Set the timer to return as soon as possible before enabling the
* tone generator.
*/
index++; /* Get to the length of MPI data to send */
cliTimer = (tickRate >> 8);
/*
* Send the MPI data to the device starting at the point after the
* length of data field
*/
indexVal = startOfCliData + index + 1;
VpMpiCmdWrapper(deviceId, ecVal, NOOP_CMD,
pCidStruct->pCliProfile[startOfCliData + index],
(VpProfileDataType *)(&pCidStruct->pCliProfile[indexVal]));
/*
* We don't know if the previous command modified the FSK
* Generator. To be safe, assume that it DID affect it. Next
* time the FSK generator is required, clearing this flag will
* force it to be reprogrammed.
*/
pCidStruct->status &= ~VP_CID_FSK_GEN_VALID;
/* Get to the next command after the MPI data */
index += pCidStruct->pCliProfile[startOfCliData + index];
index += 2;
break;
/*
* This case starts the signal generator for the time specified in
* the CLI profile. It is assumed the signal generator was set up
* first in the previous case.
*/
case VP_CLI_ALERTTONE2:
VP_CID(VpLineCtxType, pLineCtx, ("CID Op-Code VP_CLI_ALERTTONE2"));
VpCtrlSetCliTone(pLineCtx, TRUE);
index++;
cliTimer = pCidStruct->pCliProfile[startOfCliData + index];
cliTimer = ((cliTimer << 8) & 0xFF00);
index++;
cliTimer |=
(pCidStruct->pCliProfile[startOfCliData + index] & 0x00FF);
cliTimer *= VP_CID_TIMESCALE;
index+=2;
break;
/*
* This case creates a silent period of the time specified in the
* profile. This is done by disabling the signal generator and
* FSK generator.
*/
case VP_CLI_SILENCE:
VP_CID(VpLineCtxType, pLineCtx, ("CID Op-Code VP_CLI_SILENCE"));
VpSetLineTone(pLineCtx, VP_PTABLE_NULL, VP_PTABLE_NULL, VP_NULL);
index++;
cliTimer = pCidStruct->pCliProfile[startOfCliData + index];
cliTimer = ((cliTimer << 8) & 0xFF00);
index++;
cliTimer |=
(pCidStruct->pCliProfile[startOfCliData + index] & 0x00FF);
cliTimer *= VP_CID_TIMESCALE;
index+=2;
break;
/*
* This case creates a silent period and prevents hook switch from
* being detected for cliDebouncing time. Currently the debounce
* period does nothing and should be enabled by creating a mask hook
* method in the LIU.
*/
case VP_CLI_SILENCE_MASKHOOK:
VP_CID(VpLineCtxType, pLineCtx, ("CID Op-Code VP_CLI_SILENCE_MASKHOOK"));
VpSetLineTone(pLineCtx, VP_PTABLE_NULL, VP_PTABLE_NULL, VP_NULL);
index++;
cliTimer = pCidStruct->pCliProfile[startOfCliData + index];
cliTimer = ((cliTimer << 8) & 0xFF00);
index++;
cliTimer |= (pCidStruct->pCliProfile[startOfCliData + index] & 0x00FF);
cliTimer *= VP_CID_TIMESCALE;
index++;
tempDebounceTime = (pCidStruct->pCliProfile[startOfCliData + index] << 8) & 0xFF00;
index++;
tempDebounceTime |= (pCidStruct->pCliProfile[startOfCliData + index] & 0x00FF);
tempDebounceTime *= VP_CID_TIMESCALE;
pCidStruct->cliDebounceTime = MS_TO_TICKRATE(tempDebounceTime, tickRate);
index+=2;
break;
/*
* This case creates the channel seizure or mark signal for the time
* specified. Channel Siezure is alternating 1/0 (0x55) so the
* start bit has to be set = 1, stop bit set = 0. Mark signal is
* all '1's so start bit = stop bit = 1.
*/
case VP_CLI_CHANSEIZURE:
case VP_CLI_MARKSIGNAL: {
uint8 signalData;
uint8 maxCount = 0;
if (pCidStruct->pCliProfile[startOfCliData + index] == VP_CLI_CHANSEIZURE) {
signalData = VP_FSK_CHAN_SEIZURE;
} else {
signalData = VP_FSK_MARK_SIGNAL;
}
VP_CID(VpLineCtxType, pLineCtx, ("CID Op-Code %s",
((signalData == VP_FSK_CHAN_SEIZURE) ? "VP_CLI_CHANSEIZURE" : "VP_CLI_MARKSIGNAL")));
/*
* See if the FSK generator is programmed correctly. If not,
* program based on data from the profile. Note: If not set,
* it is likely because this is the first time programming it
* for the current Caller ID profile OR the generator was
* possibly reprogrammed for Alterting Tone.
*/
if (!(pCidStruct->status & VP_CID_FSK_GEN_VALID)) {
VP_CID(VpLineCtxType, pLineCtx,
("FSK Generator Programming Required"));
indexVal = VP_CID_PROFILE_FSK_PARAM_LEN + 1;
VpMpiCmdWrapper(deviceId, ecVal, NOOP_CMD,
pCidStruct->pCliProfile[VP_CID_PROFILE_FSK_PARAM_LEN],
(VpProfileDataType *)(&pCidStruct->pCliProfile[indexVal]));
pCidStruct->status |= VP_CID_FSK_GEN_VALID;
}
pCidStruct->markOutByteCount = 0;
if (VpFSKGeneratorReady(pLineCtx)) {
maxCount = 1;
while(VpCtrlSetFSKGen(pLineCtx, VP_CID_GENERATOR_KEYED_CHAR, signalData) != 0) {
maxCount++;
}
}
VP_CID(VpLineCtxType, pLineCtx,
("Provided %d bytes of Keyed Character to FSK Generator",
maxCount));
pCidStruct->currentData = signalData;
pCidStruct->status |= VP_CID_REPEAT_MSG;
index++;
cliTimer = pCidStruct->pCliProfile[startOfCliData + index];
cliTimer = ((cliTimer << 8) & 0xFF00);
index++;
cliTimer |=
(pCidStruct->pCliProfile[startOfCliData + index] & 0x00FF);
cliTimer *= VP_CID_TIMESCALE;
/*
* cliTimer is now in ms. For low values, subtract off the
* time provided in the caller id data already loaded in the
* buffer to improve accuracy. High values (>300ms) are not
* generally used, and the accuracy is far less important.
* NOTE: Caller ID timescale is in ms and one byte is 8.33ms
*/
if ((cliTimer / 100) <= 655) {
if ((cliTimer * 100) < (833 * maxCount)) {
cliTimer = 0;
} else {
cliTimer = ((cliTimer * 100) - (833 * maxCount));
cliTimer /= 100;
}
}
index+=2;
pCidStruct->status |= VP_CID_TERM_FSK;
VP_CID(VpLineCtxType, pLineCtx,
("Setting Keyed Character timer to %d ms", cliTimer));
}
break;
/* This case sends the actual message data (FSK Format). */
case VP_CLI_MESSAGE: {
bool startEndFsk = FALSE;
uint8 dataRemain;
VP_CID(VpLineCtxType, pLineCtx, ("CID Op-Code VP_CLI_MESSAGE"));
/* If the FSK Generator was not setup before, set it now */
if (!(pCidStruct->status & VP_CID_FSK_GEN_VALID)) {
indexVal = VP_CID_PROFILE_FSK_PARAM_LEN + 1;
VpMpiCmdWrapper(deviceId, ecVal, NOOP_CMD,
pCidStruct->pCliProfile[VP_CID_PROFILE_FSK_PARAM_LEN],
(VpProfileDataType *)(&pCidStruct->pCliProfile[indexVal]));
pCidStruct->status |= VP_CID_FSK_GEN_VALID;
}
/*
* Limit the number of bytes we'll try to provide the device in
* order to avoid infinite looping. Infinite looping can only
* occur if the device is not responding to CID message data
* buffer (fill) commands OR if the VP-API-II cannot read from
* the device and is simply confused. The message data buffer
* won't respond in certain SLIC feed states - so this condition
* is certainly possible.
*/
if (VpFSKGeneratorReady(pLineCtx)) {
VpCliEncodedDataType encodeDataType;
dataRemain = 1;
while((dataRemain > 0) && (startEndFsk == FALSE)) {
/*
* For legacy reasons all CID commands are word aligned but
* none actually use the upper byte (always set to 0x00 by
* Profile Wizard). The FSK message type is expanded to
* support a Mark-Out signal by setting this byte to the
* number of Mark-Out bytes after FSK message data (and
* checksum) required. This makes it 100% compatible with
* legacy profiles since 0 means no mark-out required.
*/
pCidStruct->markOutByteCount =
pCidStruct->pCliProfile[startOfCliData + index - 1];
encodeDataType = VpCliGetEncodedByte(pLineCtx, scratchData);
VP_CID(VpLineCtxType, pLineCtx,
("VpCidSeq(): EncodedData (%s) Mark-Out Length (%d) Mark-Out Remain (%d) at time %d",
((encodeDataType == VP_CLI_ENCODE_DATA) ? "VP_CLI_ENCODE_DATA" :
((encodeDataType == VP_CLI_ENCODE_MARKOUT) ? "VP_CLI_ENCODE_MARKOUT" : "VP_CLI_ENCODE_END")),
pCidStruct->markOutByteCount,
pCidStruct->markOutByteRemain, timeStamp));
/* Determine (and send) next data/data type in the buffer */
if (encodeDataType == VP_CLI_ENCODE_DATA) {
/*
* The device level APIs at this point "know" if the
* this is the last byte being sent. If it is, these
* APIs (SetFSKGen) will start the End-Of-Message
* Sequence which creates a potential gap in the FSK
* signal. In case of providing a Mark-Out signal this
* gap has to be avoided, hence the EOM sequence has to
* be stopped before this function call. Logic in
* "Encode Byte" is where the message buffer is being
* evaluated and therefore is where this check must be
* performed.
*/
pCidStruct->currentData = VP_FSK_DATA;
dataRemain = VpCtrlSetFSKGen(pLineCtx, VP_CID_GENERATOR_DATA,
scratchData[0]);
} else if ((encodeDataType == VP_CLI_ENCODE_MARKOUT)
&& (pCidStruct->markOutByteRemain > 0)) {
/*
* Note that the "currentData" type is changed AFTER
* calling SetFSKGen(). This is to trigger the first
* time the Mark Signal is being sent to copy the
* markOutByteCount value to markOutByteRemain. If
* this value is already set to Mark Signal prior to
* this function, it only decrements markOutByteRemain
* until = 0.
*/
VP_CID(VpLineCtxType, pLineCtx,
("VpCidSeq(): Running Mark-Out (%d) with Remaining (%d) at time %d",
pCidStruct->markOutByteCount,
pCidStruct->markOutByteRemain, timeStamp));
dataRemain = VpCtrlSetFSKGen(pLineCtx, VP_CID_GENERATOR_KEYED_CHAR,
VP_FSK_MARK_SIGNAL);
pCidStruct->currentData = VP_FSK_MARK_SIGNAL;
} else { /* VP_CLI_ENCODE_END */
startEndFsk = TRUE;
/*
* We're done with FSK Message Data, but may still need
* to keep the FSK Generator on. We can only know this
* by peeking at the next element.
*/
index+=2; /* This has to be done anyway... */
if (index <= pCidStruct->pCliProfile[startOfCliData - 2]) {
/*
* Peek at the next element. If FSK is NOT required,
* start the ending sequence.
*/
switch (pCidStruct->pCliProfile[startOfCliData + index]) {
case VP_CLI_MARKSIGNAL:
case VP_CLI_CHANSEIZURE:
case VP_CLI_MESSAGE:
startEndFsk = FALSE;
break;
default: /* FSK not required for next step */
break;
}
}
if (startEndFsk) {
/*
* Last value, '1' is a flag to the device specific
* API-II that this step can tolerate suspending CID
* until all generator data is sent. A '0' indicates
* that CID cannot be suspended.
*/
VP_CID(VpLineCtxType, pLineCtx,
("VpCidSeq(): Proceeding with normal FSK Message Termination at time %d",
timeStamp));
VpCtrlSetFSKGen(pLineCtx, VP_CID_SIGGEN_EOT, 1);
dataRemain = 0;
pCidStruct->currentData = VP_FSK_NONE;
} /* if (startEndFsk) */
} /* else VP_CLI_ENCODEE_END */
} /* while there is more data and NOT the start of FSK end */
} /* if FSK Generator is Ready */
/*
* Prevent the timer from running. If it times out, it will
* disable the tone generators (without added logic above to
* prevent it).
*/
cliTimer = 0;
}
break;
/* This case sends the actual message data (DTMF Format). */
case VP_CLI_DTMF_MESSAGE:
VP_CID(VpLineCtxType, pLineCtx, ("CID Op-Code VP_CLI_DTMF_MESSAGE"));
if (VpDTMFGeneratorReady(pLineCtx) == TRUE) {
/* Send next data in buffer */
/*
* Clear the markOutByteCount intended only to be used with
* FSK message data.
*/
pCidStruct->markOutByteCount = 0;
if(VpCliGetEncodedByte(pLineCtx, scratchData) == VP_CLI_ENCODE_DATA) {
VpCtrlSetDTMFGen(pLineCtx, VP_CID_GENERATOR_DATA,
VpConvertCharToDigitType(scratchData[0]));
} else {
/* We're done. Disable the Generators and go to the next element */
VpCtrlSetDTMFGen(pLineCtx, VP_CID_SIGGEN_EOT, VP_DIG_NONE);
index+=2;
}
}
/*
* Prevent the timer from running. If it times out, it will
* disable the tone generators (without added logic above to
* prevent it).
*/
cliTimer = 0;
break;
/* Shouldn't be possible */
default:
VP_CID(VpLineCtxType, pLineCtx, ("CID Op-Code ERROR"));
index = 0xFFFF; /* Force stop */
break;
} /* End of Switch (CLI Element Type) */
} /* if index <= number of elements */
VP_CID(VpLineCtxType, pLineCtx, ("Next Index Value %d for Total Length %d",
index, pCidStruct->pCliProfile[startOfCliData - 2]));
/*
* If the CLI sequencer has indexed passed the number of elements in the
* the profile, then stop the CLI sequence. This condition is true if
* the ACK was not received, the EOT marker in the profile was reached
* or an error occurred. Otherwise, continue normally.
*/
if (index > pCidStruct->pCliProfile[startOfCliData - 2]) {
VP_CID(VpLineCtxType, pLineCtx, ("Stopping CID"));
VpCliStopCli(pLineCtx);
} else {
pCidStruct->cliIndex = index;
pCidStruct->cliTimer = MS_TO_TICKRATE(cliTimer, tickRate);
}
VP_API_FUNC_INT(VpLineCtxType, pLineCtx, ("VpCidSeq()-"));
return retFlag;
}
/**
* VpFSKGeneratorReady()
* This function returns TRUE if the FSK Generator is ready to accept the next
* CID message byte, FALSE otherwise.
*
* Preconditions:
* None.
*
* Postconditions:
* None. Status of FSK Generator only is reported. Otherwise line is unaffected
*/
bool
VpFSKGeneratorReady(
VpLineCtxType *pLineCtx)
{
VpDevCtxType *pDevCtx = pLineCtx->pDevCtx;
VpDeviceType deviceType = pDevCtx->deviceType;
switch (deviceType) {
#if defined (VP_CC_790_SERIES)
case VP_DEV_790_SERIES:
return Vp790FSKGeneratorReady(pLineCtx);
#endif
#if defined (VP_CC_880_SERIES) && defined (VP880_FXS_SUPPORT)
case VP_DEV_880_SERIES:
return Vp880FSKGeneratorReady(pLineCtx);
#endif
default:
return FALSE;
}
}
/**
* VpDTMFGeneratorReady()
* This function returns TRUE if the DTMF Generator is ready to accept the next
* CID message byte, FALSE otherwise.
*
* Preconditions:
* None.
*
* Postconditions:
* None. Status of DTMF Generator only is reported. Otherwise line is unaffected
*/
bool
VpDTMFGeneratorReady(
VpLineCtxType *pLineCtx)
{
bool returnVal = TRUE;
#if defined (VP_CC_880_SERIES) || defined (VP_CC_890_SERIES)
VpDevCtxType *pDevCtx = pLineCtx->pDevCtx;
VpDeviceType deviceType = pDevCtx->deviceType;
uint16 tickRate;
VpCslacTimers *pLineTimers;
void *pLineObj = pLineCtx->pLineObj;
void *pDevObj = pDevCtx->pDevObj;
VpCallerIdType *pCidStruct = VP_NULL;
switch (deviceType) {
#ifdef VP_CC_880_SERIES
case VP_DEV_880_SERIES:
pLineTimers = &((Vp880LineObjectType *)pLineObj)->lineTimers.timers;
#if defined (VP880_FXS_SUPPORT)
pCidStruct = &((Vp880LineObjectType *)pLineObj)->callerId;
#endif
tickRate =
((Vp880DeviceObjectType *)pDevObj)->devProfileData.tickRate;
break;
#endif
default:
return TRUE;
}
if ((!(pLineTimers->timer[VP_LINE_TIMER_CID_DTMF] & VP_ACTIVATE_TIMER))&&
(pCidStruct != VP_NULL)) {
/*
* Timer appears available. If we're in an on-time already, change to
* the off-time and return FALSE (i.e., cannot program another DTMF
* number yet). If it's in an off-time, it's now complete so return
* TRUE.
*/
if (pCidStruct->dtmfStatus & VP_CID_ACTIVE_ON_TIME) {
pCidStruct->dtmfStatus &= ~VP_CID_ACTIVE_ON_TIME;
/* Setup the line timer for the off-time for DTMF CID */
pLineTimers->timer[VP_LINE_TIMER_CID_DTMF] =
MS_TO_TICKRATE(VP_CID_DTMF_OFF_TIME, tickRate);
pLineTimers->timer[VP_LINE_TIMER_CID_DTMF] |= VP_ACTIVATE_TIMER;
pCidStruct->dtmfStatus |= VP_CID_ACTIVE_OFF_TIME;
/* Disable the DTMF Generator */
VpCtrlSetDTMFGen(pLineCtx, VP_CID_SIGGEN_EOT, VP_DIG_NONE);
returnVal = FALSE;
} else if (pCidStruct->dtmfStatus & VP_CID_ACTIVE_OFF_TIME) {
pCidStruct->dtmfStatus &= ~VP_CID_ACTIVE_OFF_TIME;
/* Nothing more to do here */
returnVal = TRUE;
}
} else {
returnVal = FALSE;
}
#endif
return returnVal;
}
#if defined (VP_CC_790_SERIES) || (defined (VP_CC_880_SERIES) && defined (VP880_FXS_SUPPORT)) || \
(defined (VP_CC_890_SERIES) && defined (VP890_FXS_SUPPORT))
/**
* VpCliStopCli()
* This function stops the CLI sequence on the passed line.
*
* Preconditions
* None
*
* Postconditions
* The caller ID sequence, if running, will be aborted.
*/
void
VpCliStopCli(
VpLineCtxType *pLineCtx)
{
VpDevCtxType *pDevCtx = pLineCtx->pDevCtx;
VpDeviceType deviceType = pDevCtx->deviceType;
void *pLineObj = pLineCtx->pLineObj;
uint16 *pEventData;
VpOptionEventMaskType *pLineEvents;
VpCallerIdType *pCidStruct;
switch (deviceType) {
#if defined (VP_CC_790_SERIES)
case VP_DEV_790_SERIES:
pCidStruct = &((Vp790LineObjectType *)pLineObj)->callerId;
pCidStruct->cliTimer = 0;
Vp790SetLineTone(pLineCtx, VP_PTABLE_NULL, VP_PTABLE_NULL, VP_NULL);
pLineEvents = &((Vp790LineObjectType *)pLineObj)->lineEvents;
pEventData = &((Vp790LineObjectType *)pLineObj)->processData;
break;
#endif
#if defined (VP_CC_880_SERIES) && defined (VP880_FXS_SUPPORT)
case VP_DEV_880_SERIES:
pCidStruct = &((Vp880LineObjectType *)pLineObj)->callerId;
pCidStruct->cliTimer = 0;
Vp880SetLineTone(pLineCtx, VP_PTABLE_NULL, VP_PTABLE_NULL, VP_NULL);
pLineEvents = &((Vp880LineObjectType *)pLineObj)->lineEvents;
pEventData = &((Vp880LineObjectType *)pLineObj)->processData;
break;
#endif
default:
return;
}
if ((pCidStruct->status & VP_CID_FSK_GEN_VALID) == VP_CID_FSK_GEN_VALID) {
VpCtrlSetFSKGen(pLineCtx, VP_CID_SIGGEN_EOT, 1);
pCidStruct->status &= ~(VP_CID_FSK_GEN_VALID);
} else {
VpCtrlSetDTMFGen(pLineCtx, VP_CID_SIGGEN_EOT, VP_DIG_NONE);
}
VpCtrlDetectDTMF(pLineCtx, FALSE);
pCidStruct->status &= ~(VP_CID_MUTE_ON);
pCidStruct->currentData = VP_FSK_NONE;
/*
* This should be unnecessary because it's initialized in the SendCid() and InitCid()
* functions, but seems like a good/simple precautionary measure.
*/
pCidStruct->messageDataRemain = 0;
VpCtrlMuteChannel(pLineCtx, FALSE);
pCidStruct->status &= ~VP_CID_IN_PROGRESS;
pLineEvents->process |= VP_LINE_EVID_CID_DATA;
*pEventData = VP_CID_DATA_TX_DONE;
}
/**
* VpCliGetEncodedByte()
* 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
VpCliGetEncodedByte(
VpLineCtxType *pLineCtx,
uint8 *pByte)
{
VpDevCtxType *pDevCtx = pLineCtx->pDevCtx;
VpDeviceType deviceType = pDevCtx->deviceType;
switch (deviceType) {
#if defined (VP_CC_790_SERIES)
case VP_DEV_790_SERIES:
return Vp790CliGetEncodedByte(pLineCtx, pByte);
#endif
#if defined (VP_CC_880_SERIES) && defined (VP880_FXS_SUPPORT)
case VP_DEV_880_SERIES:
return Vp880CliGetEncodedByte(pLineCtx, pByte);
#endif
default:
return VP_CLI_ENCODE_END;
}
}
/**
* VpCSLACCliGetEncodedByte()
* This function provides the common code for VE880/890 silicon for managing
* the FSK message buffer.
*
* Preconditions
*
* Postconditions
*/
VpCliEncodedDataType
VpCSLACCliGetEncodedByte(
uint8 *pByte,
VpCallerIdType *pCidStruct,
uint16 *pProcessData,
VpOptionEventMaskType *pLineEvents,
uint8 checkSumIndex)
{
uint8 nextByte = '\0';
VpCliEncodedDataType returnStatus;
VP_API_FUNC_INT(None, VP_NULL, ("+VpCSLACCliGetEncodedByte()"));
/*
* This logic is entered when the API previously sent the checksum (which
* of course means it was not provided by the application). See if a
* mark-out signal is required. If not, end FSK Message Data.
*/
if (pCidStruct->status & VP_CID_MID_CHECKSUM) {
VP_CID(None, VP_NULL, ("Just previously sent Checksum"));
pCidStruct->status &= ~VP_CID_MID_CHECKSUM;
*pByte = '\0';
if (pCidStruct->markOutByteCount > 0) {
/*
* This means a mark-out signal is needed. If this is the first
* time knowing this, initialize the value used to keep track of
* the remaining number of mark bytes to send.
*/
if (pCidStruct->currentData == VP_FSK_DATA) {
/*
* This is the first time knowing Mark Out Signal is needed.
* Initialize the value used to keep track of the remaining
* number of mark bytes to send.
*
* Note that upon return from this function, currentData
* should be changed to MARK type assuming a mark signal is
* sent. That will avoid constant reset of this value. If
* any other type is sent - which would be an error - then
* currentData should be set to that type.
*/
VP_CID(None, VP_NULL, ("Entering Markout Period..."));
pCidStruct->markOutByteRemain = pCidStruct->markOutByteCount;
}
/* The last markout byte when exists is always the last byte */
if (pCidStruct->markOutByteRemain == 1) {
VP_CID(None, VP_NULL, ("1. Last Markout Byte being sent"));
pCidStruct->status |= VP_CID_END_OF_MSG;
}
returnStatus = VP_CLI_ENCODE_MARKOUT;
} else {
VP_CID(None, VP_NULL, ("FSK Message Data Complete"));
pCidStruct->status |= VP_CID_END_OF_MSG;
returnStatus = VP_CLI_ENCODE_END;
}
VP_API_FUNC_INT(None, VP_NULL, ("-VpCSLACCliGetEncodedByte()"));
return returnStatus;
}
/*
* This logic is entered regardless of where the checksum comes from and
* doesn't need to check for mark-out signal. That can be done outside this
* logic.
*/
/* Check to determine which buffer is in use to index the message data */
if (pCidStruct->status & VP_CID_PRIMARY_IN_USE) {
/*
* If the index is at the length of the buffer, we need to switch
* buffers if there is more data
*/
if (pCidStruct->cliMPIndex >= pCidStruct->primaryMsgLen) {
/*
* At the end of the Primary Buffer. Flag an event and indicate to
* the API that this buffer is no longer being used and we can
* accept more data
*/
pCidStruct->status &= ~VP_CID_PRIMARY_IN_USE;
pCidStruct->status &= ~VP_CID_PRIMARY_FULL;
/*
* The primary buffer is now empty. If the secondary buffer is full,
* continue sending CID data. If it is also empty, determine if we
* need to send the checksum.
*/
if (pCidStruct->status & VP_CID_SECONDARY_FULL) {
pLineEvents->process |= VP_LINE_EVID_CID_DATA;
*pProcessData = VP_CID_DATA_NEED_MORE_DATA;
pCidStruct->status |= VP_CID_SECONDARY_IN_USE;
pCidStruct->cliMSIndex = 1;
*pByte = pCidStruct->secondaryBuffer[0];
pCidStruct->messageDataRemain -= ((pCidStruct->messageDataRemain) ? 1 : 0);
nextByte = pCidStruct->secondaryBuffer[1];
VP_CID(None, VP_NULL,
("CSLAC FSK Buffer -- Switching to Secondary 0x%02X", *pByte));
} else {
/*
* Secondary buffer is emppty. If a checksum is required flag
* the API that the current data being sent is the checksum.
* Otherwise, prepare to end FSK message data.
*/
if (pCidStruct->pCliProfile[checkSumIndex]) {
*pByte = (uint8)(~pCidStruct->cidCheckSum + 1);
pCidStruct->status |= VP_CID_MID_CHECKSUM;
VP_CID(None, VP_NULL, ("1. Preparing Checksum 0x%02X", *pByte));
} else {
*pByte = '\0';
}
}
} else {
*pByte = pCidStruct->primaryBuffer[pCidStruct->cliMPIndex];
pCidStruct->messageDataRemain -= ((pCidStruct->messageDataRemain) ? 1 : 0);
/* Get the next byte to be sent after the current byte */
if ((pCidStruct->cliMPIndex+1) >= pCidStruct->primaryMsgLen) {
if (pCidStruct->status & VP_CID_SECONDARY_FULL) {
nextByte = pCidStruct->secondaryBuffer[0];
VP_CID(None, VP_NULL,
("CSLAC FSK Buffer -- From Secondary 0x%02X", *pByte));
}
} else {
nextByte = pCidStruct->primaryBuffer[pCidStruct->cliMPIndex+1];
VP_CID(None, VP_NULL,
("2. CSLAC FSK Buffer -- From Primary 0x%02X", *pByte));
}
}
pCidStruct->cliMPIndex++;
} else if (pCidStruct->status & VP_CID_SECONDARY_IN_USE) {
/*
* If the index is at the length of the buffer, we need to switch
* buffers if there is more data
*/
if (pCidStruct->cliMSIndex >= pCidStruct->secondaryMsgLen) {
/*
* At the end of the Secondary Buffer. Flag an event and indicate to
* the API that this buffer is no longer being used and is empty
*/
pLineEvents->process |= VP_LINE_EVID_CID_DATA;
*pProcessData = VP_CID_DATA_NEED_MORE_DATA;
pCidStruct->status &= ~VP_CID_SECONDARY_IN_USE;
pCidStruct->status &= ~VP_CID_SECONDARY_FULL;
if (pCidStruct->status & VP_CID_PRIMARY_FULL) {
pLineEvents->process |= VP_LINE_EVID_CID_DATA;
*pProcessData = VP_CID_DATA_NEED_MORE_DATA;
pCidStruct->status |= VP_CID_PRIMARY_IN_USE;
pCidStruct->cliMPIndex = 1;
*pByte = pCidStruct->primaryBuffer[0];
pCidStruct->messageDataRemain -= ((pCidStruct->messageDataRemain) ? 1 : 0);
nextByte = pCidStruct->primaryBuffer[1];
VP_CID(None, VP_NULL,
("CSLAC FSK Buffer -- Switching to Primary 0x%02X", *pByte));
} else {
/* There is no more data in either buffer */
/*
* If a checksum is required flag the API that the current data
* being sent is the checksum. Otherwise, prepare to end FSK
* message data.
*/
if (pCidStruct->pCliProfile[checkSumIndex]) {
*pByte = (uint8)(~pCidStruct->cidCheckSum + 1);
pCidStruct->status |= VP_CID_MID_CHECKSUM;
VP_CID(None, VP_NULL,
("2. Preparing Checksum 0x%02X", *pByte));
} else {
*pByte = '\0';
}
}
} else {
*pByte = pCidStruct->secondaryBuffer[pCidStruct->cliMSIndex];
pCidStruct->messageDataRemain -= ((pCidStruct->messageDataRemain) ? 1 : 0);
/* Get the next byte to be sent after the current byte */
if ((pCidStruct->cliMSIndex+1) >= pCidStruct->secondaryMsgLen) {
if (pCidStruct->status & VP_CID_PRIMARY_FULL) {
nextByte = pCidStruct->primaryBuffer[0];
VP_CID(None, VP_NULL,
("3. CSLAC FSK Buffer -- From Primary 0x%02X", *pByte));
}
} else {
nextByte = pCidStruct->secondaryBuffer[pCidStruct->cliMSIndex+1];
VP_CID(None, VP_NULL,
("2. CSLAC FSK Buffer -- From Secondary 0x%02X", *pByte));
}
}
pCidStruct->cliMSIndex++;
}
/*
* Buffers are no longer in use when all user message data has been provided
* to the silicon. Next can be the API provided checksum or Mark-Out signal
* as part of FSK Message Data. Step by step...
*/
if ((!(pCidStruct->status & VP_CID_PRIMARY_IN_USE))
&& (!(pCidStruct->status & VP_CID_SECONDARY_IN_USE))) {
/*
* We're here because the FSK message data buffers are empty. We don't
* yet know if the checksum is provided by the API or if we need to
* generate a mark-out.
*/
if (pCidStruct->status & VP_CID_MID_CHECKSUM) {
/*
* This means the API is supposed to provide the checksum AND it is
* now doing that. If we won't run Mark-Out this is the EOM.
*/
VP_API_FUNC_INT(None, VP_NULL, ("-VpCSLACCliGetEncodedByte()"));
/* Mark-Out will not be required. This is the EOM */
if (pCidStruct->markOutByteCount == 0) {
VP_CID(None, VP_NULL,
("3. Marking EOM with pByte 0x%02X", *pByte));
pCidStruct->status |= VP_CID_END_OF_MSG;
}
returnStatus = VP_CLI_ENCODE_DATA;
} else {
/*
* This means the API may or may not have provided the checksum, but
* in any case the checksum must have already been sent so we can
* check if a mark-out signal if needed.
*/
VP_CID(None, VP_NULL, ("Checking for MarkOut with Next Byte 0x%02X", nextByte));
if (pCidStruct->markOutByteCount > 0) {
/*
* This means a mark-out signal is needed. If this is the first
* time entering this transition, iniitialize the parameter used
* to keep track of the number of mark out bytes remaiming.
* Note that when this value == 1, it is the EOM byte.
*/
if (pCidStruct->currentData == VP_FSK_DATA) {
/* This is the first time entering this transition. */
/*
* Note the handshaking here - that upon return from this
* function, currentData should be changed to MARK type to
* prevent continued re-initialization of this parameter.
*/
VP_CID(None, VP_NULL, ("Starting Markout with Next Byte 0x%02X", nextByte));
pCidStruct->markOutByteRemain = pCidStruct->markOutByteCount;
returnStatus = VP_CLI_ENCODE_MARKOUT;
} else if (pCidStruct->markOutByteRemain >= 1) {
/*
* The last markout byte when exists is always when only 1 byte
* remains, even if only 1 byte was specified.
*/
if (pCidStruct->markOutByteRemain == 1) {
VP_CID(None, VP_NULL,
("2. Last Markout Byte being sent"));
pCidStruct->status |= VP_CID_END_OF_MSG;
}
returnStatus = VP_CLI_ENCODE_MARKOUT;
} else {
returnStatus = VP_CLI_ENCODE_END;
}
} else {
/*
* This means a mark-out signal is NOT needed so we can end FSK
* transmission now.
*/
returnStatus = VP_CLI_ENCODE_END;
}
}
VP_API_FUNC_INT(None, VP_NULL, ("-VpCSLACCliGetEncodedByte()"));
return returnStatus;
/*
* In the following cases, a message byte is being sent but detect whether
* this is the last byte or not. If it's the last byte, flag EOM in the line
* object which will cause SetFSKGen function to set the device EOM bit.
* If we don't do this, a mark-byte will be added to the end of the message
* that was not specified.
*/
/* No More Bytes in Message Buffers -- */
} else if ((pCidStruct->messageDataRemain == 0)
/* AND Checksum Not Required -- */
&& (!(pCidStruct->pCliProfile[checkSumIndex]))
/* AND Mark-Out Not Required = This is the last byte. */
&& (pCidStruct->markOutByteCount == 0)) {
VP_CID(None, VP_NULL,
("Last Byte (0x%02X) Being Sent - No Checksum - No Markout", *pByte));
pCidStruct->status |= VP_CID_END_OF_MSG;
/* The byte now being sent is the checksum -- */
} else if ((pCidStruct->status & VP_CID_MID_CHECKSUM)
/* AND Mark-Out Not Required = This is the last byte. */
&& (pCidStruct->markOutByteCount == 0)) {
VP_CID(None, VP_NULL,
("Last Byte is Checksum (0x%02x) Now Being Sent - No Markout Required", *pByte));
pCidStruct->status |= VP_CID_END_OF_MSG;
}
VP_API_FUNC_INT(None, VP_NULL, ("-VpCSLACCliGetEncodedByte()"));
return VP_CLI_ENCODE_DATA;
}
/**
* VpCtrlSetCliTone()
* 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
*
* Postconditions:
* The signal generator used for CID tones is enabled/disabled indicated by
* the mode parameter passed.
*/
VpStatusType
VpCtrlSetCliTone(
VpLineCtxType *pLineCtx,
bool mode)
{
VpDevCtxType *pDevCtx = pLineCtx->pDevCtx;
VpDeviceType deviceType = pDevCtx->deviceType;
switch (deviceType) {
#if defined (VP_CC_790_SERIES)
case VP_DEV_790_SERIES:
return Vp790CtrlSetCliTone(pLineCtx, mode);
#endif
#if defined (VP_CC_880_SERIES) && defined (VP880_FXS_SUPPORT)
case VP_DEV_880_SERIES:
return Vp880CtrlSetCliTone(pLineCtx, mode);
#endif
default:
return VP_STATUS_FUNC_NOT_SUPPORTED;
}
}
#endif
/**
* VpCtrlDetectDTMF()
* This function is called by the API internally to enable or disable the
* system level dtmf detector (if present).
*
* Preconditions:
* The line context must be valid
*
* Postconditions:
* If implemented, DTMF detection is enabled (or disabled) in the system
* services layer. This function does not call the system services function for
* devices that support internal DTMF detection.
*/
VpStatusType
VpCtrlDetectDTMF(
VpLineCtxType *pLineCtx,
bool mode)
{
VpDevCtxType *pDevCtx = pLineCtx->pDevCtx;
VpDeviceType deviceType = pDevCtx->deviceType;
VpDeviceIdType deviceId;
uint8 channelId;
VpCallerIdType *pCidStruct = VP_NULL;
void *pDevObj = pDevCtx->pDevObj;
void *pLineObj = pLineCtx->pLineObj;
if ((pDevObj == VP_NULL) || (pLineObj == VP_NULL)) {
return VP_STATUS_INVALID_ARG;
}
switch (deviceType) {
#if defined (VP_CC_790_SERIES)
case VP_DEV_790_SERIES:
deviceId = ((Vp790DeviceObjectType *)pDevObj)->deviceId;
channelId = ((Vp790LineObjectType *)pLineObj)->channelId;
pCidStruct = &((Vp790LineObjectType *)pLineObj)->callerId;
break;
#endif
#if defined (VP_CC_880_SERIES)
case VP_DEV_880_SERIES:
deviceId = ((Vp880DeviceObjectType *)pDevObj)->deviceId;
channelId = ((Vp880LineObjectType *)pLineObj)->channelId;
#if defined (VP880_FXS_SUPPORT)
pCidStruct = &((Vp880LineObjectType *)pLineObj)->callerId;
#endif
break;
#endif
default:
return VP_STATUS_FUNC_NOT_SUPPORTED;
}
/*
* IF enabling DTMF detection, make sure the TX PCM is enabled. Otherwise,
* return TX/RX to states determined by Mute On/Off and linestate
*/
if (mode == TRUE) {
if (pCidStruct != VP_NULL) {
pCidStruct->status |= VP_CID_AWAIT_TONE;
pCidStruct->digitDet = VP_DIG_NONE;
}
VpCtrlMuteChannel(pLineCtx, TRUE);
VpSysDtmfDetEnable(deviceId, channelId);
} else {
if (pCidStruct != VP_NULL) {
pCidStruct->status &= ~(VP_CID_AWAIT_TONE);
}
VpSysDtmfDetDisable(deviceId, channelId);
VpCtrlMuteChannel(pLineCtx, FALSE);
}
return VP_STATUS_SUCCESS;
}
#endif
/**
* VpCtrlSetFSKGen()
* This function is called by the CID sequencer executed internally by the API
*
* Preconditions:
* The line context must be valid
*
* 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".
*/
#if (defined (VP_CC_880_SERIES) && defined (VP880_FXS_SUPPORT)) || \
(defined (VP_CC_890_SERIES) && defined (VP890_FXS_SUPPORT)) || \
(defined (VP_CC_790_SERIES))
bool
VpCtrlSetFSKGen(
VpLineCtxType *pLineCtx,
VpCidGeneratorControlType mode,
uint8 data)
{
VpDevCtxType *pDevCtx = pLineCtx->pDevCtx;
VpDeviceType deviceType = pDevCtx->deviceType;
bool returnStatus = FALSE;
switch (deviceType) {
#if defined (VP_CC_790_SERIES)
case VP_DEV_790_SERIES:
Vp790CtrlSetFSKGen(pLineCtx, mode, data);
break;
#endif
#if defined (VP_CC_880_SERIES) && defined (VP880_FXS_SUPPORT)
case VP_DEV_880_SERIES:
returnStatus = Vp880CtrlSetFSKGen(pLineCtx, mode, data);
break;
#endif
default:
break;
}
return returnStatus;
}
/**
* VpConvertCharToDigitType()
* This function is called by the CID sequencer executed internally by the API.
* It converts a character to a VpDigitType and is used for functions requiring
* a VpDigitType specifically.
*
* Preconditions:
* None. Utility function only.
*
* Postconditions:
* The character passed is converted/returned as a VpDigitType
*/
VpDigitType
VpConvertCharToDigitType(
char digit)
{
VpDigitType vpDig;
switch(digit) {
case '0':
vpDig = VP_DIG_ZERO;
break;
case 'A':
vpDig = VP_DIG_A;
break;
case 'B':
vpDig = VP_DIG_B;
break;
case 'C':
vpDig = VP_DIG_C;
break;
case 'D':
vpDig = VP_DIG_D;
break;
case '*':
vpDig = VP_DIG_ASTER;
break;
case '#':
vpDig = VP_DIG_POUND;
break;
default:
vpDig = (VpDigitType)(digit-48);
break;
}
return vpDig;
}
/**
* VpCtrlSetDTMFGen()
* This function sets the DTMF generators of the device and if DTMF message
* data is in progress, disables the TX/RX PCM highway. If this is the end
* of DTMF message data transmission, then the TX/RX PCM is re-enabled based
* on the line state and the tx/rx mode set for "talk" states.
*
* Preconditions:
* The line must first be initialized.
*
* Postconditions:
* The DTMF signal generators are set to the CID specified level and the digit
* passed is applied to the line (if mode == VP_CID_GENERATOR_DATA).
*/
void
VpCtrlSetDTMFGen(
VpLineCtxType *pLineCtx,
VpCidGeneratorControlType mode,
VpDigitType digit)
{
VpDevCtxType *pDevCtx = pLineCtx->pDevCtx;
if (mode == VP_CID_GENERATOR_DATA) {
VpCtrlMuteChannel(pLineCtx, TRUE);
} else {
VpCtrlMuteChannel(pLineCtx, FALSE);
}
switch (pDevCtx->deviceType) {
#if defined (VP_CC_880_SERIES)
case VP_DEV_880_SERIES:
Vp880SetDTMFGenerators(pLineCtx, mode, digit);
break;
#endif
default:
break;
}
return;
}
#endif
/**
* VpCtrlMuteChannel()
* This function disables the TX/RX PCM highway if mode == TRUE, otherwise it
* enables the TX/RX PCM highway based on line state and the option set for
* TX/RX enable mode in talk states.
*
* Preconditions:
* The line must first be initialized.
*
* Postconditions:
* The TX/RX PCM mode is set on the line.
*/
void
VpCtrlMuteChannel(
VpLineCtxType *pLineCtx,
bool mode)
{
VpDevCtxType *pDevCtx = pLineCtx->pDevCtx;
VpDeviceType deviceType = pDevCtx->deviceType;
switch (deviceType) {
#if defined (VP_CC_790_SERIES)
case VP_DEV_790_SERIES:
Vp790MuteChannel(pLineCtx, mode);
break;
#endif
#if defined (VP_CC_880_SERIES)
case VP_DEV_880_SERIES:
Vp880MuteChannel(pLineCtx, mode);
break;
#endif
default:
break;
}
return;
}
/**
* VpCSLACInitMeter()
* This function is used to initialize metering parameters. See VP-API
* reference guide for more information.
*
* Preconditions:
* The device and line context must be created and initialized before calling
* this function.
*
* Postconditions:
* This function initializes metering parameters as per given profile.
*/
VpStatusType
VpCSLACInitMeter(
VpLineCtxType *pLineCtx,
VpProfilePtrType pMeterProfile)
{
#if defined(VP_CC_790_SERIES) || (defined(VP_CC_880_SERIES) && defined (VP880_FXS_SUPPORT))
VpDevCtxType *pDevCtx = pLineCtx->pDevCtx;
VpDeviceType deviceType = pDevCtx->deviceType;
void *pLineObj = pLineCtx->pLineObj;
void *pDevObj = pDevCtx->pDevObj;
VpDeviceIdType deviceId;
VpProfilePtrType pMetering;
VpCSLACDeviceProfileTableType *pDevProfTable;
VpProfileDataType *pMpiData;
uint8 channelId, ecVal, meterProfEntry;
bool deviceInit = FALSE;
int tableSize = VP_CSLAC_METERING_PROF_TABLE_SIZE;
int meterIndex = VpGetProfileIndex(pMeterProfile);
switch (deviceType) {
#if defined (VP_CC_790_SERIES)
case VP_DEV_790_SERIES:
deviceId = ((Vp790DeviceObjectType *)pDevObj)->deviceId;
channelId = ((Vp790LineObjectType *)pLineObj)->channelId;
pDevProfTable = &((Vp790DeviceObjectType *)pDevObj)->devProfileTable;
deviceInit = (((Vp790DeviceObjectType *)pDevObj)->status.state
& VP_DEV_INIT_CMP);
meterProfEntry =
((Vp790DeviceObjectType *)pDevObj)->profEntry.meterProfEntry;
switch(channelId) {
case 0: ecVal = VP790_EC_CH1; break;
case 1: ecVal = VP790_EC_CH2; break;
case 2: ecVal = VP790_EC_CH3; break;
case 3: ecVal = VP790_EC_CH4; break;
default:
return VP_STATUS_FAILURE;
}
break;
#endif
#if defined (VP_CC_880_SERIES) && defined (VP880_FXS_SUPPORT)
case VP_DEV_880_SERIES:
deviceId = ((Vp880DeviceObjectType *)pDevObj)->deviceId;
channelId = ((Vp880LineObjectType *)pLineObj)->channelId;
ecVal = ((Vp880LineObjectType *)pLineObj)->ecVal;
pDevProfTable = &((Vp880DeviceObjectType *)pDevObj)->devProfileTable;
deviceInit = (((Vp880DeviceObjectType *)pDevObj)->state
& VP_DEV_INIT_CMP);
/*
* Do not proceed if the device calibration is in progress. This could
* damage the device.
*/
if (((Vp880DeviceObjectType *)pDevObj)->state & VP_DEV_IN_CAL) {
deviceInit = FALSE;
}
meterProfEntry =
((Vp880DeviceObjectType *)pDevObj)->profEntry.meterProfEntry;
break;
#endif
default:
return VP_STATUS_INVALID_ARG;
}
if (!(deviceInit)) {
return VP_STATUS_DEV_NOT_INITIALIZED;
}
VpSysEnterCritical(deviceId, VP_CODE_CRITICAL_SEC);
/*
* If the profile passed is an index, make sure it's in the valid range
* and if so, set the currently used profile to it.
*/
if ((meterIndex >= 0) && (meterIndex < tableSize)) {
if (!(meterProfEntry & (0x01 << meterIndex))) {
VpSysExitCritical(deviceId, VP_CODE_CRITICAL_SEC);
return VP_STATUS_ERR_PROFILE;
}
pMetering = pDevProfTable->pMeteringProfileTable[meterIndex];
/* Valid Cadence Entry. Set it if the profile has been initialized */
if (pMetering != VP_PTABLE_NULL) {
pMpiData = (VpProfileDataType *)(&pMetering[VP_PROFILE_MPI_LEN+1]);
VpMpiCmdWrapper(deviceId, ecVal, NOOP_CMD,
pMetering[VP_PROFILE_MPI_LEN], pMpiData);
}
} else if (meterIndex >= tableSize) {
/* It's an index, but it's out of range */
VpSysExitCritical(deviceId, VP_CODE_CRITICAL_SEC);
return VP_STATUS_ERR_PROFILE;
} else {
/* This is a valid metering pointer. Set it */
pMpiData = (VpProfileDataType *)(&pMeterProfile[VP_PROFILE_MPI_LEN+1]);
VpMpiCmdWrapper(deviceId, ecVal, NOOP_CMD,
pMeterProfile[VP_PROFILE_MPI_LEN], pMpiData);
}
VpSysExitCritical(deviceId, VP_CODE_CRITICAL_SEC);
#endif /* 880 || 790 */
return VP_STATUS_SUCCESS;
}
/**
* VpCSLACStartMeter()
* This function starts (can also abort) metering pulses on the line. See
* VP-API-II documentation for more information about this function.
*
* Preconditions:
* Device/Line context should be created and initialized.
*
* Postconditions:
* Metering pulses are transmitted on the line.
*/
VpStatusType
VpCSLACStartMeter(
VpLineCtxType *pLineCtx,
uint16 onTime,
uint16 offTime,
uint16 numMeters)
{
#if defined(VP_CC_790_SERIES) || (defined (VP_CC_880_SERIES) && defined (VP880_FXS_SUPPORT))
VpStatusType status;
uint16 tickRate;
uint8 *pIntSequence;
uint16 bigLoops;
uint16 remainder;
uint8 branchTarget;
VpSeqDataType *pCadence;
VpDevCtxType *pDevCtx = pLineCtx->pDevCtx;
VpDeviceType deviceType = pDevCtx->deviceType;
VpOptionEventMaskType *pLineEvents;
VpDeviceIdType deviceId;
uint16 *pMeterCnt;
bool deviceInit = FALSE;
uint8 index;
VpSetLineStateFuncPtrType SetLineState = pDevCtx->funPtrsToApiFuncs.SetLineState;
VpProfileCadencerStateTypes profWizCurrentState;
VpLineStateType currentState;
uint8 seqLen;
void *pLineObj = pLineCtx->pLineObj;
void *pDevObj = pDevCtx->pDevObj;
switch (deviceType) {
#if defined (VP_CC_790_SERIES)
case VP_DEV_790_SERIES:
deviceId = ((Vp790DeviceObjectType *)pDevObj)->deviceId;
pIntSequence = &((Vp790LineObjectType *)pLineObj)->intSequence[0];
pCadence = &((Vp790LineObjectType *)pLineObj)->cadence;
pLineEvents = &((Vp790LineObjectType *)pLineObj)->lineEvents;
currentState = ((Vp790LineObjectType *)pLineObj)->lineState.currentState;
seqLen = VP790_INT_SEQ_LEN;
deviceInit = (((Vp790DeviceObjectType *)pDevObj)->status.state & VP_DEV_INIT_CMP);
pMeterCnt = &((Vp790LineObjectType *)pLineObj)->processData;
tickRate = ((Vp790DeviceObjectType *)pDevObj)->devProfileData.tickRate;
break;
#endif
#if defined (VP_CC_880_SERIES) && defined (VP880_FXS_SUPPORT)
case VP_DEV_880_SERIES:
deviceId = ((Vp880DeviceObjectType *)pDevObj)->deviceId;
pIntSequence = &((Vp880LineObjectType *)pLineObj)->intSequence[0];
pCadence = &((Vp880LineObjectType *)pLineObj)->cadence;
pLineEvents = &((Vp880LineObjectType *)pLineObj)->lineEvents;
currentState = ((Vp880LineObjectType *)pLineObj)->lineState.currentState;
seqLen = VP880_INT_SEQ_LEN;
deviceInit = (((Vp880DeviceObjectType *)pDevObj)->state & VP_DEV_INIT_CMP);
/*
* Do not proceed if the device calibration is in progress. This could
* damage the device.
*/
if (((Vp880DeviceObjectType *)pDevObj)->state & VP_DEV_IN_CAL) {
deviceInit = FALSE;
}
pMeterCnt = &((Vp880LineObjectType *)pLineObj)->processData;
tickRate = ((Vp880DeviceObjectType *)pDevObj)->devProfileData.tickRate;
break;
#endif
default:
return VP_STATUS_INVALID_ARG;
}
if (!(deviceInit)) {
return VP_STATUS_DEV_NOT_INITIALIZED;
}
VpSysEnterCritical(deviceId, VP_CODE_CRITICAL_SEC);
/*
* If we're not in a valid line state where metering is possible, generate
* an event that metering was aborted for FXS lines and return success.
* Error on FXO lines.
*/
switch(currentState) {
case VP_LINE_TIP_OPEN:
case VP_LINE_DISCONNECT:
pLineEvents->process |= VP_LINE_EVID_MTR_ABORT;
*pMeterCnt = pCadence->meteringBurst;
VpSysExitCritical(deviceId, VP_CODE_CRITICAL_SEC);
return VP_STATUS_SUCCESS;
case VP_LINE_FXO_OHT:
case VP_LINE_FXO_LOOP_OPEN:
case VP_LINE_FXO_LOOP_CLOSE:
case VP_LINE_FXO_TALK:
case VP_LINE_FXO_RING_GND:
VpSysExitCritical(deviceId, VP_CODE_CRITICAL_SEC);
return VP_STATUS_INVALID_ARG;
default:
break;
}
/* Make sure we at least can convert the current state to a Profile Wizard State */
profWizCurrentState = ConvertApiState2PrfWizState(currentState);
if (profWizCurrentState == VP_PROFILE_CADENCE_STATE_UNKNOWN) {
VpSysExitCritical(deviceId, VP_CODE_CRITICAL_SEC);
return VP_STATUS_INVALID_ARG;
}
/*
* Number of meters = 0 will stop metering. SetLineState() will manage
* how and when the metering is actually stopped and generate the
* appropriate event.
*/
if (numMeters == 0) {
SetLineState(pLineCtx, currentState);
VpSysExitCritical(deviceId, VP_CODE_CRITICAL_SEC);
return VP_STATUS_SUCCESS;
}
/* Clear out the internal sequencer */
VpMemSet(pIntSequence, 0, seqLen);
/* Stop all other cadences if they were active */
pCadence->status = VP_CADENCE_RESET_VALUE;
/*
* We are starting a new metering session. Reset the metering pulse count
* that is used if metering is aborted.
*/
pCadence->meteringBurst = 0;
/*
* Clear the pending abort flag.
*/
pCadence->meterPendingAbort = FALSE;
/*
* Build the sequence in the order that it will be executed. Not necessary,
* but makes reading this code easier.
*/
/* Set the type of profile to "metering internal" */
pIntSequence[VP_PROFILE_TYPE_LSB] = VP_PRFWZ_PROFILE_METERING_GEN;
/* The branch command only allows a branch count up to 255, for a total
* maximum of 256 iterations in a loop. To implement numMeters > 256, we
* will use a nested loop of ((numMeters / 256) * 256) followed by a
* a section for the remaining (numMeters % 256).
* Examples:
* numMeters = 60000: numMeters = 500:
* [1] [1]
* <metering> <metering>
* <branch to 1 x256-1> <branch to 1 x256-1>
* <branch to 1 x234-1> [2]
* [2] <metering>
* <metering> <branch to 2 x244-1>
* <branch to 2 x96-1>
*
* numMeters = 256: numMeters = 200:
* [1] [2]
* <metering> <metering>
* <branch to 1 x256-1> <branch to 2 x200-1>
*
* numMeters = 1:
* <metering>
*/
bigLoops = numMeters / 256;
remainder = numMeters % 256;
index = 0;
/* Add the big nested loop of 256 pulses per iteration */
if (bigLoops > 0) {
branchTarget = index / 2;
status = AddMeteringSection(pLineCtx, pIntSequence, &index, tickRate, onTime, offTime, 256);
if (status != VP_STATUS_SUCCESS) {
VpSysExitCritical(deviceId, VP_CODE_CRITICAL_SEC);
return status;
}
/* If needed, add the outer big loop branch */
if (bigLoops > 1) {
pIntSequence[VP_PROFILE_TYPE_SEQUENCER_START+index] =
VP_SEQ_SPRCMD_BRANCH_INSTRUCTION | branchTarget;
index++;
pIntSequence[VP_PROFILE_TYPE_SEQUENCER_START+index] = bigLoops - 1;
index++;
}
}
/* Add the remaining pulses */
if (remainder > 0) {
status = AddMeteringSection(pLineCtx, pIntSequence, &index, tickRate,
onTime, offTime, remainder);
if (status != VP_STATUS_SUCCESS) {
VpSysExitCritical(deviceId, VP_CODE_CRITICAL_SEC);
return status;
}
}
/*
* When the metering is complete, return to the current line state. Note
* that this step is not reached if metering is infinite (on-time = 0)
*/
pIntSequence[VP_PROFILE_TYPE_SEQUENCER_START+index] =
(VP_SEQ_SPRCMD_COMMAND_INSTRUCTION | VP_SEQ_SUBCMD_LINE_STATE);
index++;
pIntSequence[VP_PROFILE_TYPE_SEQUENCER_START+index] = profWizCurrentState;
index++;
pIntSequence[VP_PROFILE_TYPE_SEQUENCER_COUNT_LSB] = index;
/*
* Profile Length is always sequence lengh + 4 because of header
* definition.
*/
pIntSequence[VP_PROFILE_LENGTH] = pIntSequence[VP_PROFILE_TYPE_SEQUENCER_COUNT_LSB] + 4;
pCadence->index = VP_PROFILE_TYPE_SEQUENCER_START;
pCadence->length = pIntSequence[VP_PROFILE_LENGTH];
pCadence->pActiveCadence = &pIntSequence[0];
pCadence->pCurrentPos = &pIntSequence[8];
pCadence->status |= VP_CADENCE_STATUS_ACTIVE;
pCadence->status |= VP_CADENCE_STATUS_METERING;
#if (VP_CC_DEBUG_SELECT & VP_DBG_SEQUENCER)
VP_SEQUENCER(VpLineCtxType, pLineCtx, ("Metering Cadence:"));
for (index = 0; index < pIntSequence[VP_PROFILE_LENGTH]; index++) {
VP_SEQUENCER(VpLineCtxType, pLineCtx,
(" 0x%02X", pIntSequence[VP_PROFILE_LENGTH + index + 1]));
}
#endif /* (VP_CC_DEBUG_SELECT & VP_DBG_SEQUENCER) */
VpSysExitCritical(deviceId, VP_CODE_CRITICAL_SEC);
#endif /* 790 || 880 */
return VP_STATUS_SUCCESS;
}
#if defined(VP_CC_790_SERIES) || (defined (VP_CC_880_SERIES) && defined (VP880_FXS_SUPPORT))
static VpStatusType
AddMeteringSection(
VpLineCtxType *pLineCtx,
uint8 *pIntSequence,
uint8 *pIndex,
uint16 tickRate,
uint16 onTime,
uint16 offTime,
uint16 numMeters)
{
uint16 tempTime;
uint32 timeRemaining;
uint8 tickAdjust;
uint8 branchTarget;
uint8 index;
index = *pIndex;
branchTarget = index / 2;
/* The maximum branch target that can be specified is 31, 0x1F. To exceed
* that, VpStartMeter() would need to be called with a total on+off time of
* at least 110579, or 1105.79 seconds, and numMeters > 256. With a maximum
* offtime of 65535 (655.35 seconds), that would still require an ontime of
* 45044 (450.44 seconds), so this is an impractical case anyway. */
if (branchTarget > 0x1F) {
VP_ERROR(VpLineCtxType, pLineCtx,
("Could not build metering cadence with excessive on/off times (%d/%d) and numMeters > 256.",
onTime, offTime));
return VP_STATUS_FAILURE;
}
/* And set the starting command to start metering */
pIntSequence[VP_PROFILE_TYPE_SEQUENCER_START + index]
= (VP_SEQ_SPRCMD_COMMAND_INSTRUCTION | VP_SEQ_SUBCMD_METERING);
index++;
pIntSequence[VP_PROFILE_TYPE_SEQUENCER_START + index] = 0x01;
index++;
/*
* Set the metering on time after metering is enabled. Metering time is
* specified in 10ms incr, Cadence in 5ms.
*/
timeRemaining = onTime * 2;
if (timeRemaining == 0) {
/* Infinite on-time */
pIntSequence[VP_PROFILE_TYPE_SEQUENCER_START+index] =
VP_SEQ_SPRCMD_TIME_INSTRUCTION;
index++;
pIntSequence[VP_PROFILE_TYPE_SEQUENCER_START+index] = 0x00;
index++;
} else {
/* Adjust for tick timing. The processing of the metering off command will
* extend the on-time by one tick. */
tickAdjust = TICKS_TO_MS(1, tickRate);
if (tickAdjust % 5 > 2) {
tickAdjust = (tickAdjust / 5) + 1;
} else {
tickAdjust = (tickAdjust / 5);
}
if (timeRemaining > tickAdjust) {
timeRemaining -= tickAdjust;
} else {
timeRemaining = 0;
}
}
/* The maximum time representable by a uint16 in 10ms units is 655.35
* seconds. One cadence time operator gives us at most 40.955 seconds, so
* we potentially need to put several together to achieve longer times. */
while (timeRemaining) {
if (timeRemaining > 0x1FFF) {
tempTime = 0x1FFF;
timeRemaining -= 0x1FFF;
} else {
tempTime = timeRemaining;
timeRemaining = 0;
}
/* Add time command (2 bytes) */
pIntSequence[VP_PROFILE_TYPE_SEQUENCER_START+index] =
VP_SEQ_SPRCMD_TIME_INSTRUCTION;
pIntSequence[VP_PROFILE_TYPE_SEQUENCER_START+index] |=
(tempTime & 0x1F00) >> 8;
index++;
pIntSequence[VP_PROFILE_TYPE_SEQUENCER_START+index] =
(tempTime & 0x00FF);
index++;
}
/*
* If the on-time is = 0, the Sequencer automatically suspends indefinitely
* at the current state. Otherwise, it will proceed to the next step.
*/
/* Then turn the metering off */
pIntSequence[VP_PROFILE_TYPE_SEQUENCER_START+index]
= (VP_SEQ_SPRCMD_COMMAND_INSTRUCTION | VP_SEQ_SUBCMD_METERING);
index++;
pIntSequence[VP_PROFILE_TYPE_SEQUENCER_START+index] = 0x00;
index++;
/*
* Set the metering on time after metering is enabled. Metering time is
* specified in 10ms incr, Cadence in 5ms.
*/
timeRemaining = offTime * 2;
if (timeRemaining == 0) {
/* Infinite off-time */
pIntSequence[VP_PROFILE_TYPE_SEQUENCER_START+index] =
VP_SEQ_SPRCMD_TIME_INSTRUCTION;
index++;
pIntSequence[VP_PROFILE_TYPE_SEQUENCER_START+index] = 0x00;
index++;
} else {
/* Adjust for tick timing. The processing of the branch and metering on
* commands will extend the off-time by two ticks. */
tickAdjust = TICKS_TO_MS(2, tickRate);
if (tickAdjust % 5 > 2) {
tickAdjust = (tickAdjust / 5) + 1;
} else {
tickAdjust = (tickAdjust / 5);
}
if (timeRemaining > tickAdjust) {
timeRemaining -= tickAdjust;
} else {
timeRemaining = 0;
}
}
/* The maximum time representable by a uint16 in 10ms units is 655.35
* seconds. One cadence time operator gives us at most 40.955 seconds, so
* we potentially need to put several together to achieve longer times. */
while (timeRemaining) {
if (timeRemaining > 0x1FFF) {
tempTime = 0x1FFF;
timeRemaining -= 0x1FFF;
} else {
tempTime = timeRemaining;
timeRemaining = 0;
}
/* Add time command (2 bytes) */
pIntSequence[VP_PROFILE_TYPE_SEQUENCER_START+index] =
VP_SEQ_SPRCMD_TIME_INSTRUCTION;
pIntSequence[VP_PROFILE_TYPE_SEQUENCER_START+index] |=
(tempTime & 0x1F00) >> 8;
index++;
pIntSequence[VP_PROFILE_TYPE_SEQUENCER_START+index] =
(tempTime & 0x00FF);
index++;
}
/*
* Condition of numMeters = 0 (stop metering) is taken care of at top.
* Then, condition numMeters != 0 and on-time is (meter forever) is taken
* care of as a consequence of the Sequencer (i.e., suspend for any time
* operator set to 0).
* All other operators will count some number of metering pulses, end, then
* go back to the state the line was in when the sequence started.
*/
/* Can only be 1 or > 1, not 0 */
if (numMeters > 1) {
/*
* If more than 1, we'll branch back to the start of this metering
* section until all metering pulses occur.
*/
pIntSequence[VP_PROFILE_TYPE_SEQUENCER_START+index] =
VP_SEQ_SPRCMD_BRANCH_INSTRUCTION | branchTarget;
index++;
pIntSequence[VP_PROFILE_TYPE_SEQUENCER_START+index] = numMeters - 1;
index++;
} else {
/* If exactly equal to 1, no branch is necessary. */
}
*pIndex = index;
return VP_STATUS_SUCCESS;
}
#endif /* defined(VP_CC_790_SERIES) || (defined (VP_CC_880_SERIES) && defined (VP880_FXS_SUPPORT)) */
#endif /* VP_CSLAC_SEQ_EN */