| /****************************************************************************** |
| |
| 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; |
| } |