blob: 8ad1c68957cc9d41fa50889657e0fa22e34f4aec [file] [log] [blame]
/*******************************************************************************
Copyright (c) 2014-2015 Lantiq Deutschland GmbH
Copyright (c) 2015-2016 Lantiq Beteiligungs-GmbH & Co.KG
Copyright 2016, 2017, 2018 Intel Corporation.
For licensing information, see the file 'LICENSE' in the root folder of
this software module.
*******************************************************************************/
/**
\file dxs_ring.c
Function implementation for cadenced ringing.
*/
/* ========================================================================== */
/* Includes */
/* ========================================================================== */
#include <time.h>
#include <errno.h>
#include "dxs_config.h"
#include "dxs_lib.h"
#include "dxs_sdd.h"
#include "dxs_errno.h"
#include "dxs_error.h"
#include "dxs_misc.h"
#include "dxs_ring.h"
#include "dxs_dcdc_hw.h"
#include "dxs_cid.h"
/* ========================================================================== */
/* Macro definitions */
/* ========================================================================== */
/* total channel count */
#define DXS_RING_CHANNELS (DXS_MAX_DEVICES * CH_PER_DEVICE)
#define RING_TIMER_INTERVAL_MS 50
/* ========================================================================== */
/* Type definitions */
/* ========================================================================== */
typedef struct
{
#define RING_TIMER_STOPPED 0
#define RING_TIMER_RUNNING 1
int32_t state;
sem_t sem;
pthread_t thr;
int32_t ret_val;
int32_t current_bit;
int32_t reset_bits;
} dxs_ring_timer_t;
typedef struct
{
#define RING_CADENCE_NOT_INIT 0
#define RING_CADENCE_INIT 1
#define RING_CADENCE_CID_ACT 2
int32_t flag;
DXS_CHANNEL_t *pCh;
#define RING_POLARITY_NORMAL 0
#define RING_POLARITY_REVERSE 1
int32_t polarity;
#define RING_CAD_STATE_IDLE 0
#define RING_CAD_STATE_RINGING 1
#define RING_CAD_STATE_CID_DURING_RINGING 2
#define RING_CAD_STATE_CID_PRIOR_TO_RINGING 3
volatile int32_t ring_cadence_state;
#define RINGER_STATE_PAUSE 0
#define RINGER_STATE_RINGING 1
int32_t ringer_state;
DXS_RingCadence_t cadence;
/* used only for
prior to ringing CID standards */
int32_t wait_counter;
int32_t current_bit;
} dxs_ring_res_t;
/* ========================================================================== */
/* Global variables */
/* ========================================================================== */
static dxs_ring_timer_t reft = {0};
static dxs_ring_res_t ring_cadence[DXS_RING_CHANNELS] = {0};
/* ========================================================================== */
/* Function implementation */
/* ========================================================================== */
/**
Return the bit position of the ringing pulse number in a cadence
\param pRes - pointer to ringing cadence resource
\param ring - ringing pulse number starting from 0
\return
- bit position of the ringing pulse
*/
static int32_t dxs_ring_next_pos_get (dxs_ring_res_t *pRes, int32_t ring)
{
int32_t bit=0, i=0, rpulse=0, ring_phase=0;
/* TODO: protection against infinite loop */
while (1)
{
if ((pRes->cadence.data[i >> 3] >> (7-(i%8))) & 1)
{
ring_phase = 1;
if (rpulse == ring)
break;
}
else
{
if (ring_phase)
{
ring_phase = 0;
++rpulse;
}
}
++bit;
if (++i == pRes->cadence.valid_bits)
i = 0;
}
return bit;
}
/**
Return the ringing cadence initial state; decision point
whether the CID sequence shall be used in the ringing cadence.
\param pCh - pointer to \ref DXS_CHANNEL_t
\return
- RING_CAD_STATE_CID_DURING_RINGING
- RING_CAD_STATE_CID_PRIOR_TO_RINGING
- RING_CAD_STATE_RINGING
*/
static int32_t dxs_ring_cad_initial_state (DXS_CHANNEL_t *pCh)
{
int32_t err, ret = RING_CAD_STATE_RINGING;
DXS_CID_Std_t cid_std;
DXS_CID_AS_t cid_as;
err = DXS_CID_StandardGet(pCh, &cid_std);
if (err == DXS_statusOk)
{
err = DXS_CID_AlertSigGet(pCh, &cid_as);
}
if (err == DXS_statusOk)
{
if (cid_as == DXS_CID_AS_NONE &&
(cid_std == DXS_CID_STD_TELCORDIA || cid_std == DXS_CID_STD_ETSI))
{
ret = RING_CAD_STATE_CID_DURING_RINGING;
}
else if (cid_as != DXS_CID_AS_NONE && cid_std != DXS_CID_STD_UNDEF)
{
ret = RING_CAD_STATE_CID_PRIOR_TO_RINGING;
}
}
return ret;
}
/**
Attempt to shift the ringing cadence so that the new cadence
fits into the limitation of IFB12CH8 HW - only two ring bursts
are possible at a time per DCDC.
1) The first available bit position for a cadence is searched -
where less than two rings are programmed for parallel channels.
2) The new cadence is bit shifted to the found position.
3) The new shifted cadence is checked from shift position to the
end if there is no conflict of two parallel rings.
If step 1) or 3) fails, than the new cadence does not fit into
the ring matrix of a DCDC and an error will be returned.
\param pointer to ringing resource context
\param ringing cadence length in bytes
\return
the code from \ref DXS_status_t list
*/
static int32_t dxs_ifb12ch8_shift_cadence (dxs_ring_res_t *pRes, int32_t len)
{
int i, pos=0, conflict=0, dcdc;
unsigned int max_bits;
uint8_t channels_per_dcdc;
dcdc = DXS_DCDC_HW_SharedDcDcNumberGet(pRes->pCh);
channels_per_dcdc = DXS_DCDC_HW_SharedDcDcChannelNumGet(pRes->pCh);
/* get maximum cadence length for a DCDC */
for (i=0, max_bits=0; i<channels_per_dcdc; i++)
{
DXS_CHANNEL_t *pCh = DXS_DCDC_HW_SharedDcDcChannelGet(dcdc, i);
if (pCh != NULL)
{
dxs_ring_res_t *p = (dxs_ring_res_t *)pCh->ring;
if (p->cadence.valid_bits > max_bits)
max_bits = p->cadence.valid_bits;
}
}
/* find the first available bit position for the cadence */
for (i=0; i<max_bits; i++)
{
int c = 0;
uint8_t bits=0,
my_bit = (pRes->cadence.data[i >> 3] >> (7-(i % 8))) & 1;
while (c < channels_per_dcdc)
{
DXS_CHANNEL_t *pCh = DXS_DCDC_HW_SharedDcDcChannelGet(dcdc, c);
if (pCh != NULL && pCh != pRes->pCh)
{
dxs_ring_res_t *p = (dxs_ring_res_t *)pCh->ring;
uint8_t bit = (p->cadence.data[i >> 3] >> (7-(i % 8))) & 1;
if (bit) ++bits;
}
++c;
}
if (bits >= 2 && my_bit && !conflict)
{
conflict = 1;
}
else if (conflict && bits < 2)
{
pos = i;
conflict = 0;
break;
}
}
if (conflict)
{
DXS_RETURN(DXS_statusRingCadConfInvalid);
}
DXS_Misc_DataBlkRor(pRes->cadence.data, len, pos);
/* position for a cadence start is found, now check the rest
of the cadence for possible conflicts */
i = pos;
do
{
int c = 0;
uint8_t bits=0,
my_bit = (pRes->cadence.data[i >> 3] >> (7-(i % 8))) & 1;
while (c < channels_per_dcdc)
{
DXS_CHANNEL_t *pCh = DXS_DCDC_HW_SharedDcDcChannelGet(dcdc, c);
if (pCh != NULL && pCh != pRes->pCh)
{
dxs_ring_res_t *p = (dxs_ring_res_t *)pCh->ring;
uint8_t bit = (p->cadence.data[i >> 3] >> (7-(i % 8))) & 1;
if (bit) ++bits;
}
++c;
}
if (bits >= 2 && my_bit)
{
DXS_RETURN(DXS_statusRingCadConfInvalid);
}
if (++i == pRes->cadence.valid_bits)
i = 0;
} while (i != pos);
return DXS_statusOk;
}
/**
Start the ringing pulse
\param pointer to ringing resource context
\return
the code from \ref DXS_status_t list
*/
static int32_t dxs_ring_pulse_start (dxs_ring_res_t *pRes)
{
int32_t ret = DXS_statusOk;
if (pRes->ring_cadence_state == RING_CAD_STATE_RINGING ||
pRes->ring_cadence_state == RING_CAD_STATE_CID_DURING_RINGING)
{
DXS_CHANNEL_t *pCh = pRes->pCh;
ret = DXS_SDD_LineModeSet(pCh, pRes->polarity ?
DXS_LINE_FEED_RINGING_REVPOL :
DXS_LINE_FEED_RING_BURST
);
pRes->ringer_state = (DXS_statusOk == ret) ?
RINGER_STATE_RINGING : RINGER_STATE_PAUSE;
}
return ret;
}
/**
Stop the ringing pulse
\param pointer to ringing resource context
\return
the code from \ref DXS_status_t list
*/
static int32_t dxs_ring_pulse_stop (dxs_ring_res_t *pRes)
{
int32_t ret = DXS_statusOk;
if (pRes->ring_cadence_state == RING_CAD_STATE_RINGING ||
pRes->ring_cadence_state == RING_CAD_STATE_CID_DURING_RINGING)
{
DXS_CHANNEL_t *pCh = pRes->pCh;
ret = DXS_SDD_LineModeSet(pCh, pRes->polarity ?
DXS_LINE_FEED_ACTIVE_REVPOL :
DXS_LINE_FEED_ACTIVE
);
pRes->ringer_state = (DXS_statusOk == ret) ?
RINGER_STATE_PAUSE : RINGER_STATE_RINGING;
}
return ret;
}
/**
Get current bit position for the ringing cadence
\param pointer to ringing cadence resource context
\return bit position
*/
static int32_t dxs_ring_cad_bit_position_get (dxs_ring_res_t *pRes)
{
int32_t bit_pos;
if (pRes->pCh->pParent->dcdcType == DXS_DCDC_IFB12CH8)
{
/* cadence bit position is global for all channels
in case of IFB12CH8 */
bit_pos = reft.current_bit;
}
else
{
/* cadence bit position is individual for each
channel in case of HW without ringing limitations */
bit_pos = pRes->current_bit;
}
return bit_pos;
}
/**
Ringing timer thread function
\param pointer to ringing timer context
\return none
*/
static void *ring_timer_thread (void *arg)
{
int err;
dxs_ring_timer_t *p = (dxs_ring_timer_t *)arg;
struct timespec ts = {0};
unsigned long ns;
while (p->state == RING_TIMER_RUNNING)
{
int32_t i, bit_position = -1;
/* run through all channels, get ring cadence bit value for
each channel and execute appropriate action */
for (i=0; i<DXS_RING_CHANNELS; i++)
{
dxs_ring_res_t *pRes = &ring_cadence[i];
uint8_t *cad, ring;
if (!(pRes->flag & RING_CADENCE_INIT))
continue;
cad = pRes->cadence.data;
bit_position = dxs_ring_cad_bit_position_get(pRes);
/* get ring cadence bit value */
ring = (cad[bit_position >> 3] >> (7-(bit_position % 8))) & 1;
/* handle CID prior to ringing */
if (pRes->ring_cadence_state == RING_CAD_STATE_CID_PRIOR_TO_RINGING)
{
if (!pRes->wait_counter && !(pRes->flag & RING_CADENCE_CID_ACT))
{
pRes->flag |= RING_CADENCE_CID_ACT;
err = DXS_CID_TypeI_Play(pRes->pCh, DXS_CID_MSG_TYPE_CS,
DXS_CID_CALL_FROM_RING_SM);
if (err)
{
DXS_ERROR_PUSH(err);
}
}
else
pRes->wait_counter--;
}
if (!ring && pRes->ringer_state == RINGER_STATE_RINGING)
{
/* RING OFF */
err = dxs_ring_pulse_stop(pRes);
if (err)
{
DXS_ERROR_PUSH(err);
}
/* handle CID during ringing */
if (pRes->ring_cadence_state == RING_CAD_STATE_CID_DURING_RINGING)
{
pRes->flag |= RING_CADENCE_CID_ACT;
err = DXS_CID_TypeI_Play(pRes->pCh, DXS_CID_MSG_TYPE_CS,
DXS_CID_CALL_FROM_RING_SM);
if (err)
{
DXS_ERROR_PUSH(err);
}
}
}
}
if (bit_position == -1)
{
DXS_ERROR_PUSH(-1);
continue;
}
for (i=0; i<DXS_RING_CHANNELS; i++)
{
dxs_ring_res_t *pRes = &ring_cadence[i];
uint8_t *cad, ring;
if (!(pRes->flag & RING_CADENCE_INIT))
continue;
cad = pRes->cadence.data;
bit_position = dxs_ring_cad_bit_position_get(pRes);
/* get ring cadence bit value */
ring = (cad[bit_position >> 3] >> (7-(bit_position % 8))) & 1;
if (ring && pRes->ringer_state == RINGER_STATE_PAUSE)
{
int32_t reset_bits = (pRes->pCh->pParent->dcdcType == DXS_DCDC_IFB12CH8) ?
p->reset_bits : pRes->cadence.valid_bits;
int32_t pos = ((bit_position + 1) >= reset_bits) ?
0 : (bit_position + 1);
int32_t ring_bits = 0;
uint8_t bit_val = ring;
/* skip execution of "short ring pulses",
ensure that ring pulse is at least 500ms long */
while (bit_val)
{
bit_val = (cad[pos >> 3] >> (7-(pos % 8))) & 1;
if (bit_val)
++ring_bits;
if (++pos == reset_bits)
pos = 0;
}
if (ring_bits >= 10)
{
/* RING ON */
err = dxs_ring_pulse_start(pRes);
if (err)
{
DXS_ERROR_PUSH(err);
}
}
}
}
/* wait 50ms */
clock_gettime (CLOCK_REALTIME, &ts);
ns = ts.tv_nsec + RING_TIMER_INTERVAL_MS * 1000 * 1000;
ts.tv_nsec = ns % (1000 * 1000 * 1000);
ts.tv_sec += ns / (1000 * 1000 * 1000);
while ((err = sem_timedwait(&p->sem, &ts)) == -1 && errno == EINTR)
continue;
if (err == -1 && errno != ETIMEDOUT)
{
p->ret_val = errno;
break;
}
/* update global bit position */
if (++p->current_bit == p->reset_bits)
p->current_bit = 0;
/* update individual bit positions per channel */
for (i=0; i<DXS_RING_CHANNELS; i++)
{
dxs_ring_res_t *pRes = &ring_cadence[i];
if (!(pRes->flag & RING_CADENCE_INIT))
continue;
if (pRes->ring_cadence_state != RING_CAD_STATE_CID_PRIOR_TO_RINGING)
{
if (++pRes->current_bit == pRes->cadence.valid_bits)
pRes->current_bit = 0;
}
}
}
pthread_exit((void *)(&p->ret_val));
}
/**
Start the ringing timer
\param none
\return
the code from \ref DXS_status_t list
*/
static int32_t ring_timer_start (void)
{
int32_t ret = DXS_statusOk;
if (reft.state == RING_TIMER_STOPPED)
{
int32_t i;
reft.state = RING_TIMER_RUNNING;
sem_init(&reft.sem, 0, 0);
reft.current_bit = 0;
reft.reset_bits = 0;
for (i=0; i<DXS_RING_CHANNELS; i++)
{
dxs_ring_res_t *pRes = &ring_cadence[i];
/* reset_bits is the maximum cadence length */
if ((pRes->flag & RING_CADENCE_INIT) &&
reft.reset_bits < pRes->cadence.valid_bits)
{
reft.reset_bits = pRes->cadence.valid_bits;
}
}
if (0 != pthread_create(&reft.thr, NULL, ring_timer_thread, (void *)&reft))
ret = DXS_statusThreadCreatError;
}
return ret;
}
/**
Stop the ringing timer
\param none
\return none
*/
static void ring_timer_stop (void)
{
int i;
for (i=0; i<DXS_RING_CHANNELS; i++)
{
dxs_ring_res_t *pRes = &ring_cadence[i];
if (pRes->ring_cadence_state != RING_CAD_STATE_IDLE)
return;
}
if (reft.state != RING_TIMER_STOPPED)
{
reft.state = RING_TIMER_STOPPED;
sem_post(&reft.sem);
pthread_join(reft.thr, NULL);
sem_destroy(&reft.sem);
}
}
/**
Inform ringing SM about finished CID sequence
\param pCh - pointer to DXS channel structure
\return
- none
*/
void DXS_Ring_CidFinished (DXS_CHANNEL_t *pCh)
{
dxs_ring_res_t *pRes =
&ring_cadence[pCh->pParent->nDevNum * CH_PER_DEVICE + pCh->nCh];
if (pRes->ring_cadence_state == RING_CAD_STATE_CID_DURING_RINGING ||
pRes->ring_cadence_state == RING_CAD_STATE_CID_PRIOR_TO_RINGING)
{
pRes->ring_cadence_state = RING_CAD_STATE_RINGING;
}
pRes->flag &= ~RING_CADENCE_CID_ACT;
}
/**
Initialize ringing resource and link it
with the \ref DXS_CHANNEL_t structure
\param pCh - pointer to DXS channel structure
\return
pointer to ringing resource
*/
void *DXS_Ring_ResInit(DXS_CHANNEL_t *pCh)
{
dxs_ring_res_t *pRes =
&ring_cadence[pCh->pParent->nDevNum * CH_PER_DEVICE + pCh->nCh];
pRes->pCh = pCh;
pRes->flag = RING_CADENCE_NOT_INIT;
return (void *)pRes;
}
/**
Initialize ringing cadence
\param pCh - pointer to DXS channel structure
\param pConf - pointer to \ref DXS_RingCadence_t structure
\return
the code from \ref DXS_status_t list
*/
int32_t DXS_RING_CadenceInit (DXS_CHANNEL_t *pCh, DXS_RingCadence_t *pConf)
{
int32_t ret = DXS_statusOk;
dxs_ring_res_t *pRes = (dxs_ring_res_t *)pCh->ring;
int32_t bytes, bytes_to_copy, extra_bits;
POINTER_ASSERT(pConf);
if (pRes->ring_cadence_state != RING_CAD_STATE_IDLE)
{
DXS_RETURN(DXS_statusRingCadConfNotPossible);
}
bytes = pConf->valid_bits >> 3;
extra_bits = pConf->valid_bits % 8;
bytes_to_copy = bytes + (extra_bits ? 1 : 0);
if (bytes_to_copy > DXS_RING_CADENCE_MAX_LEN_BYTES)
{
DXS_RETURN(DXS_statusRingCadConfInvalid);
}
pRes->flag &= ~RING_CADENCE_INIT;
/* copy cadence data and valid bits */
pRes->cadence = *pConf;
if (extra_bits)
pRes->cadence.data[bytes] &= (0xff & (0xff << (8 - extra_bits)));
/* special arrangements for IFB12CH8 HW */
if (pCh->pParent->dcdcType == DXS_DCDC_IFB12CH8)
{
ret = dxs_ifb12ch8_shift_cadence(pRes, bytes_to_copy);
}
if (ret == DXS_statusOk)
{
pRes->ring_cadence_state = RING_CAD_STATE_IDLE;
pRes->ringer_state = RINGER_STATE_PAUSE;
pRes->flag |= RING_CADENCE_INIT;
}
DXS_RETURN(ret);
}
/**
Start ringing cadence
\param pCh - pointer to DXS channel structure
\return
the code from \ref DXS_status_t list
*/
int32_t DXS_RING_CadenceStart (DXS_CHANNEL_t *pCh)
{
dxs_ring_res_t *pRes = (dxs_ring_res_t *)pCh->ring;
int32_t ret;
int opmode;
if (!(pRes->flag & RING_CADENCE_INIT))
{
DXS_RETURN(DXS_statusRingCadStartNotConf);
}
ret = DXS_SDD_LineModeGet(pCh, &opmode);
if (ret == DXS_statusOk && opmode == DXS_LINE_FEED_ACTIVE_REVPOL)
pRes->polarity = RING_POLARITY_REVERSE;
if (ret == DXS_statusOk)
{
pRes->current_bit = 0;
pRes->ring_cadence_state = dxs_ring_cad_initial_state(pCh);
pRes->ringer_state = RINGER_STATE_PAUSE;
pRes->wait_counter = 0;
if (pRes->pCh->pParent->dcdcType == DXS_DCDC_IFB12CH8 &&
pRes->ring_cadence_state == RING_CAD_STATE_CID_PRIOR_TO_RINGING)
{
int32_t next_ring_bit_pos, cid_len_bits, ring_pulse=0;
DXS_CID_LengthBitsGet(pCh, &cid_len_bits);
if (cid_len_bits > 0)
{
/* set the wait_counter */
do
{
next_ring_bit_pos = dxs_ring_next_pos_get(pRes, ring_pulse++);
pRes->wait_counter = next_ring_bit_pos - cid_len_bits;
} while (pRes->wait_counter < 0);
}
else
{
/* zero or negative CID length means no CID */
pRes->ring_cadence_state = RING_CAD_STATE_RINGING;
}
}
ret = ring_timer_start();
}
DXS_RETURN(ret);
}
/**
Stop ringing cadence
\param pCh - pointer to DXS channel structure
\return
DXS_statusOk
*/
int32_t DXS_RING_CadenceStop (DXS_CHANNEL_t *pCh)
{
int32_t ret = DXS_statusOk;
dxs_ring_res_t *pRes = (dxs_ring_res_t *)pCh->ring;
if (pRes->ringer_state == RINGER_STATE_RINGING)
{
ret = dxs_ring_pulse_stop(pRes);
}
if (pRes->ring_cadence_state == RING_CAD_STATE_CID_DURING_RINGING ||
pRes->ring_cadence_state == RING_CAD_STATE_CID_PRIOR_TO_RINGING)
{
DXS_CID_Stop(pCh);
}
if (ret == DXS_statusOk)
{
pRes->ring_cadence_state = RING_CAD_STATE_IDLE;
ring_timer_stop();
}
DXS_RETURN(ret);
}