Integrator Resources

The official home for NAI Support

Not sure where to start? Try Quick Start Guide or ask a question below!

Toggle Components with Visual Button
JavaScript Form Processing

RG EtherInterrupts

RG EtherInterrupts Sample Application (SSK 1.x)

Overview

The RG EtherInterrupts sample application demonstrates how to receive IRIG (Inter-Range Instrumentation Group) timecode interrupt notifications over an Ethernet network using the NAI Software Support Kit (SSK 1.x) Interrupt Driven Response (IDR) mechanism. When a configured IRIG event occurs — such as a BIT data loss, reference loss, 1PPS output, or other timecode status change — the board generates an interrupt and sends a UDP packet to a remote host containing the BIT latched status register value and any embedded read/write command results. Your application runs a UDP server that listens for these packets and decodes the IRIG status information.

This is the Ethernet IDR-only variant of the IRIG interrupt handling samples. It operates as a self-contained single-file application with its own socket server and IDR command construction. For the full ISR-based IRIG interrupt sample that supports local interrupt delivery, see the RG Interrupts guide. For basic IRIG configuration and operation without interrupt delivery, see the RG BasicOps guide.

This sample supports the following IRIG module type: RG1.

IRIG modules support two interrupt status categories: BIT Status (Built-In Test) and General Status. Valid BIT statuses include Data Loss and Software Fault. Valid General statuses include Reference Loss, Receiving Reference, Reference Pulse Received, Interrupt on 1PPS Output Going High, Received Control Bits Changed, Control Bits Received, Change in Reference Source, Event Detected, Programmable-Duration User Interrupt, DST Adjust, and Test Interrupt. This sample defaults to monitoring the BIT Data Loss latched status (NAIBRD_IRIG_EVTMAP_STATUS_BIT_DATA_LOSS_LATCHED) in the BIT category (NAIBRD_IRIG_EVTMAP_CATEGORY_BIT).

Why Ethernet IDR for IRIG?

IRIG timecode modules provide critical timing references for avionics, test ranges, and distributed measurement systems. Ethernet IDR enables remote monitoring of IRIG timing health:

  • Remote timing health monitoring — your application can monitor IRIG reference integrity from any host reachable over the network, without requiring a physical bus connection to the board.

  • Self-contained notification — each IDR packet contains the BIT latched status register value and automatically clears it, so your application receives the fault information and re-arms the interrupt in a single network transaction.

  • No ISR infrastructure — there is no ISR to install, no IRQ to manage, and no thread synchronization to worry about. Your application simply listens on a UDP socket.

  • Simplified deployment — the entire application is a single source file with no dependencies on shared interrupt utility libraries.

The tradeoff is that Ethernet IDR requires a Gen4 or later board, and network latency is inherently higher than a local ISR. For latency-critical timing applications where the host is directly attached to the board, standard ISR delivery may be more appropriate.

Prerequisites

Before running this sample, make sure you have:

  • An NAI board with an IRIG module installed (RG1).

  • A Gen4 or later board with Ethernet connectivity. Earlier board generations do not support the Ethernet IDR protocol.

  • SSK 1.x installed on your development host.

  • The sample applications built. Refer to the SSK 1.x build instructions for your platform if you have not already compiled them.

  • Network connectivity between the host running this sample and the board. The host must be reachable at the IP address configured in the IDR settings (default: 192.168.1.100).

How to Run

Launch the RG_EtherInterrupts executable from your build output directory. On startup the application looks for a configuration file (default_RGEthInterrupts.txt). On the first run, this file will not exist — the application will present an interactive board menu where you configure a board connection, card index, and module slot. You can save this configuration so that subsequent runs skip the menu and connect automatically. Once connected, the application prompts you for channel selection and IDR network settings, then begins listening for IDR packets from the board.

Board Connection and Module Selection

Note
This startup sequence is common to all NAI sample applications. The board connection and module selection code shown here is not specific to IRIG. For details on board connection configuration, see the First Time Setup Guide.

The main() function follows a standard SSK 1.x startup flow:

  1. Call naiapp_RunBoardMenu() to load a saved configuration file (if one exists) or present the interactive board menu. The configuration file (default_RGEthInterrupts.txt) is not included with the SSK — it is created when the user saves their connection settings from the board menu. On the first run, the menu will always appear.

  2. Query the user for card index and module number.

  3. Retrieve the module ID with naibrd_GetModuleID() to verify an IRIG module is installed at the selected slot.

  4. Call Run_IRIG_EthInterrupt() to execute the IDR interrupt workflow.

#if defined (__VXWORKS__)
int32_t RG_EtherInterrupts(void)
#else
int32_t main(void)
#endif
{
   bool_t stop = FALSE;
   int32_t cardIndex;
   int32_t moduleCnt;
   int32_t module;
   uint32_t moduleID = 0;
   int8_t inputBuffer[80];
   int32_t inputResponseCnt;

   if (naiapp_RunBoardMenu(CONFIG_FILE) == TRUE)
   {
      while (stop != TRUE)
      {
         stop = naiapp_query_CardIndex(naiapp_GetBoardCnt(), 0, &cardIndex);
         if (stop != TRUE)
         {
            check_status(naibrd_GetModuleCount(cardIndex, &moduleCnt));
            stop = naiapp_query_ModuleNumber(moduleCnt, 1, &module);
            if (stop != TRUE)
            {
               moduleID = naibrd_GetModuleID(cardIndex, module);
               if ((moduleID != 0))
               {
                  Run_IRIG_EthInterrupt(cardIndex, module, moduleID);
               }
            }
         }

         printf("\nType Q to quit or Enter key to restart application:\n");
         stop = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR,
                                             inputBuffer, &inputResponseCnt);
      }
   }

   printf("\nType the Enter key to exit the program: ");
   naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
   naiapp_access_CloseAllOpenCards();

   return 0;
}

Unlike the multi-file interrupt samples, this application does not call shared initialization functions. All configuration is handled directly within Run_IRIG_EthInterrupt() using local variables and compile-time defaults.

Important

Common connection errors you may encounter at this stage:

  • No board found — verify that the board is powered on and physically connected. Check that the configuration file lists the correct interface and address.

  • Connection timeout — confirm network settings (for Ethernet connections) or bus configuration (for PCI/PCIe). Firewalls and IP mismatches are frequent causes.

  • Invalid card or module index — indices are zero-based for cards and one-based for modules. Ensure the values you pass match your hardware setup.

  • Module not present at selected slot — the slot you selected does not contain an IRIG module. Use the board menu to verify which slots are populated.

Program Structure

The RG EtherInterrupts sample is implemented entirely in a single source file: RG_EtherInterrupts.c. This self-contained design includes the board connection logic, IRIG interrupt configuration, IDR command construction, socket server, and UDP message parsing — all without relying on shared interrupt utility libraries.

Compile-Time Defaults

The sample defines its IDR configuration as compile-time constants:

#define NAI_IRIG_CHANNEL1_INTERRUPT_VECTOR        0x000000A1u

#define DEF_IRIG_IDR_ID                           2
#define DEF_IRIG_ETHER_CMD_COUNT                  2
#define DEF_IRIG_RESPONSE_PROTOCOL                ETHER_GEN4_UDP_PROTOCOL
#define DEF_IRIG_RESPONSE_IP_LEN                  ETHER_GEN4_IPv4_ADDR_LEN

static uint16_t DEF_IRIG_RESPONSE_PORT = 52802;
static uint8_t  DEF_IRIG_RESPONSE_IP_ADDR[] = { 192,168,1,100 };
  • NAI_IRIG_CHANNEL1_INTERRUPT_VECTOR — the interrupt vector assigned to channel 1’s IRIG interrupt. This vector links the module interrupt to the IDR engine.

  • DEF_IRIG_IDR_ID — the IDR ID used to register this interrupt handler with the board’s IDR engine. Must be unique across all active IDR configurations.

  • DEF_IRIG_ETHER_CMD_COUNT — the number of embedded Ethernet commands in the IDR (one read + one write = 2).

  • DEF_IRIG_RESPONSE_PORT and DEF_IRIG_RESPONSE_IP_ADDR — the UDP destination where the board sends IDR packets. The user can override these interactively at runtime.

