blob: 5a347f902e41f4304596d3bf86552755354dde53 [file] [log] [blame]
/******************************************************************************
Copyright (c) 2014-2015 Lantiq Deutschland GmbH
Copyright (c) 2015-2016 Lantiq Beteiligungs-GmbH & Co.KG
Copyright 2016, Intel Corporation.
For licensing information, see the file 'LICENSE' in the root folder of
this software module.
******************************************************************************/
/**
\file dxs_outbox.c
*/
/* ========================================================================== */
/* Includes */
/* ========================================================================== */
#include <stdio.h>
#include <errno.h>
#include "dxs_config.h"
#include "dxs.h"
#include "dxs_lib.h"
#include "dxs_mbx.h"
#include "dxs_errno.h"
#include "dxs_error.h"
#include "dxs_event.h"
#include "dxs_sdd.h"
#include "dxs_sig.h"
#include "dxs_cid_fsk.h"
#include "dxs_cid.h"
#include "dxs_init.h"
#include "dxs_dcdc_hw.h"
#ifdef EVENT_LOGGER_DEBUG
#include <sys/ioctl.h>
#endif
/* ========================================================================== */
/* Macro definitions */
/* ========================================================================== */
/*#define DXS_CMD_PRINT*/
#undef DXS_CMD_PRINT
//#define DXS_CMD_PRINT
/* SDD events */
/** Over temperature detected */
#define EVT_SDD_OTEMP 0
/** Line testing finished */
#define EVT_SDD_LT_FIN 1
/** Ground fault detected */
#define EVT_SDD_GF 3
/** Ground key detected */
#define EVT_SDD_GK 4
/** Opmode changed */
#define EVT_SDD_OPC 5
/** Error in command SDD_CoeffReadConfig */
#define EVT_SDD_CORCE 6
/** Error in command SDD_Coeff */
#define EVT_SDD_COEFE 7
/** On-hook */
#define EVT_SDD_ONH 9
/** Off-hook */
#define EVT_SDD_OFFH 10
/** Ground Fault Finished */
#define EVT_SDD_GF_FIN 12
/** Ground Key Finished */
#define EVT_SDD_GK_FIN 13
/** Overtemp finished which means that
SLIC temperature is back in normal range */
#define EVT_SDD_OTEMP_FIN 14
/** Operating Mode Ignored */
#define EVT_SDD_OMI 17
/** Operating Mode discarded */
#define EVT_SDD_OPM_DIS 18
/** Line testing aborted */
#define EVT_SDD_LT_ABORT 19
/* SIG events */
/** DTMF detector */
#define EVT_SIG_DTMF_DET 1
/** Caller ID sender request */
#define EVT_SIG_CIS_REQ 2
/** Caller ID sender buffer underflow */
#define EVT_SIG_CIS_BUF 3
/** Caller ID sender has finished sending data */
#define EVT_SIG_CIS_FIN 4
/** Start of tone detected */
#define EVT_SIG_UTD_START 5
/** End of tone detected */
#define EVT_SIG_UTD_END 6
/** Metering pulse sent */
#define EVT_SIG_TTX_FIN 9
/** AC level metering finished */
#define EVT_SIG_AC_LM_FIN 10
/* ========================================================================== */
/* Type definitions */
/* ========================================================================== */
struct __fw_evt_header
{
#if __BYTE_ORDER == __BIG_ENDIAN
/* Reserved */
uint32_t Res00 : 3;
/* Command Type */
uint32_t CMD : 5;
/* Channel */
uint32_t CHAN : 8;
/* Command Mode */
uint32_t MOD : 3;
/* Command Sub-Mode */
uint32_t ECMD : 5;
/* Length of Command Payload */
uint32_t LENGTH : 8;
#else
/* Length of Command Payload */
uint32_t LENGTH : 8;
/* Command Sub-Mode */
uint32_t ECMD : 5;
/* Command Mode */
uint32_t MOD : 3;
/* Channel */
uint32_t CHAN : 8;
/* Command Type */
uint32_t CMD : 5;
/* Reserved */
uint32_t Res00 : 3;
#endif
} __attribute__ ((packed));
struct __fw_evt_int_err
{
struct __fw_evt_header hdr;
#if __BYTE_ORDER == __BIG_ENDIAN
/* Reserved */
uint32_t Res01 : 27;
/* Event Code */
uint32_t ERREVT : 5;
#else
/* Event Code */
uint32_t ERREVT : 5;
/* Reserved */
uint32_t Res01 : 27;
#endif
} __attribute__ ((packed));
struct __fw_evt_cmd_err
{
struct __fw_evt_header hdr;
#if __BYTE_ORDER == __BIG_ENDIAN
/* Reserved */
uint32_t Res01 : 18;
/* Error Cause */
uint32_t CMDERR : 14;
/* Command Header of Command */
uint32_t CMDHDR;
#else
/* Error Cause */
uint32_t CMDERR : 14;
/* Reserved */
uint32_t Res01 : 18;
/* Command Header of Command */
uint32_t CMDHDR;
#endif
} __attribute__ ((packed));
struct __fw_evt_sdd
{
struct __fw_evt_header hdr;
#if __BYTE_ORDER == __BIG_ENDIAN
/* Time Stamp */
uint32_t TIME_STAMP : 16;
/* Analog line operating mode */
uint32_t OPMODE : 8;
/* Reserved */
uint32_t Res01 : 3;
/* Event code */
uint32_t EVT : 5;
#else
/* Event code */
uint32_t EVT : 5;
/* Reserved */
uint32_t Res01 : 3;
/* Analog line operating mode */
uint32_t OPMODE : 8;
/* Time Stamp */
uint32_t TIME_STAMP : 16;
#endif
} __attribute__ ((packed));
struct __fw_evt_sig
{
struct __fw_evt_header hdr;
#if __BYTE_ORDER == __BIG_ENDIAN
/* Time Stamp */
uint32_t TIME_STAMP : 16;
/* Reserved */
uint32_t Res01 : 4;
/* DTMF Key */
uint32_t DTMF_KEY : 4;
/* Reserved */
uint32_t Res02 : 3;
/* Event code */
uint32_t SIGEVT : 5;
#else
/* Event code */
uint32_t SIGEVT : 5;
/* Reserved */
uint32_t Res02 : 3;
/* DTMF Key */
uint32_t DTMF_KEY : 4;
/* Reserved */
uint32_t Res01 : 4;
/* Time Stamp */
uint32_t TIME_STAMP : 16;
#endif
} __attribute__ ((packed));
/* ========================================================================== */
/* Global variables */
/* ========================================================================== */
/* ========================================================================== */
/* Function prototypes */
/* ========================================================================== */
/* ========================================================================== */
/* Function implementation */
/* ========================================================================== */
#ifdef EVENT_LOGGER_DEBUG
static void dxs_el_trace_event_read(DXS_DEVICE_t *pDev,
struct __fw_evt_header *pHdr)
{
EL_IoctlAddLog_t stLog;
stLog.nLogType = EL_LOG_TYPE_EVT_MBX_RD;
stLog.nOrygLogType = EL_MAX_LOG_TYPE;
stLog.nDevType = DXS_LIB_DEV_TYPE;
stLog.nDevNum = pDev->nDevNum;
stLog.nChNum = pHdr->CHAN;
stLog.uLogDetails.stEvt_Mbx_Rd.nCmdLength = 4;
stLog.uLogDetails.stEvt_Mbx_Rd.nCount = pHdr->LENGTH + 4;
stLog.uLogDetails.stEvt_Mbx_Rd.nDataLength = pHdr->LENGTH;
stLog.uLogDetails.stEvt_Mbx_Rd.pCmd = (char*)pHdr;
if(pHdr->LENGTH == 0)
{
stLog.uLogDetails.stEvt_Mbx_Rd.pDATA = NULL;
}
else
{
stLog.uLogDetails.stEvt_Mbx_Rd.pDATA = (IFX_char_t*)((int16_t*) pHdr + 2);
}
ioctl(pDev->el_fd, EL_ADD_LOG, (int32_t)&stLog);
}
#endif
/**
Dispatch raw off-hook event
\param pCh - pointer to DXS channel structure
\return none
*/
static void dxs_evt_offhook_raw (DXS_CHANNEL_t *pCh)
{
DXS_Event_t evt = {0};
evt.dev = pCh->pParent->nDevNum;
evt.ch = pCh->nCh;
evt.id = DXS_EVENT_FXS_RAW_OFFHOOK;
DXS_EventDispatch(pCh->pParent, &evt);
}
/**
Dispatch raw on-hook event
\param pCh - pointer to DXS channel structure
\return none
*/
static void dxs_evt_onhook_raw (DXS_CHANNEL_t *pCh)
{
DXS_Event_t evt = {0};
evt.dev = pCh->pParent->nDevNum;
evt.ch = pCh->nCh;
evt.id = DXS_EVENT_FXS_RAW_ONHOOK;
DXS_EventDispatch(pCh->pParent, &evt);
}
/**
Function dxs_cerr_ack
\param pDev - pointer to DXS device structure
\return
- DXS_status_t
*/
static int32_t dxs_cerr_ack(DXS_DEVICE_t *pDev)
{
uint32_t cerr_ack_cmd = 0x0600e000;
return DXS_CmdWrite(pDev, &cerr_ack_cmd);
}
/**
Function dxs_event_sdd
\param pDev - pointer to DXS device structure
\param pSddEvt - pointer to SDD event structure
*/
static void dxs_event_sdd(DXS_DEVICE_t *pDev, struct __fw_evt_sdd *pSddEvt)
{
DXS_Event_t dxs_event = {0};
int32_t ret;
uint8_t nTmp;
DXS_CHANNEL_t *pCh = &pDev->pChannel[pSddEvt->hdr.CHAN];
dxs_event.dev = pDev->nDevNum;
dxs_event.ch = pSddEvt->hdr.CHAN;
switch (pSddEvt->EVT)
{
case EVT_SDD_OTEMP:
dxs_event.id = DXS_EVENT_OVERTEMP;
DXS_EventDispatch(pDev, &dxs_event);
break;
case EVT_SDD_OTEMP_FIN:
DXS_SDD_LineModeSet(pCh, DXS_LINE_FEED_DISABLED);
dxs_event.id = DXS_EVENT_OVERTEMP_END;
DXS_EventDispatch(pDev, &dxs_event);
break;
case EVT_SDD_LT_FIN:
#if defined(DXS_FEAT_CAPMEAS) && defined(DXS_FEAT_GR909)
obx_DXS_SDD_OLCalibrationFinished(pCh, 0);
#endif /* DXS_FEAT_CAPMEAS && DXS_FEAT_GR909 */
dxs_event.id = DXS_EVENT_NLT_END;
DXS_EventDispatch(pDev, &dxs_event);
break;
case EVT_SDD_LT_ABORT:
#if defined(DXS_FEAT_CAPMEAS) && defined(DXS_FEAT_GR909)
obx_DXS_SDD_OLCalibrationFinished(pCh, 1);
#endif /* DXS_FEAT_CAPMEAS && DXS_FEAT_GR909 */
dxs_event.id = DXS_EVENT_NLT_ABORT;
DXS_EventDispatch(pDev, &dxs_event);
break;
case EVT_SDD_GF:
dxs_event.id = DXS_EVENT_GROUND_FAULT;
DXS_EventDispatch(pDev, &dxs_event);
break;
case EVT_SDD_GF_FIN:
DXS_SDD_LineModeSet(pCh, DXS_LINE_FEED_DISABLED);
dxs_event.id = DXS_EVENT_GROUND_FAULT_END;
DXS_EventDispatch(pDev, &dxs_event);
break;
case EVT_SDD_GK:
dxs_event.id = DXS_EVENT_GROUND_KEY;
DXS_EventDispatch(pDev, &dxs_event);
break;
case EVT_SDD_GK_FIN:
dxs_event.id = DXS_EVENT_GROUND_KEY_END;
DXS_EventDispatch(pDev, &dxs_event);
break;
case EVT_SDD_OPC:
case EVT_SDD_OMI:
{
uint8_t opmode_changed = (pSddEvt->EVT == EVT_SDD_OPC) ? 1 : 0;
/*DXS_DCDC_HW_Lock(pCh);*/
obx_DXS_SDD_OpmodeUpdate(pCh, pSddEvt->OPMODE, opmode_changed);
/*DXS_DCDC_HW_UnLock(pCh);*/
/* For some OPC events more actions are needed */
if (opmode_changed)
{
#ifdef DXS_FEAT_HSM
/* Set hook-state to ONHOOK if opmode was changed to disabled */
if (pSddEvt->OPMODE == DXS_LINE_FEED_DISABLED)
{
#ifdef DXS_FEAT_CID
if (DXS_CID_EventOpcDisabled(pCh))
#endif
{
DXS_Dial_HookStateSetOnhook(pCh);
}
}
#endif
/* Calibration ends with an opmode change */
if (pSddEvt->OPMODE != DXS_LINE_FEED_CALIBRATE)
{
ret = DXS_SDD_CalibrationRunningGet(pCh, &nTmp);
/* if calibration was running, signal that it has finished */
if (ret == DXS_statusOk && nTmp == 1)
DXS_SDD_Calibration_Finish(pCh, &dxs_event);
}
}
}
break;
case EVT_SDD_CORCE:
break;
case EVT_SDD_COEFE:
break;
case EVT_SDD_ONH:
/* Report RAW on-hook */
dxs_evt_onhook_raw(pCh);
#ifdef DXS_FEAT_CID
/* Report on-hook */
if (DXS_CID_EventOnhook(pCh, pSddEvt->TIME_STAMP))
#endif
{
#ifdef DXS_FEAT_HSM
DXS_Dial_HookEvent(pCh, 0, pSddEvt->TIME_STAMP);
#else
dxs_event.id = DXS_EVENT_FXS_ONHOOK;
DXS_EventDispatch(pDev, &dxs_event);
#endif
}
break;
case EVT_SDD_OFFH:
/* Report RAW off-hook */
dxs_evt_offhook_raw(pCh);
#ifdef DXS_FEAT_FSK
/* Attempt to disable FSK generator */
DXS_FSK_Disable(pCh, 0);
#endif
#ifdef DXS_FEAT_CID
/* Report off-hook */
if (DXS_CID_EventOffhook(pCh, pSddEvt->TIME_STAMP))
#endif
{
#ifdef DXS_FEAT_HSM
DXS_Dial_HookEvent(pCh, 1, pSddEvt->TIME_STAMP);
#else
dxs_event.id = DXS_EVENT_FXS_OFFHOOK;
DXS_EventDispatch(pDev, &dxs_event);
#endif
}
break;
case EVT_SDD_OPM_DIS:
break;
default:
break;
}
}
/**
Function dxs_event_sig
\param pDev - pointer to DXS device structure
\param pSigEvt - pointer to SIG event structure
*/
static void dxs_event_sig(DXS_DEVICE_t *pDev, struct __fw_evt_sig *pSigEvt)
{
DXS_Event_t dxs_event = {0};
#if defined (DXS_FEAT_CID) || defined (DXS_FEAT_UTD) || defined (DXS_FEAT_METERING) || defined (DXS_FEAT_ACMETER)
DXS_CHANNEL_t *pCh = &pDev->pChannel[pSigEvt->hdr.CHAN];
#endif
dxs_event.dev = pDev->nDevNum;
dxs_event.ch = pSigEvt->hdr.CHAN;
switch (pSigEvt->SIGEVT)
{
case EVT_SIG_DTMF_DET:
{
dxs_event.id = DXS_EVENT_DTMF_DIGIT;
switch (pSigEvt->DTMF_KEY)
{
case 0x0:
dxs_event.data.dtmf.digit = 11;
dxs_event.data.dtmf.ascii = '0';
break;
case 0x1:
case 0x2:
case 0x3:
case 0x4:
case 0x5:
case 0x6:
case 0x7:
case 0x8:
case 0x9:
dxs_event.data.dtmf.digit = pSigEvt->DTMF_KEY;
dxs_event.data.dtmf.ascii = '0' + pSigEvt->DTMF_KEY;
break;
case 0xA:
dxs_event.data.dtmf.digit = 10;
dxs_event.data.dtmf.ascii = '*';
break;
case 0xB:
dxs_event.data.dtmf.digit = 12;
dxs_event.data.dtmf.ascii = '#';
break;
case 0xC:
dxs_event.data.dtmf.digit = 28;
dxs_event.data.dtmf.ascii = 'A';
break;
case 0xD:
dxs_event.data.dtmf.digit = 29;
dxs_event.data.dtmf.ascii = 'B';
break;
case 0xE:
dxs_event.data.dtmf.digit = 30;
dxs_event.data.dtmf.ascii = 'C';
break;
case 0xF:
dxs_event.data.dtmf.digit = 31;
dxs_event.data.dtmf.ascii = 'D';
break;
default:
break;
}
#ifdef DXS_FEAT_CID
/* Report dtmf tone */
if (DXS_CID_EventDtmf(pCh, &dxs_event))
#endif
{
DXS_EventDispatch(pDev, &dxs_event);
break;
}
}
break;
#if defined(DXS_FEAT_FSK) || defined(DXS_FEAT_CID)
case EVT_SIG_CIS_REQ:
dxs_event.id = DXS_EVENT_CID_REQ_DATA;
DXS_EventDispatch(pDev, &dxs_event);
#ifdef DXS_FEAT_CID
/* inform FSK state machine */
DXS_CID_FSK_EventInfo (pCh, DXS_CID_FSK_DATA_REQUEST);
#endif /* DXS_FEAT_CID */
break;
case EVT_SIG_CIS_BUF:
dxs_event.id = DXS_EVENT_CID_BUF_UNDERFLOW;
DXS_EventDispatch(pDev, &dxs_event);
#ifdef DXS_FEAT_CID
/* inform FSK state machine */
DXS_CID_FSK_EventInfo (pCh, DXS_CID_FSK_DATA_BUF);
#endif /* DXS_FEAT_CID */
break;
case EVT_SIG_CIS_FIN:
dxs_event.id = DXS_EVENT_CID_END;
DXS_EventDispatch(pDev, &dxs_event);
#ifdef DXS_FEAT_CID
/* inform FSK state machine */
DXS_CID_FSK_EventInfo (pCh, DXS_CID_FSK_DATA_FINISH);
#endif /* DXS_FEAT_CID */
break;
#endif /* DXS_FEAT_FSK || DXS_FEAT_CID */
#ifdef DXS_FEAT_UTD
case EVT_SIG_UTD_START:
dxs_event.id = DXS_EVENT_TONE_DET_CPT;
dxs_event.data.tone_det.index = DXS_UTD_ToneIdxGet(pCh);
DXS_EventDispatch(pDev, &dxs_event);
break;
case EVT_SIG_UTD_END:
dxs_event.id = DXS_EVENT_TONE_DET_CPT_END;
dxs_event.data.tone_det.index = DXS_UTD_ToneIdxGet(pCh);
DXS_EventDispatch(pDev, &dxs_event);
break;
#endif /* DXS_FEAT_UTD */
#ifdef DXS_FEAT_METERING
case EVT_SIG_TTX_FIN:
obx_DXS_SIG_MeterPulseStatusClear(pCh);
dxs_event.id = DXS_EVENT_METERING_END;
DXS_EventDispatch(pDev, &dxs_event);
break;
#endif /* DXS_FEAT_METERING */
#ifdef DXS_FEAT_ACMETER
case EVT_SIG_AC_LM_FIN:
obx_DXS_SDD_ACLM_Finish(pCh);
break;
#endif /* DXS_FEAT_ACMETER */
default:
break;
}
}
/**
Function dxs_handle_event
\param pDev Pointer to DXS device structure.
\param pHdr Pointer to __fw_evt_header structure.
*/
static void dxs_handle_event(DXS_DEVICE_t *pDev, struct __fw_evt_header *pHdr)
{
/* Discard all events here while event handling is globally disabled. */
if (pDev->obxDiscardEvents != 0)
{
#ifdef DXS_CMD_PRINT
/* The boot finished event has only one word all other two words. */
if (pHdr->MOD == 7 && pHdr->ECMD == 0)
fprintf (stderr, "[evt] dev:%d %08X DISCARDED\n",
pDev->nDevNum, *((uint32_t *)pHdr));
else
fprintf (stderr, "[evt] dev:%d %08X %08X DISCARDED\n", pDev->nDevNum,
*((uint32_t *)pHdr), *((uint32_t *)pHdr + 1));
#endif /* DXS_CMD_PRINT */
return;
}
if (pHdr->MOD == 7 && pHdr->ECMD == 0)
{
/* Boot Finished Event */
uint8_t i;
#ifdef DXS_CMD_PRINT
fprintf (stderr, "[evt] dev:%d %08X\n", pDev->nDevNum,
*((uint32_t *)pHdr));
#endif /* DXS_CMD_PRINT */
/* clear flags and capabilities */
dxs_caps_vers_forget(pDev);
pDev->flags &= ~DXS_DEV_PRAM_PATCH_DOWNLOADED;
for (i=0; i<CH_PER_DEVICE; i++)
{
DXS_CHANNEL_t *pCh = &pDev->pChannel[i];
pCh->flags &= ~DXS_CH_BBD_DOWNLOADED;
}
if (pDev->bWaitingInPatchDwld)
{
sem_post(&pDev->mtxPatchDwld);
pDev->bWaitingInPatchDwld = 0;
}
else
{
/* FW crash */
DXS_Event_t dxs_event = {0};
dxs_event.dev = pDev->nDevNum;
dxs_event.ch = 0;
dxs_event.id = DXS_EVENT_FAULT_FW_WATCHDOG;
DXS_EventDispatch(pDev, &dxs_event);
}
}
if (pHdr->MOD == 7 && pHdr->ECMD == 11)
{
/* Internal Error Event */
#ifdef DXS_CMD_PRINT
fprintf (stderr, "[evt] dev:%d %08X %08X\n", pDev->nDevNum,
*((uint32_t *)pHdr), *((uint32_t *)pHdr + 1));
#endif /* DXS_CMD_PRINT */
}
else if (pHdr->MOD == 7 && pHdr->ECMD == 2)
{
/* Command Error Event */
struct __fw_evt_cmd_err *pCerr = (struct __fw_evt_cmd_err *)pHdr;
DXS_Event_t dxs_event = {0};
#ifdef DXS_CMD_PRINT
fprintf (stderr, "[evt] dev:%d %08X %08X %08X\n", pDev->nDevNum,
*((uint32_t *)pHdr), *((uint32_t *)pHdr + 1),
*((uint32_t *)pHdr + 2));
#endif /* DXS_CMD_PRINT */
/* acknowledge command error */
dxs_cerr_ack(pDev);
dxs_event.dev = pDev->nDevNum;
dxs_event.id = DXS_EVENT_DEBUG_CERR;
dxs_event.data.cerr.reason = pCerr->CMDERR;
dxs_event.data.cerr.command = pCerr->CMDHDR;
DXS_EventDispatch(pDev, &dxs_event);
}
else if (pHdr->MOD == 1 && pHdr->ECMD == 4)
{
/* SDD Event */
#ifdef DXS_CMD_PRINT
fprintf (stderr, "[evt] dev:%d %08X %08X\n", pDev->nDevNum,
*((uint32_t *)pHdr), *((uint32_t *)pHdr + 1));
#endif /* DXS_CMD_PRINT */
dxs_event_sdd(pDev, (struct __fw_evt_sdd *)pHdr);
}
else if (pHdr->MOD == 1 && pHdr->ECMD == 5)
{
/* Signaling Event */
#ifdef DXS_CMD_PRINT
fprintf (stderr, "[evt] dev:%d %08X %08X\n", pDev->nDevNum,
*((uint32_t *)pHdr), *((uint32_t *)pHdr + 1));
#endif /* DXS_CMD_PRINT */
dxs_event_sig(pDev, (struct __fw_evt_sig *)pHdr);
}
}
/**
Function dxs_outbox_handler
\param arg - pointer to device
*/
static void *dxs_outbox_handler (void *arg)
{
DXS_DEVICE_t *pDev = (DXS_DEVICE_t *)arg;
while (pDev->obxThreadStop == 0)
{
uint8_t words16, words32, msg_len/*in 32-bit words*/, tmp,
words32_remaining;
/* 16 bit index points to the header of message that has been read out */
uint8_t pos1;
int32_t ret;
ret = sem_wait (&pDev->obxSemaphore);
if (ret == EINTR)
pthread_exit((void *)DXS_statusOk);
/* TODO:- take additional mutex (trylock ???) - concurrency with CmdRead() */
/* TODO: check if SPI works */
/* read the outbox */
DXS_ObxRead(pDev, pDev->dmbx, &words16);
if (words16 == 0)
{
continue;
}
if (words16 & 1)
{
/* TODO: is this an error ? */
fprintf (stderr, "OutMbx: Odd word count: %d\n", words16);
continue;
}
words32_remaining = words32 = words16 >> 1;
pos1 = 0;
while ((pos1/2) < words32)
{
struct __fw_evt_header *pHdr = (struct __fw_evt_header *)(pDev->dmbx+pos1);
uint8_t attempts = 0, out_of_sync = 0;
/* get the length of the message in 32-bit words */
msg_len = (pHdr->LENGTH + sizeof(struct __fw_evt_header)) >> 2;
while (msg_len > words32_remaining)
{
/* read 2nd segment */
/* read the outbox again */
DXS_ObxRead(pDev, pDev->dmbx + words16, &tmp);
/* here we don't expect that nothing is read */
words16 += tmp;
words32 = words16 >> 1;
words32_remaining += (tmp >> 1);
if (++attempts == 3)
{
out_of_sync = 1;
break;
}
}
if (out_of_sync)
{
int32_t i;
fprintf (stderr, "Mailbox out of sync.\n");
/* print the read buffer */
fprintf (stderr, "READ BUFFER:\n");
for (i=0; i<(sizeof(pDev->dmbx)>>1); i+=2)
fprintf (stderr, "%02d: %04X %04X\n", i, pDev->dmbx[i], pDev->dmbx[i+1]);
fprintf (stderr, "pos=%02d\n", pos1);
break;
}
/* decode message, buffer it, advance to the next */
if (pHdr->Res00 == 0 &&
pHdr->CMD == 9 &&
(pHdr->MOD == 1 || pHdr->MOD == 7) &&
(pHdr->LENGTH == 0 || pHdr->LENGTH == 4 || pHdr->LENGTH == 8))
{
#ifdef EVENT_LOGGER_DEBUG
dxs_el_trace_event_read(pDev, pHdr);
#endif
/* this is an event header */
dxs_handle_event(pDev, pHdr);
}
else
{
/* this is a command header */
int i;
uint32_t *pw32;
for (i=0, pw32 = (uint32_t *)pHdr; i<msg_len; i++, pw32++)
{
fifo_put(pDev->cmd_obx_queue, NULL, *pw32);
}
sem_post(&pDev->obxCmdDataSem);
}
pos1 += (msg_len * 2);
words32_remaining -= msg_len;
/* This implementation works in IRQ edge mode. */
#if 0
/* If the IRQ is edge triggered we have to make sure that the
mailbox is empty when we exit the loop. There won't be another IRQ
generated if there is still data left in outbox. */
if (pos1 >= words16)
{
DXS_ObxRead(pDev, pDev->dmbx, &words16);
if (words16 == 0)
break;
if (words16 & 1)
{
/* TODO: is this an error ? */
fprintf (stderr, "OutMbx: Odd word count: %d\n", words16);
break;
}
words32 = words16 >> 1;
pos1 = 0;
}
#endif
}
}
pthread_exit((void *)DXS_statusOk);
}
/**
Function dxs_outbox_handler_init
\param pDev - pointer to DXS device structure
\return
- DXS_status_t
*/
int32_t dxs_outbox_handler_init(DXS_DEVICE_t *pDev)
{
int32_t ret;
sem_init(&pDev->obxSemaphore, 0, 0);
sem_init(&pDev->obxCmdDataSem, 0, 0);
pDev->obxThreadStop = 0;
pDev->obxDiscardEvents = 1;
ret = pthread_create(&pDev->obxThread, NULL, dxs_outbox_handler, (void *)pDev);
if (ret)
{
/* error code */
DXS_RETURN(DXS_statusThreadCreatError);
}
return DXS_statusOk;
}
/**
Function dxs_outbox_handler_exit
\param pDev - pointer to DXS device structure
\return
- DXS_status_t
*/
int32_t dxs_outbox_handler_exit(DXS_DEVICE_t *pDev)
{
int32_t ret;
void *status;
/* condition to stop */
pDev->obxThreadStop = 1;
/* let the thread run */
sem_post (&pDev->obxSemaphore);
/* wait for the thread to exit */
ret = pthread_join(pDev->obxThread, &status);
if (ret || status != DXS_statusOk)
{
/* error code */
DXS_RETURN(DXS_statusThreadStopError);
}
sem_destroy(&pDev->obxSemaphore);
sem_destroy(&pDev->obxCmdDataSem);
return ret;
}
/**
Function dxs_outbox_handler_enable
This enables the event handling. After init the outbox is processed but
events are discarded until this function is called.
\param pDev - pointer to DXS device structure
*/
void dxs_outbox_handler_enable(DXS_DEVICE_t *pDev)
{
/* From now on process all events. */
pDev->obxDiscardEvents = 0;
}