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