IRIG Event-Mapped Interrupt Model

IRIG modules use an event-mapped interrupt model that differs from simpler I/O modules. Instead of a single status register, IRIG interrupts are organized into:

  • Status types — individual event conditions (e.g., NAIBRD_IRIG_EVTMAP_STATUS_BIT_DATA_LOSS_LATCHED for BIT data loss).

  • Category types — groups of related status types (e.g., NAIBRD_IRIG_EVTMAP_CATEGORY_BIT for all BIT-related events).

The interrupt vector and steering are configured per category, while the trigger type and enable are configured per individual status type. This sample defaults to monitoring BIT Data Loss in the BIT category.

Gen4 Ethernet Requirement Check

The first thing Run_IRIG_EthInterrupt() does is verify that the connected board supports Gen4 Ethernet commands:

bGen4IRIGIDRCommands = SupportsGen4Ether(cardIndex);

If the board does not support Gen4, the application prints an error and exits. There is no fallback to an older protocol — you must use a Gen4 or later board for Ethernet IDR.

In your own application, always call SupportsGen4Ether() before attempting any IDR configuration.

Important

Common Errors

  • "IRIG Ethernet Interrupt Support Prior to Generation 4 Ethernet commands currently not supported" — the connected board does not support IDR. Verify you are using a Gen4 or later board. Check the board’s firmware version if you believe the hardware should support Gen4.

IDR Configuration

After the Gen4 check passes and the user selects a channel and confirms network settings, the sample configures the IDR. The process begins by clearing any pending interrupts for the default status type:

/* Clear channel status */
check_status(naibrd_IRIG_ClearEventMappedStatus(cardIndex, module, channel,
   NAIBRD_IRIG_EVTMAP_STATUS_BIT_DATA_LOSS_LATCHED));

Building IDR Commands

Init_IRIG_IDRCommands() constructs the two embedded Ethernet commands that the board executes when the IRIG interrupt fires:

static nai_status_t Init_IRIG_IDRCommands(int32_t cardIndex, int32_t module,
   int32_t channel, uint8_t commands[], uint16_t *cmdcount, uint16_t *cmdlen)
{
   nai_status_t status = NAI_SUCCESS;
   uint16_t msgIndex = 0;
   uint16_t idrcmdcnt = 0;
   uint16_t ethcmdlen = 0;
   uint32_t moduleOffset;

   status = check_status(naibrd_GetModuleOffset(cardIndex, module, &moduleOffset));
   if (status == NAI_SUCCESS)
   {
      if (bGen4IRIGIDRCommands)
      {
         MakeIRIGReadRegsCommand(bGen4IRIGIDRCommands, msgIndex, moduleOffset,
                                 channel, commands, &ethcmdlen);
         msgIndex += ethcmdlen;
         idrcmdcnt++;

         MakeIRIGWriteRegsCommand(bGen4IRIGIDRCommands, msgIndex, moduleOffset,
                                  channel, commands, &ethcmdlen);
         msgIndex += ethcmdlen;
         idrcmdcnt++;

         *cmdcount = idrcmdcnt;
         *cmdlen = msgIndex;
      }
   }
   return status;
}

The function retrieves the module’s register offset with naibrd_GetModuleOffset(), then constructs two commands:

  1. Read command — reads the NAI_IRIG_GEN5_REG_COM_BIT_INTERRUPTS_LAT_ADD register to capture the BIT latched interrupt status.

  2. Write command — writes 0xFFFFFFFF to the same register to clear all latched status bits and re-arm the interrupt.

Read Command Construction

MakeIRIGReadRegsCommand() builds a Gen4 Ethernet read command targeting the BIT latched status register:

seqno = 0;
regaddr = moduleOffset + bitLatchedStatusOffset;
count = 1;
stride = 4;
msgIndex = (uint16_t)nai_ether_MakeReadMessage(&commands[startIndex], seqno,
   NAI_ETHER_GEN4, NAI_INTF_ONBOARD, regaddr, stride, count, NAI_REG32);

The register address is the module offset plus NAI_IRIG_GEN5_REG_COM_BIT_INTERRUPTS_LAT_ADD. A single 32-bit register is read (count = 1, stride = 4).

Write Command Construction

MakeIRIGWriteRegsCommand() builds a Gen4 Ethernet write command that clears all latched BIT status bits:

uint32_t data = 0xFFFFFFFFu;  /* Clear interrupt bits */

seqno = 0;
regaddr = moduleOffset + bitLatchedStatusOffset;
count = 1;
stride = 4;

msgIndex = (uint16_t)nai_ether_BeginWriteMessage(&commands[startIndex], seqno,
   NAI_ETHER_GEN4, NAI_INTF_ONBOARD, regaddr, stride, count, NAI_REG32);
msgIndex = (uint16_t)nai_ether_WriteMessageData(&commands[startIndex], msgIndex,
   NAI_REG32, &data, NAI_REG32, count);
msgIndex = (uint16_t)nai_ether_FinishMessage(&commands[startIndex], msgIndex, NAI_ETHER_GEN4);

The write command uses the three-step message construction pattern and writes 0xFFFFFFFF to clear all bits in the BIT latched status register. This clears all BIT interrupt sources at once, unlike the RTD samples which clear only the specific channel’s bit.

Installing the IDR Configuration

After the commands are built, the sample installs the IDR configuration:

status = check_status(naibrd_Ether_ClearIDRConfig(cardIndex, (uint16_t)DEF_IRIG_IDR_ID));

Init_IRIG_IDRCommands(cardIndex, module, channel, commands, &cmdcount, &cmdlength);
for (i = 0; i < DEF_IRIG_RESPONSE_IP_LEN; i++)
   ip[i] = DEF_IRIG_RESPONSE_IP_ADDR[i];
port = DEF_IRIG_RESPONSE_PORT;
status = check_status(naibrd_Ether_SetIDRConfig(cardIndex, (uint16_t)DEF_IRIG_IDR_ID,
   protocol, iplen, ip, port, vector, cmdcount, cmdlength, commands));

The key API calls are:

  • naibrd_Ether_ClearIDRConfig() — clears any existing IDR configuration for the specified IDR ID.

  • naibrd_Ether_SetIDRConfig() — installs the complete IDR configuration: protocol, IP length, destination IP address, port, interrupt vector, command count, command byte length, and the command buffer.

Important

Common Errors

  • naibrd_Ether_SetIDRConfig() fails — verify the IDR ID is valid and that no other IDR is already configured with the same ID. Always call naibrd_Ether_ClearIDRConfig() first.

  • Incorrect register address — the module offset must be queried at runtime with naibrd_GetModuleOffset(). Hardcoded offsets will fail on different board configurations.

Interrupt Configuration

After the IDR is configured, the sample sets up the IRIG module’s event-mapped interrupt system:

/* Configure to generate interrupts */
check_status(naibrd_IRIG_SetEventMappedInterruptTriggerType(cardIndex, module, channel,
   statusType, NAIBRD_IRIG_EDGE_INTERRUPT));
check_status(naibrd_IRIG_SetEventMappedInterruptVector(cardIndex, module, channel,
   categoryType, (uint32_t)vector));
check_status(naibrd_IRIG_SetEventMappedInterruptSteering(cardIndex, module, channel,
   categoryType, NAIBRD_INT_STEERING_ON_BOARD_1));
check_status(naibrd_IRIG_SetEventMappedInterruptEnable(cardIndex, module, channel,
   statusType, TRUE));

The steps are:

  1. Set trigger type — naibrd_IRIG_SetEventMappedInterruptTriggerType() configures edge triggering (NAIBRD_IRIG_EDGE_INTERRUPT) for the selected status type. Edge triggering fires once when the status condition transitions from inactive to active.

  2. Set vector — naibrd_IRIG_SetEventMappedInterruptVector() assigns the interrupt vector to the category. Note that the vector is set per category (e.g., BIT), not per individual status type. The vector 0x000000A1 must match the vector specified in naibrd_Ether_SetIDRConfig().

  3. Set steering — naibrd_IRIG_SetEventMappedInterruptSteering() routes the interrupt category to NAIBRD_INT_STEERING_ON_BOARD_1, directing it to the board’s onboard processor where the IDR engine runs.

  4. Enable interrupt — naibrd_IRIG_SetEventMappedInterruptEnable() enables the specific status type interrupt for the selected channel.

After configuration, the IDR is started:

status = check_status(naibrd_Ether_StartIDR(cardIndex, (uint16_t)DEF_IRIG_IDR_ID));

From this point forward, any BIT Data Loss event on the configured IRIG channel will trigger a UDP packet to the configured host.

Important

Common Errors

  • No interrupts received — verify that steering is set to NAIBRD_INT_STEERING_ON_BOARD_1 for the BIT category. Using the wrong steering routes the interrupt away from the IDR engine.

  • Interrupt vector mismatch — the vector assigned to the category via naibrd_IRIG_SetEventMappedInterruptVector() must match the vector in naibrd_Ether_SetIDRConfig().

  • Wrong category/status pairing — the vector and steering are set per category, but the trigger type and enable are set per status type. Make sure you configure both levels.

UDP Server and IDR Message Handling

Once the IDR is started and interrupts are enabled, the sample spawns a listener thread that runs a UDP server:

terminateIRIGThread = FALSE;

#if defined (__VXWORKS__)
taskSpawn("uprIRIGHandler", 100, 0, 10000,
          (FUNCPTR)UPR_IRIGEthInt_Handler, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0);
#elif defined (LINUX)
pthread_create(&interruptIRIGThread, NULL, (void*)UPR_IRIGEthInt_Handler, NULL);
#else /* Default Windows */
CreateThread(NULL, 0, UPR_IRIGEthInt_Handler, NULL, 0, NULL);
#endif

The thread handler (UPR_IRIGEthInt_Handler) creates either a TCP or UDP server depending on DEF_IRIG_RESPONSE_PROTOCOL (default is UDP). The UDP server binds to DEF_IRIG_RESPONSE_PORT (52802) and enters a receive loop, calling Decode_UPR_IRIG_IDRMessages() for each incoming packet.

Decoding IDR Packets

Decode_UPR_IRIG_IDRMessages() iterates through the received byte stream, extracting individual Ethernet messages delimited by Gen4 preamble and postamble markers:

static void Decode_UPR_IRIG_IDRMessages(const uint8_t msg[], int32_t msgsize)
{
   uint32_t arraysize = MAX_ETHER_IDR_CMD_CNT * MAX_ETHER_BLOCK_REG_CNT;
   uint16_t responselen;
   uint8_t response[MAX_ETHER_BLOCK_REG_CNT];
   int32_t startIndex;

   startIndex = 0;
   while (startIndex < msgsize)
   {
      if (Parse_IRIG_IDRResponse(startIndex, arraysize, (uint8_t *)msg,
                                 &responselen, MAX_ETHER_BLOCK_REG_CNT, response))
      {
         startIndex += responselen;
         Display_Decoded_IRIG_IDRResponse(responselen, response);
      }
   }
}

Parse_IRIG_IDRResponse() scans for ETHER_GEN4_PREAMBLE to locate the start of each message, then copies bytes until it finds ETHER_GEN4_POSTAMBLE.

Displaying Decoded Results

Display_Decoded_IRIG_IDRResponse() decodes the message header and displays the results:

offset = nai_ether_DecodeMessageHeader(response, responselen, &seq, &tc, gen, &size);

/* Parse IDR Response Sequence Value */
seqrsptype = seq & ETHER_GEN4_SEQ_UPR_MASK;
seqrspidindex = (seq & (ETHER_GEN4_SEQ_ID_MASK)) >> ETHER_GEN4_SEQ_ID_SHIFT;
seqcmdindex = (seq & (ETHER_GEN4_SEQ_CMD_MASK)) >> ETHER_GEN4_SEQ_CMD_SHIFT;

The sequence number encodes three pieces of information:

  • Response type (bits 15-14) — identifies whether this is an unprompted TDR or IDR response.

  • IDR ID (bits 13-10) — the IDR ID index (value is ID minus 1).

  • Command index (bits 9-6) — which command in the IDR configuration produced this response.

For read command responses (NAI_ETHER_TYPECODE_RSP_COMMAND_COMPLETE_READ_4), the handler extracts 32-bit data values from the payload. The BIT latched status register is a bitmask where each bit corresponds to a different BIT condition. Consult your IRIG module’s manual for the specific bit definitions.

For write command responses (NAI_ETHER_TYPECODE_RSP_COMMAND_COMPLETE_WRITE_4), the handler confirms the status register was cleared.

Important

Common Errors

  • No IDR packets received — verify the destination IP address matches the host running the application. Check that no firewall is blocking UDP port 52802. Confirm the board has network connectivity.

  • IDR packets arrive but show status 0x0 — no BIT fault condition exists. The IRIG module must detect an actual BIT event (e.g., data loss) to trigger the interrupt.

  • Packets received but parsing fails — verify the protocol matches between the IDR configuration and the server. The sample defaults to UDP; using TCP requires both sides to agree.

Cleanup

When the user presses the quit key, the sample terminates the listener thread and stops the IDR engine:

terminateIRIGThread = TRUE;

/* Stop the IDR */
status = check_status(naibrd_Ether_StopIDR(cardIndex, (uint16_t)DEF_IRIG_IDR_ID));

The cleanup sets the terminateIRIGThread flag, which causes the UDP receive loop to exit on the next iteration. Then naibrd_Ether_StopIDR() halts the IDR engine on the board.

Note that this sample does not explicitly call naibrd_Ether_ClearIDRConfig() during cleanup. In a production application, you should also call naibrd_Ether_ClearIDRConfig() and naibrd_IRIG_SetEventMappedInterruptEnable() with FALSE to fully release board resources and disable interrupts.

Troubleshooting Reference

Note
This section summarizes errors covered in the preceding sections and provides additional diagnostics. Consult your module’s manual for hardware-specific diagnostics.
Error / Symptom Possible Causes Suggested Resolution

"IRIG Ethernet Interrupt Support Prior to Generation 4 Ethernet commands currently not supported"

Board does not support Gen4 Ethernet protocol.

Use a Gen4 or later board. Check firmware version with your NAI support contact.

No IDR packets received

Destination IP address does not match the host. Firewall blocking UDP port. Board has no network connectivity. IDR not started.

Verify IP address matches the host running the application. Open UDP port 52802 in the firewall. Ping the board to confirm connectivity. Confirm naibrd_Ether_StartIDR() was called after naibrd_Ether_SetIDRConfig().

IDR packets arrive but show status 0x0

No BIT fault condition present. Interrupt already cleared by a previous cycle.

The IRIG module must detect an actual BIT event to trigger the interrupt. Verify the IRIG reference source is configured and that a fault condition can occur.

BIT Data Loss not triggering

IRIG reference connected and stable. No data loss condition to detect.

Disconnect or disrupt the IRIG reference signal to trigger a data loss event for testing purposes.

Interrupts fire once but do not re-arm

Latched status not cleared after processing. Write command in IDR not executing.

Verify the IDR write command targets the correct BIT latched status register. Check that naibrd_Ether_SetIDRConfig() included both the read and write commands (command count = 2).

naibrd_Ether_SetIDRConfig() returns error

Stale IDR configuration not cleared. Invalid IDR ID.

Call naibrd_Ether_ClearIDRConfig() before naibrd_Ether_SetIDRConfig(). Verify the IDR ID is within the valid range for your board.

Event-mapped interrupt category mismatch

Vector or steering set on wrong category. Enable set on wrong status type.

Vector and steering are per-category (NAIBRD_IRIG_EVTMAP_CATEGORY_BIT). Trigger type and enable are per-status type (NAIBRD_IRIG_EVTMAP_STATUS_BIT_DATA_LOSS_LATCHED). Verify both levels.

Socket bind fails on the host

Another application is already using UDP port 52802. Insufficient permissions.

Close any other application using the same port. Run with appropriate permissions. Change DEF_IRIG_RESPONSE_PORT to an unused port.

No board found / Connection timeout

Board not powered or connected. Incorrect interface or address in configuration file.

Verify physical connection. Delete the configuration file and re-run to enter settings interactively.

Invalid card or module index

Zero-based card index or one-based module index mismatch.

Cards are zero-indexed, modules are one-indexed. Verify values match your hardware setup.

Module not present at selected slot

IRIG module not installed in the selected slot.

Use the board menu to verify which slots are populated with RG1 modules.

Full Source

Full Source — RG_EtherInterrupts.c (SSK 1.x)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

/* Common Sample Program include files */
#include "include/naiapp_boardaccess_menu.h"
#include "include/naiapp_boardaccess_query.h"
#include "include/naiapp_boardaccess_access.h"
#include "include/naiapp_boardaccess_display.h"
#include "include/naiapp_boardaccess_utils.h"

/* naibrd include files */
#include "nai.h"
#include "naibrd.h"
#include "functions/naibrd_irig.h"
#include "naibrd_ether.h"
#include "advanced/nai_ether_adv.h"
#include "maps/nai_map_irig.h"

#if defined (WIN32)
#include <winsock2.h>
#include <ws2tcpip.h>
/* Need to link with Ws2_32.lib */
#pragma comment (lib, "Ws2_32.lib")
/* The following eliminates the FD_SET warning C4127 associated with the do...while(0) construct in the Microsoft winsock2.h file */
#pragma warning (disable:4127)
#elif (LINUX)
#include <sys/errno.h>
#include <pthread.h>
typedef int32_t SOCKET;
#define ZeroMemory(S, N)         memset((S), 0, (N))
#define closesocket(SD)          close(SD)
#define INVALID_SOCKET -1
#define SOCKET_ERROR   -1
#define SD_RECEIVE        0
#define SD_SEND           1
#define SD_BOTH           2
#elif (__VXWORKS__)
#include <netinet/ip.h>
#define ZeroMemory(S, N)         memset((S), 0, (N))
#define closesocket(SD)          close(SD)
#define INVALID_SOCKET -1
#define SOCKET_ERROR   -1
#define SD_RECEIVE        0
#define SD_SEND           1
#define SD_BOTH           2
#endif

static const int8_t *CONFIG_FILE = (const int8_t *)"default_RGEthInterrupts.txt";

/* Function prototypes */
static bool_t Run_IRIG_EthInterrupt(int32_t cardIndex, int32_t module, uint32_t modid);
static bool_t QueryEthInterruptIPAddr(void);
/*static void Cfg_IRIG(int32_t cardIndex, int32_t module, int32_t channel, uint32_t modid);*/
static nai_status_t Init_IRIG_IDRCommands(int32_t cardIndex, int32_t module, int32_t channel, uint8_t commands[], uint16_t *cmdcount, uint16_t *cmdlen);
static void MakeIRIGReadRegsCommand(bool_t bGen4Ether, uint16_t startIndex, uint32_t moduleOffset, int32_t channel, uint8_t commands[], uint16_t *cmdlen);
static void MakeIRIGWriteRegsCommand(bool_t bGen4Ether, uint16_t startIndex, uint32_t moduleOffset, int32_t channel, uint8_t commands[], uint16_t *cmdlen);
static void Create_IRIG_IDR_TCPServer(void);
static void Create_IRIG_IDR_UDPServer(void);
static void Decode_UPR_IRIG_IDRMessages(const uint8_t msg[], int32_t msgsize);
static bool_t Parse_IRIG_IDRResponse(int32_t startIndex, int32_t rsparraysize, uint8_t response[], uint16_t *idrrsplen, int32_t idrrsparraysize, uint8_t idr_response[]);
static void Display_Decoded_IRIG_IDRResponse(uint16_t responselen, uint8_t response[]);

#define NAI_IRIG_CHANNEL1_INTERRUPT_VECTOR             0x000000A1u

static bool_t bGen4IRIGIDRCommands = FALSE;
/* Default Ethernet Command Count specified for the IDR Commands.
   Change this to match the Command Count specified in Init_IRIG_IDRCommands() routine
*/
#define DEF_IRIG_IDR_ID                           2
#define DEF_IRIG_ETHER_CMD_COUNT                  2   /* Number of Ethernet Commands in IDR Configuration */
#define DEF_IRIG_RESPONSE_PROTOCOL                ETHER_GEN4_UDP_PROTOCOL
#define DEF_IRIG_RESPONSE_IP_LEN                  ETHER_GEN4_IPv4_ADDR_LEN

static uint16_t DEF_IRIG_RESPONSE_PORT = 52802;
static uint8_t  DEF_IRIG_RESPONSE_IP_ADDR[] = { 192,168,1,100 };

#define CLEAR_FIFO_TIMEOUT 1000  /* 1 second */

static const uint32_t NAI_IRIG_GEN5_BitStatusLatchedAddr_1Ch[] = { NAI_IRIG_GEN5_REG_COM_BIT_INTERRUPTS_LAT_ADD };
static const uint32_t NAI_IRIG_GEN5_GenStatusLatchedAddr_1Ch[] = { NAI_IRIG_GEN5_REG_COM_INTERRUPTS_LAT_ADD };

#if defined (__VXWORKS__)
int UPR_IRIGEthInt_Handler(int Param);
#elif LINUX
pthread_t interruptIRIGThread;
static void* UPR_IRIGEthInt_Handler(void* lpParam);
#else /* Default Windows */
DWORD WINAPI UPR_IRIGEthInt_Handler(LPVOID lpParam);
#endif
static int terminateIRIGThread;

/**************************************************************************************************************/
/** \defgroup IRIGEthInt IRIG Ethernet Interrupts
The purpose of the RG_EtherInterrupts is to demonstrate the methods to call in the naibrd library to
perform Ethernet interrupt operations using NAI's protocol with a given IRIG module.
*/
/**************************************************************************************************************/
#if defined (__VXWORKS__)
int32_t RG_EtherInterrupts(void)
#else
int32_t main(void)
#endif
{
   bool_t stop = FALSE;
   int32_t cardIndex;
   int32_t moduleCnt;
   int32_t module;
   uint32_t moduleID = 0;
   int8_t inputBuffer[80];
   int32_t inputResponseCnt;

   if (naiapp_RunBoardMenu(CONFIG_FILE) == TRUE)
   {
      while (stop != TRUE)
      {
         /* Query the user for the card index */
         stop = naiapp_query_CardIndex(naiapp_GetBoardCnt(), 0, &cardIndex);
         if (stop != TRUE)
         {
            check_status(naibrd_GetModuleCount(cardIndex, &moduleCnt));

            /* Query the user for the module number */
            stop = naiapp_query_ModuleNumber(moduleCnt, 1, &module);
            if (stop != TRUE)
            {
               moduleID = naibrd_GetModuleID(cardIndex, module);
               if ((moduleID != 0))
               {
                  Run_IRIG_EthInterrupt(cardIndex, module, moduleID);
               }
            }
         }

         printf("\nType Q to quit or Enter key to restart application:\n");
         stop = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
      }
   }

   printf("\nType the Enter key to exit the program: ");
   naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
   naiapp_access_CloseAllOpenCards();

   return 0;
}

/**************************************************************************************************************/
/** \ingroup IRIGEthInt
TODO Description: Queries the user for which IRIG Status they would like to configure interrupts on for the
following two IRIG module status categories: BIT Status and IRIG General Status. This routine will create a
thread to wait and receive the unprompted interrupt ethernet messages. Valid IRIG Statuses for the BIT Status
category are Data Loss and Software Fault Statuses. Valid IRIG Statuses for IRIG General Status are Reference
Loss, Receiving Reference, Reference Pulse Received, Interrupt on 1PPS Output Going High, Received Control
Bits Changed, Control Bits Received, Change in Reference Source, Event Detected, Programmable-Duration User
Interrupt, Did a DST Adjust, and Test Interrupt Statuses.
\param cardIndex (Input) Logical Card Index assigned to connection with the NAI_BOARD (0 - NAI_MAX_CARDS-1).
\param module    (Input) Module Number of the module to access (1 - [max modules for board]).
\param modid     (Input) The ID of the module.
*/
/**************************************************************************************************************/
static bool_t Run_IRIG_EthInterrupt(int32_t cardIndex, int32_t module, uint32_t modid)
{
   bool_t bQuit = FALSE;
   nai_status_t status;
   int32_t MAX_CHANNELS = naibrd_IRIG_GetChannelCount(modid);
   int32_t channel;
   naibrd_irig_event_mapped_status_type_t statusType;
   naibrd_irig_event_mapped_category_type_t categoryType;
   int32_t vector;
   uint8_t commands[MAX_ETHER_IDR_CMD_CNT*MAX_ETHER_BLOCK_REG_CNT];
   uint16_t cmdcount = 0;
   uint16_t cmdlength = 0;
   uint16_t protocol = DEF_IRIG_RESPONSE_PROTOCOL;
   uint16_t iplen = DEF_IRIG_RESPONSE_IP_LEN;
   uint16_t port;
   uint8_t ip[DEF_IRIG_RESPONSE_IP_LEN];
   int32_t i;
   int8_t inputBuffer[80];
   int32_t inputResponseCnt;

   /* Determine if the board selected supports the Generation 4 Ethernet Commands */
   bGen4IRIGIDRCommands = SupportsGen4Ether(cardIndex);

   /* Get the IRIG channel to configure for Ethernet interrupts */
   printf("IRIG Channel Selection:");
   bQuit = naiapp_query_ChannelNumber(MAX_CHANNELS, 1, &channel);
   /* TODO: Possibly query for status type and status category type */

   if (!bQuit)
      bQuit = QueryEthInterruptIPAddr();

   if (!bQuit)
   {
      if (bGen4IRIGIDRCommands)
      {
         /* Make sure there are no pending interrupts before interrupts are enabled */
         switch (channel)
         {
            case 1:
               statusType = NAIBRD_IRIG_EVTMAP_STATUS_BIT_DATA_LOSS_LATCHED;
               categoryType = NAIBRD_IRIG_EVTMAP_CATEGORY_BIT;
               vector = NAI_IRIG_CHANNEL1_INTERRUPT_VECTOR;
               break;
            default:
               statusType = NAIBRD_IRIG_EVTMAP_STATUS_BIT_DATA_LOSS_LATCHED;
               categoryType = NAIBRD_IRIG_EVTMAP_CATEGORY_BIT;
               vector = NAI_IRIG_CHANNEL1_INTERRUPT_VECTOR;
               break;
         }

         /* Clear channel status */
         check_status(naibrd_IRIG_ClearEventMappedStatus(cardIndex, module, channel,
            NAIBRD_IRIG_EVTMAP_STATUS_BIT_DATA_LOSS_LATCHED));

         /* Configure the Interrupt Driven Response Ethernet Command */
         status = check_status(naibrd_Ether_ClearIDRConfig(cardIndex, (uint16_t)DEF_IRIG_IDR_ID));

         Init_IRIG_IDRCommands(cardIndex, module, channel, commands, &cmdcount, &cmdlength);
         for (i = 0; i < DEF_IRIG_RESPONSE_IP_LEN; i++)
            ip[i] = DEF_IRIG_RESPONSE_IP_ADDR[i];
         port = DEF_IRIG_RESPONSE_PORT;
         status = check_status(naibrd_Ether_SetIDRConfig(cardIndex, (uint16_t)DEF_IRIG_IDR_ID,
            protocol, iplen, ip, port, vector, cmdcount, cmdlength, commands));
         if (status == NAI_SUCCESS)
         {
            printf("IDR ID = %d configured.\n", DEF_IRIG_IDR_ID);
         }

         /* Configure to generate interrupts */
         check_status(naibrd_IRIG_SetEventMappedInterruptTriggerType(cardIndex, module, channel,
            statusType, NAIBRD_IRIG_EDGE_INTERRUPT));
         check_status(naibrd_IRIG_SetEventMappedInterruptVector(cardIndex, module, channel,
            categoryType, (uint32_t)vector));
         check_status(naibrd_IRIG_SetEventMappedInterruptSteering(cardIndex, module, channel,
            categoryType, NAIBRD_INT_STEERING_ON_BOARD_1));
         check_status(naibrd_IRIG_SetEventMappedInterruptEnable(cardIndex, module, channel,
            statusType, TRUE));

         /* Start the IDR */
         status = check_status(naibrd_Ether_StartIDR(cardIndex, (uint16_t)DEF_IRIG_IDR_ID));
         if (status == NAI_SUCCESS)
         {
            printf("IDR ID = %d Started\n", DEF_IRIG_IDR_ID);
         }

         /* Create a thread that will receive Unprompted Reply interrupt messages */
         terminateIRIGThread = FALSE;

#if defined (__VXWORKS__)
         taskSpawn("uprIRIGHandler", 100, 0, 10000,
            (FUNCPTR)UPR_IRIGEthInt_Handler, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0);
#elif defined (LINUX)
         pthread_create(&interruptIRIGThread, NULL, (void*)UPR_IRIGEthInt_Handler, NULL);
#else /* Default Windows */
         CreateThread(NULL, 0, UPR_IRIGEthInt_Handler, NULL, 0, NULL);
#endif

         while (!bQuit)
         {
            printf("Type %c to exit program : ", NAI_QUIT_CHAR);
            bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR,
               inputBuffer, &inputResponseCnt);
            if (bQuit)
            {
               terminateIRIGThread = TRUE;

               /* Stop the IDR */
               status = check_status(naibrd_Ether_StopIDR(cardIndex, (uint16_t)DEF_IRIG_IDR_ID));
               if (status == NAI_SUCCESS)
               {
                  printf("IDR ID = %d Stopped\n", DEF_IRIG_IDR_ID);
               }
            }
         }
      }
      else
      {
         printf("IRIG Ethernet Interrupt Support Prior to Generation 4 Ethernet commands currently not supported\n");
         bQuit = TRUE;
      }
   }
   return bQuit;
}

/**************************************************************************************************************/
/** \ingroup IRIGEthInt
This function queries the user to enter the IP address associated with receiving IRIG Interrupt commands.
*/
/**************************************************************************************************************/
static bool_t QueryEthInterruptIPAddr(void)
{
   bool_t bQuit = FALSE;
   bool_t bContinue = TRUE;
   int8_t inputBuffer[80];
   int32_t inputResponseCnt;

   /* Get the IP Address */
   if (!bQuit)
   {
      bContinue = TRUE;
      while (bContinue)
      {
         /* Get the Response IP Address */
         printf("Please Enter IDR Response IP Address: [default=%d.%d.%d.%d]): ",
            DEF_IRIG_RESPONSE_IP_ADDR[0], DEF_IRIG_RESPONSE_IP_ADDR[1],
            DEF_IRIG_RESPONSE_IP_ADDR[2], DEF_IRIG_RESPONSE_IP_ADDR[3]);
         bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR,
            inputBuffer, &inputResponseCnt);
         if (bQuit)
            bContinue = FALSE;
         else
         {
            if (inputResponseCnt > NAI_MAX_IP_LEN)
               printf("ERROR: Invalid IP Address.\n");
            else
            {
               if (inputResponseCnt > 0)
                  ParseIPv4Address((char *)inputBuffer, DEF_IRIG_RESPONSE_IP_ADDR);
            }
         }
         if (!bQuit)
         {
            /* Get the Response Port */
            printf("Please Enter IDR Response Port: [default=%d]): ", DEF_IRIG_RESPONSE_PORT);
            bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR,
               inputBuffer, &inputResponseCnt);
            if (bQuit)
               bContinue = FALSE;
            else
            {
               if (inputResponseCnt > 0)
                  DEF_IRIG_RESPONSE_PORT = (uint16_t)atol((const char *)inputBuffer);
               bContinue = FALSE;
            }
         }
      }
   }
   return bQuit;
}

/**************************************************************************************************************/
/** \ingroup IRIGEthInt
This function configures the IDR (Interrupt Driven Response) commands when an IRIG interrupt occurs.
There are two Ethernet commands that will be processed by the board when an IRIG interrupt occurs. This
routine calls the MakeIRIGReadRegsCommand() and MakeIRIGWriteRegsCommand() to configure the two
Ethernet commands to include when setting the IDR.
\param cardIndex (Input) Logical Card Index assigned to connection with the NAI_BOARD (0 - NAI_MAX_CARDS-1).
\param module    (Input) Module Number of the module to access (1 - [max modules for board]).
\param channel   (Input) Channel Number of the channel to access (1 - [max channels for module]).
\param commands  (Input) Message buffer to fill with the Ethernet Read Registers elements.
\param cmdcount  (Output) Number of ethernet commands.
\param cmdlen    (Output) Message size.
*/
/**************************************************************************************************************/
static nai_status_t Init_IRIG_IDRCommands(int32_t cardIndex, int32_t module, int32_t channel,
   uint8_t commands[], uint16_t *cmdcount, uint16_t *cmdlen)
{
   nai_status_t status = NAI_SUCCESS;
   uint16_t msgIndex = 0;
   uint16_t idrcmdcnt = 0;
   uint16_t ethcmdlen = 0;
   uint16_t idrcmdlen = 0;
   uint32_t moduleOffset;

   status = check_status(naibrd_GetModuleOffset(cardIndex, module, &moduleOffset));
   if (status == NAI_SUCCESS)
   {
      if (bGen4IRIGIDRCommands)
      {
         /* First command */
         MakeIRIGReadRegsCommand(bGen4IRIGIDRCommands, msgIndex, moduleOffset,
            channel, commands, &ethcmdlen);
         msgIndex += ethcmdlen;
         idrcmdlen += ethcmdlen;
         idrcmdcnt++;

         /* Next command */
         MakeIRIGWriteRegsCommand(bGen4IRIGIDRCommands, msgIndex, moduleOffset,
            channel, commands, &ethcmdlen);
         msgIndex += ethcmdlen;
         idrcmdlen += ethcmdlen;
         idrcmdcnt++;

         *cmdcount = idrcmdcnt;
         *cmdlen = msgIndex;
      }
      else
      {
         printf("IRIG Ethernet Interrupt Support Prior to Generation 4 Ethernet "
            "commands currently not supported\n");
         status = NAI_ERROR_NOT_SUPPORTED;
      }
   }
   return status;
}

/**************************************************************************************************************/
/** \ingroup IRIGEthInt
This function formats the buffer for the Ethernet Read Registers command.
\param bGen4Ether   (Input) Boolean to represent if the module supports NAI's Gen4 ethernet protocol.
\param startIndex   (Input) Offset to index the commands[] array.
\param moduleOffset (Input) Address offset of the module.
\param channel      (Input) IRIG channel.
\param commands     (Input) Message buffer to fill with the Ethernet Read Registers elements.
\param cmdlen       (Output) Message size.
*/
/**************************************************************************************************************/
static void MakeIRIGReadRegsCommand(bool_t bGen4Ether, uint16_t startIndex,
   uint32_t moduleOffset, int32_t channel, uint8_t commands[], uint16_t *cmdlen)
{
   uint16_t msgIndex = startIndex;
   uint16_t seqno;
   uint32_t count, stride;
   uint32_t regaddr;
   uint32_t bitLatchedStatusOffset = NAI_IRIG_GEN5_REG_COM_BIT_INTERRUPTS_LAT_ADD;

#if defined (WIN32)
   UNREFERENCED_PARAMETER(channel);
#endif

   if (bGen4Ether)
   {
      seqno = 0;
      regaddr = moduleOffset + bitLatchedStatusOffset;
      count = 1;
      stride = 4;
      msgIndex = (uint16_t)nai_ether_MakeReadMessage(&commands[startIndex], seqno,
         NAI_ETHER_GEN4, NAI_INTF_ONBOARD, regaddr, stride, count, NAI_REG32);

      *cmdlen = msgIndex;
   }
}

/**************************************************************************************************************/
/** \ingroup IRIGEthInt
This function formats the buffer for the Ethernet Write Registers command.
\param bGen4Ether   (Input) Boolean to represent if the module supports NAI's Gen4 ethernet protocol.
\param startIndex   (Input) Offset to index the commands[] array.
\param moduleOffset (Input) Address offset of the module.
\param channel      (Input) IRIG channel.
\param commands     (Input) Message buffer to fill with the Ethernet Write Registers elements.
\param cmdlen       (Output) Message size.
*/
/**************************************************************************************************************/
static void MakeIRIGWriteRegsCommand(bool_t bGen4Ether, uint16_t startIndex,
   uint32_t moduleOffset, int32_t channel, uint8_t commands[], uint16_t *cmdlen)
{
   uint16_t msgIndex = startIndex;
   uint16_t seqno;
   uint32_t count, stride;
   uint32_t regaddr;
   uint32_t bitLatchedStatusOffset = NAI_IRIG_GEN5_REG_COM_BIT_INTERRUPTS_LAT_ADD;
   uint32_t data = 0xFFFFFFFFu;  /* Clear interrupt bits */

#if defined (WIN32)
   UNREFERENCED_PARAMETER(channel);
#endif

   if (bGen4Ether)
   {
      seqno = 0;
      regaddr = moduleOffset + bitLatchedStatusOffset;
      count = 1;
      stride = 4;

      msgIndex = (uint16_t)nai_ether_BeginWriteMessage(&commands[startIndex], seqno,
         NAI_ETHER_GEN4, NAI_INTF_ONBOARD, regaddr, stride, count, NAI_REG32);
      if (msgIndex >= 0)
      {
         msgIndex = (uint16_t)nai_ether_WriteMessageData(&commands[startIndex], msgIndex,
            NAI_REG32, &data, NAI_REG32, count);

         if (msgIndex >= 0)
         {
            msgIndex = (uint16_t)nai_ether_FinishMessage(&commands[startIndex], msgIndex,
               NAI_ETHER_GEN4);
         }
      }
      *cmdlen = msgIndex;
   }
}

/**************************************************************************************************************/
/** \ingroup IRIGEthInt
This thread calls Create_IRIG_IDR_TCPServer or Create_IRIG_IDR_UDPServer depending on the
DEF_IRIG_RESPONSE_PROTOCOL to receive the Interrupt Driven Response (IDR) Unprompted Ethernet Replies.
*/
/**************************************************************************************************************/
#if defined (__VXWORKS__)
int UPR_IRIGEthInt_Handler(int32_t nParam)
{
#elif LINUX
static void* UPR_IRIGEthInt_Handler(void *lpParam)
{
#else /* Default Windows */
DWORD WINAPI UPR_IRIGEthInt_Handler(LPVOID lpParam)
{
#endif

   uint16_t protocol = DEF_IRIG_RESPONSE_PROTOCOL;

#if defined (WIN32)
   UNREFERENCED_PARAMETER(lpParam);
#endif

   printf("\nUnprompted Reply Thread Started....\n");
   if (protocol == ETHER_GEN4_TCP_PROTOCOL)
      Create_IRIG_IDR_TCPServer();
   else
      Create_IRIG_IDR_UDPServer();
   printf("\nUnprompted Reply Thread Terminated\n");
   return 0;
}

/**************************************************************************************************************/
/** \ingroup IRIGEthInt
This function creates a TCP Listener socket to receive the Interrupt Driven Response (IDR) Unprompted Ethernet Replies
and waits for the Ethernet messages.
*/
/**************************************************************************************************************/
static void Create_IRIG_IDR_TCPServer(void)
{
   nai_socket_t listensock = INVALID_SOCKET, clientsock = INVALID_SOCKET;
   struct addrinfo hints;
   struct addrinfo *info;
   uint8_t portbuf[10];
   uint8_t recvbuf[NAI_ETHER_MAX_PACKET_SIZE_BYTES];
   int32_t recvbuflen = NAI_ETHER_MAX_PACKET_SIZE_BYTES;
   int32_t result;

#if defined (WIN32)
   WSADATA wsaData;
   result = WSAStartup(MAKEWORD(2, 2), &wsaData);
   if (result != 0)
   {
      printf("WSAStartup failed with error: %d\n", result);
      return;
   }
#endif

   ZeroMemory(&hints, sizeof(hints));
   hints.ai_family = AF_INET;
   hints.ai_socktype = SOCK_STREAM;
   hints.ai_protocol = IPPROTO_TCP;
   hints.ai_flags = AI_PASSIVE;

   /* Resolve the server address and port */
   sprintf((char *)portbuf, "%d", DEF_IRIG_RESPONSE_PORT);
#if defined (WIN32)
   result = getaddrinfo(NULL, (PCSTR)portbuf, &hints, &info);
#elif defined(LINUX) || defined(__VXWORKS__)
   result = getaddrinfo(NULL, (const char *)portbuf, &hints, &info);
#endif
   if (result != 0)
   {
      printf("getaddrinfo failed with error: %d\n", result);
#if defined (WIN32)
      WSACleanup();
#endif
      return;
   }

   /* Create a SOCKET for connecting to server */
   listensock = socket(info->ai_family, info->ai_socktype, info->ai_protocol);
   if (listensock == INVALID_SOCKET)
   {
#if defined (WIN32)
      printf("socket failed with error: %ld\n", WSAGetLastError());
#elif defined(LINUX) || defined(__VXWORKS__)
      printf("socket failed with error: %s\n", strerror(errno));
#endif
      freeaddrinfo(info);
#if defined (WIN32)
      WSACleanup();
#endif
      return;
   }

   /* Setup the TCP listening socket */
   result = bind(listensock, info->ai_addr, (int)info->ai_addrlen);
   if (result == SOCKET_ERROR)
   {
#if defined (WIN32)
      printf("bind failed with error: %d\n", WSAGetLastError());
#elif defined(LINUX) || defined(__VXWORKS__)
      printf("bind failed with error: %s\n", strerror(errno));
#endif
      freeaddrinfo(info);
      closesocket(listensock);
#if defined (WIN32)
      WSACleanup();
#endif
      return;
   }

   freeaddrinfo(info);

   result = listen(listensock, SOMAXCONN);
   if (result == SOCKET_ERROR)
   {
#if defined (WIN32)
      printf("listen failed with error: %d\n", WSAGetLastError());
#elif defined(LINUX) || defined(__VXWORKS__)
      printf("listen failed with error: %s\n", strerror(errno));
#endif
      closesocket(listensock);
#if defined (WIN32)
      WSACleanup();
#endif
      return;
   }

   /* Accept a client socket */
   clientsock = accept(listensock, NULL, NULL);
   if (clientsock == INVALID_SOCKET)
   {
#if defined (WIN32)
      printf("accept failed with error: %d\n", WSAGetLastError());
#elif defined(LINUX) || defined(__VXWORKS__)
      printf("accept failed with error: %s\n", strerror(errno));
#endif
      closesocket(listensock);
#if defined (WIN32)
      WSACleanup();
#endif
      return;
   }

   /* No longer need server socket */
   closesocket(listensock);

   printf("Ready to receive a TCP messages...\n");

   /* Receive until the peer shuts down the connection */
   do
   {
      result = recv(clientsock, (char *)recvbuf, recvbuflen, 0);
      if (result > 0)
      {
         printf("\n\nTCP Server: Bytes received: %d\n", result);
         Decode_UPR_IRIG_IDRMessages(recvbuf, result);
      }
      else if (result == 0)
         printf("Connection closing...\n");
      else
      {
#if defined (WIN32)
         printf("recv failed with error: %d\n", WSAGetLastError());
#elif defined(LINUX) || defined(__VXWORKS__)
         printf("recv failed with error: %s\n", strerror(errno));
#endif
         closesocket(clientsock);
#if defined (WIN32)
         WSACleanup();
#endif
         return;
      }
   } while ((result > 0) && (terminateIRIGThread == FALSE));

   /* shutdown the connection when done */
   result = shutdown(clientsock, SD_SEND);
   if (result == SOCKET_ERROR)
   {
#if defined (WIN32)
      printf("shutdown failed with error: %d\n", WSAGetLastError());
#elif defined(LINUX) || defined(__VXWORKS__)
      printf("shutdown failed with error: %s\n", strerror(errno));
#endif
      closesocket(clientsock);
#if defined (WIN32)
      WSACleanup();
#endif
      return;
   }

   /*cleanup */
   closesocket(clientsock);
#if defined (WIN32)
   WSACleanup();
#endif
}

/**************************************************************************************************************/
/** \ingroup IRIGEthInt
This function creates a UDP Listener socket to receive the Interrupt Driven Response (IDR) Unprompted Ethernet
Replies and waits for the Ethernet messages.
*/
/**************************************************************************************************************/
static void Create_IRIG_IDR_UDPServer()
{
   nai_socket_t recvsock;
   uint8_t recvbuf[NAI_ETHER_MAX_PACKET_SIZE_BYTES];
   int32_t recvbuflen = NAI_ETHER_MAX_PACKET_SIZE_BYTES;
#if defined (WIN32)
   SOCKADDR_IN recvaddr;
   SOCKADDR_IN sendaddr;
#elif defined(LINUX) || defined(__VXWORKS__)
   struct sockaddr_in recvaddr;
   struct sockaddr_in sendaddr;
#endif
   int32_t sendaddrsize = sizeof(sendaddr);
   int32_t result;

#if defined (WIN32)
   WSADATA wsaData;
   result = WSAStartup(MAKEWORD(2, 2), &wsaData);
   if (result != 0)
   {
      printf("WSAStartup failed with error: %d\n", result);
      return;
   }
#endif

   /* Create a new socket to receive datagrams on. */
   recvsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
   if (recvsock == INVALID_SOCKET)
   {
#if defined (WIN32)
      printf("socket failed with error: %ld\n", WSAGetLastError());
#elif defined(LINUX) || defined(__VXWORKS__)
      printf("socket failed with error: %s\n", strerror(errno));
#endif
#if defined (WIN32)
      WSACleanup();
#endif
      return;
   }

   recvaddr.sin_family = AF_INET;
   recvaddr.sin_port = htons(DEF_IRIG_RESPONSE_PORT);
   recvaddr.sin_addr.s_addr = htonl(INADDR_ANY);

#if defined (WIN32)
   result = bind(recvsock, (SOCKADDR *)&recvaddr, sizeof(recvaddr));
#elif defined(LINUX) || defined(__VXWORKS__)
   result = bind(recvsock, (struct sockaddr *)&recvaddr, sizeof(recvaddr));
#endif
   if (result == SOCKET_ERROR)
   {
#if defined (WIN32)
      printf("bind failed with error: %d\n", WSAGetLastError());
#elif defined(LINUX) || defined(__VXWORKS__)
      printf("bind failed with error: %s\n", strerror(errno));
#endif
      closesocket(recvsock);
#if defined (WIN32)
      WSACleanup();
#endif
      return;
   }

   printf("Ready to receive UDP messages...\n");

   while (terminateIRIGThread == FALSE)
   {
#if defined (WIN32)
      result = recvfrom(recvsock, (char *)recvbuf, recvbuflen,
         0, (SOCKADDR *)&sendaddr, &sendaddrsize);
#elif defined(LINUX) || defined(__VXWORKS__)
      result = recvfrom(recvsock, (char *)recvbuf, recvbuflen,
         0, (struct sockaddr *)&sendaddr, (socklen_t *)&sendaddrsize);
#endif
      if (result > 0)
      {
         printf("\n\nUDP Server: Bytes received: %d\n", result);
         Decode_UPR_IRIG_IDRMessages(recvbuf, result);
      }
      else if (result <= 0)
#if defined (WIN32)
         printf("Connection closed with error code: %ld\n", WSAGetLastError());
#elif defined(LINUX) || defined(__VXWORKS__)
         printf("Connection closed with error code: %s\n", strerror(errno));
#endif
      else
#if defined (WIN32)
         printf("recvfrom() failed with error code: %d\n", WSAGetLastError());
#elif defined(LINUX) || defined(__VXWORKS__)
         printf("recvfrom() failed with error code: %s\n", strerror(errno));
#endif
   }

   printf("Server: Finished receiving. Closing the listening socket...\n");
   closesocket(recvsock);
#if defined (WIN32)
   WSACleanup();
#endif
   return;
}

/**************************************************************************************************************/
/** \ingroup IRIGEthInt
This function calls Parse_IRIG_IDRResponse() to break down the IDR message received to individual Ethernet
messages and calls Display_Decoded_IRIG_IDRResponse() to display the data in the Ethernet message.
\param msg     (Input) Input message buffer.
\param msgsize (Input) Size of input message buffer.
*/
/**************************************************************************************************************/
static void Decode_UPR_IRIG_IDRMessages(const uint8_t msg[], int32_t msgsize)
{
   uint32_t arraysize = MAX_ETHER_IDR_CMD_CNT * MAX_ETHER_BLOCK_REG_CNT;
   uint16_t responselen;
   uint8_t response[MAX_ETHER_BLOCK_REG_CNT];
   int32_t startIndex;

   startIndex = 0;
   while (startIndex < msgsize)
   {
      if (Parse_IRIG_IDRResponse(startIndex, arraysize, (uint8_t *)msg,
         &responselen, MAX_ETHER_BLOCK_REG_CNT, response))
      {
         startIndex += responselen;
         Display_Decoded_IRIG_IDRResponse(responselen, response);
      }
   }
}

/**************************************************************************************************************/
/** \ingroup IRIGEthInt
This function break downs the IDR message received to individual Ethernet messages.
\param startIndex      (Input) Offset to index the commands[] array.
\param rsparraysize    (Input) Max array size.
\param response        (Input) Input message array.
\param idrrsplen       (Output) Length of idr_response array.
\param idrrsparraysize (Input)  Max Array size.
\param idr_response    (Output) Output ethernet messages array.
\returns True if function succeeded in parsing the data.
*/
/**************************************************************************************************************/
static bool_t Parse_IRIG_IDRResponse(int32_t startIndex, int32_t rsparraysize,
   uint8_t response[], uint16_t *idrrsplen, int32_t idrrsparraysize, uint8_t idr_response[])
{
   bool_t bParsed = FALSE;
   bool_t bContinueCopy = TRUE;
   uint16_t preamble, postamble;
   int32_t rspIndex = startIndex;
   int32_t idrIndex = 0;

   preamble = (response[startIndex] << 8) | response[startIndex + 1];
   if (bGen4IRIGIDRCommands)
   {
      if (preamble == ETHER_GEN4_PREAMBLE)
      {
         while ((bContinueCopy) && (idrIndex < idrrsparraysize) && (rspIndex < rsparraysize - 1))
         {
            postamble = (response[rspIndex] << 8) | response[rspIndex + 1];
            if (postamble == ETHER_GEN4_POSTAMBLE)
            {
               idr_response[idrIndex++] = response[rspIndex++];
               idr_response[idrIndex++] = response[rspIndex++];
               bContinueCopy = FALSE;
            }
            else
            {
               idr_response[idrIndex++] = response[rspIndex++];
            }
         }
         bParsed = TRUE;
         *idrrsplen = (uint16_t)idrIndex;
      }
   }
   else
   {
      printf("IRIG Ethernet Interrupt Support Prior to Generation 4 Ethernet "
         "commands currently not supported\n");
   }
   return bParsed;
}

/**************************************************************************************************************/
/** \ingroup IRIGEthInt
This function displays the data in the Ethernet message.
\param responselen (Input) Length of array of ethernet messages.
\param response    (Input) Array holding ethernet messages.
*/
/**************************************************************************************************************/
static void Display_Decoded_IRIG_IDRResponse(uint16_t responselen, uint8_t response[])
{
   uint16_t seq;
   nai_ether_typecode_t tc;
   nai_ether_gen_t gen;
   int32_t size;
   int32_t offset;
   uint16_t seqrsptype;
   uint16_t seqrspidindex;
   uint16_t seqcmdindex;
   uint16_t datacnt;
   uint32_t data;
   int32_t i;

   if (bGen4IRIGIDRCommands)
      gen = NAI_ETHER_GEN4;
   else
      gen = NAI_ETHER_GEN3;

   offset = nai_ether_DecodeMessageHeader(response, responselen, &seq, &tc, gen, &size);

   printf("Size=%d   ", size);

   /* Parse IDR Response Sequence Value */
   seqrsptype = seq & ETHER_GEN4_SEQ_UPR_MASK;
   seqrspidindex = (seq & (ETHER_GEN4_SEQ_ID_MASK)) >> ETHER_GEN4_SEQ_ID_SHIFT;
   seqcmdindex = (seq & (ETHER_GEN4_SEQ_CMD_MASK)) >> ETHER_GEN4_SEQ_CMD_SHIFT;
   printf("seq:0x%04X (", seq);
   if (seqrsptype == ETHER_GEN4_SEQ_UPR_TDR)
      printf("Type=Unprompted TDR ");
   else if (seqrsptype == ETHER_GEN4_SEQ_UPR_IDR)
      printf("Type=Unprompted IDR ");
   else
      printf("Type=UNKNOWN(0x%X) ", seqrsptype);
   printf("ID=%d Cmd=%d)   ", seqrspidindex + 1, seqcmdindex + 1);

   switch (tc)
   {
      case NAI_ETHER_TYPECODE_RSP_COMMAND_COMPLETE_READ_4:
         printf("Typecode: Read Regs (0x%4X)\n", tc);
         datacnt = (responselen - 10) / NAI_REG32;
         printf("   Data (Count=%d): ", datacnt);
         for (i = 0; i < datacnt; i++)
         {
            if (i != 0)
               printf(",");
            data = 0;
            data = response[offset++] << 24;
            data |= response[offset++] << 16;
            data |= response[offset++] << 8;
            data |= response[offset++];
            printf("0x%08X", data);
         }
         printf("\n");
         break;
      case NAI_ETHER_TYPECODE_RSP_COMMAND_COMPLETE_WRITE_4:
         printf("Typecode: Write Regs (0x%4X)\n", tc);
         break;
      case NAI_ETHER_TYPECODE_RSP_COMMAND_COMPLETE_READ_BLOCK_4:
         printf("Typecode: Read Block (0x%4X)\n", tc);
         datacnt = (responselen - 10) / NAI_REG32;
         printf("   Data (Count=%d): ", datacnt);
         for (i = 0; i < datacnt; i++)
         {
            if (i != 0)
               printf(",");
            data = 0;
            data = response[offset++] << 24;
            data |= response[offset++] << 16;
            data |= response[offset++] << 8;
            data |= response[offset++];
            printf("0x%08X", data);
         }
         printf("\n");
         break;
      case NAI_ETHER_TYPECODE_RSP_COMMAND_COMPLETE_WRITE_BLOCK_4:
         printf("Typecode: Write Block (0x%4X)\n", tc);
         break;
      case NAI_ETHER_TYPECODE_RSP_ERROR_BLOCK_NOT_CONFIG_4:
         printf("Typecode: Block Error (Not Configured) (0x%4X)\n", tc);
         break;
      default:
         printf("Typecode: Unknown (0x%4X)\n", tc);
         break;
   }
}

Help Bot

X