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

SER EtherInterrupts Rx

SER EtherInterrupts Rx Sample Application (SSK 1.x)

Overview

The SER EtherInterrupts Rx sample application demonstrates how to receive serial receive-side interrupt notifications over an Ethernet network using the NAI Software Support Kit (SSK 1.x) Interrupt Driven Response (IDR) mechanism. When a serial channel’s receive buffer has data available or is almost full, the board generates an interrupt and sends a UDP packet to a remote host containing the channel’s latched status register value along with an embedded write command that clears the status to re-arm the interrupt. Your application runs a socket server that listens for these packets, decodes the serial channel status, and displays the interrupt information.

This sample is designed to run in conjunction with the SER EtherInterrupts Tx sample to demonstrate a complete serial loopback interrupt workflow over Ethernet. When running both applications, start the Rx application first and ensure it is configured and listening before launching the Tx application. For the full ISR-based serial interrupt sample that supports local interrupt delivery, see the SER Interrupts guide. For basic asynchronous serial receive operations without interrupt delivery, see the SER ASync Rx guide.

This sample supports the following serial module types: P8, PC, PD, Px, KB, SC, and the combination module CMH. The channel count is determined at runtime via naibrd_SER_GetChannelCount().

The sample monitors two receive-side interrupt status flags:

  • Rx Buffer Almost Full (NAI_SER_GEN5_INT_RXBUF_ALMOST_FULL) — the receive FIFO has reached the almost-full threshold.

  • Rx Data Available (NAI_SER_GEN5_INT_RXAVAILABLE) — new receive data is available in the FIFO.

Why Ethernet IDR for Serial Rx?

Serial modules in embedded systems often need remote monitoring of receive buffer status for data acquisition and communication systems. Ethernet IDR enables this without a physical bus connection:

  • Remote receive monitoring — your application can detect incoming serial data from any host reachable over the network, enabling remote data acquisition systems to react to serial traffic without polling.

  • Self-contained notification — each IDR packet contains both a read of the channel latched status register (to capture which flags fired) and a write to clear those flags (to re-arm the interrupt), all 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 socket.

  • Self-contained single-file design — unlike the multi-file DS Ethernet interrupt sample, this application implements its own IDR command construction, socket server, and message parsing entirely within a single source file.

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 serial communication 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 a serial module installed (P8, PC, PD, Px, KB, SC, or CMH).

  • 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 SER_EtherInterrupts_Rx executable from your build output directory. On startup the application looks for a configuration file (default_SerEthInterruptsRx.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, configures the serial channel in loopback mode, 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 serial. 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_SerEthInterruptsRx.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 a serial module is installed at the selected slot.

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

#if defined (__VXWORKS__)
int32_t SER_EtherInterrupts_Rx(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_SER_EthInterrupt_Rx(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;
}
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 a serial module. Use the board menu to verify which slots are populated.

Program Structure

The SER EtherInterrupts Rx sample is implemented entirely in a single source file: SER_EtherInterrupts_Rx.c. This self-contained design includes the board connection logic, serial channel configuration, IDR command construction, socket server (both TCP and UDP), and 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_SER_CHANNEL1_INTERRUPT_VECTOR             0x000000A1u
#define NAI_SER_CHANNEL2_INTERRUPT_VECTOR             0x000000A2u
#define NAI_SER_CHANNEL3_INTERRUPT_VECTOR             0x000000A3u
#define NAI_SER_CHANNEL4_INTERRUPT_VECTOR             0x000000A4u

#define DEF_SER_RX_IDR_ID                           2
#define DEF_SER_RX_ETHER_CMD_COUNT                  2
#define DEF_SER_RX_RESPONSE_PROTOCOL                ETHER_GEN4_UDP_PROTOCOL
#define DEF_SER_RX_RESPONSE_IP_LEN                  ETHER_GEN4_IPv4_ADDR_LEN

static uint16_t DEF_SER_RX_RESPONSE_PORT = 52802;
static uint8_t  DEF_SER_RX_RESPONSE_IP_ADDR[] = { 192,168,1,100 };
  • NAI_SER_CHANNELx_INTERRUPT_VECTOR — per-channel interrupt vectors that link the module interrupt to the IDR engine. The vector assigned depends on the selected channel.

  • DEF_SER_RX_IDR_ID — the IDR ID used to register this interrupt handler. Set to 2 so it does not conflict with the Tx sample’s IDR ID of 1 when both applications run simultaneously.

  • DEF_SER_RX_RESPONSE_PROTOCOL — defaults to UDP. The sample also supports TCP if changed.

  • DEF_SER_RX_RESPONSE_PORT and DEF_SER_RX_RESPONSE_IP_ADDR — the destination where the board sends IDR packets. The user can override these interactively at runtime.

Per-Channel Status Type and Vector Mapping

Serial modules use per-channel latched status types. The sample maps the selected channel to the correct status type and interrupt vector:

switch (channel)
{
   case 1:
      type = NAI_SER_STATUS_CHANNEL1_LATCHED;
      vector = NAI_SER_CHANNEL1_INTERRUPT_VECTOR;
      break;
   case 2:
      type = NAI_SER_STATUS_CHANNEL2_LATCHED;
      vector = NAI_SER_CHANNEL2_INTERRUPT_VECTOR;
      break;
   case 3:
      type = NAI_SER_STATUS_CHANNEL3_LATCHED;
      vector = NAI_SER_CHANNEL3_INTERRUPT_VECTOR;
      break;
   case 4:
      type = NAI_SER_STATUS_CHANNEL4_LATCHED;
      vector = NAI_SER_CHANNEL4_INTERRUPT_VECTOR;
      break;
   default:
      type = NAI_SER_STATUS_CHANNEL1_LATCHED;
      vector = NAI_SER_CHANNEL1_INTERRUPT_VECTOR;
      break;
}

Each channel has its own latched status register and interrupt vector. In your own application, use this same mapping to configure interrupts for the correct channel.

Gen4 Ethernet Requirement Check

Run_SER_EthInterrupt_Rx() begins by verifying Gen4 Ethernet support:

bGen4SerialIDRCommands = 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

  • "Serial 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.

Serial Channel Configuration

Before enabling interrupts, the sample configures the serial channel for asynchronous operation in loopback mode. This allows the channel to generate receive interrupts without requiring external cabling — data transmitted on the channel loops back to the receive FIFO.

static void Cfg_Rx_Serial(int32_t cardIndex, int32_t module, int32_t channel,
                           uint32_t modid)
{
   check_status(naibrd_SER_ChannelReset(cardIndex, module, channel));
   check_status(naibrd_SER_ClearRxFifo(cardIndex, module, channel));

   /* Wait for FIFO clear to complete */
   nCntlValueLo = NAI_SER_CTRLLO_CLEAR_RX_FIFO;
   for (i = 0; i < CLEAR_FIFO_TIMEOUT && (nCntlValueLo & NAI_SER_CTRLLO_CLEAR_RX_FIFO); i++)
   {
      check_status(naibrd_SER_GetChannelControlRaw(cardIndex, module, channel, &chanCtrlRaw));
      nCntlValueLo = chanCtrlRaw & 0x0000FFFF;
      nai_msDelay(1);
   }

   check_status(naibrd_SER_SetProtocol(cardIndex, module, channel, NAI_SER_PROTOCOL_ASYNC));
   check_status(naibrd_SER_SetInterfaceLevel(cardIndex, module, channel, NAI_SER_INTF_LOOPBACK));
   check_status(naibrd_SER_SetClockMode(cardIndex, module, channel, TXINT_RXINT));
   check_status(naibrd_SER_SetParity(cardIndex, module, channel, NAI_SER_PARITY_NONE));
   check_status(naibrd_SER_SetDataBits(cardIndex, module, channel, 8));
   check_status(naibrd_SER_SetStopBits(cardIndex, module, channel, 1));
   check_status(naibrd_SER_SetBaudrate(cardIndex, module, channel, 9600));

   if (NAI_MODULE_ID_P8 == modid || NAI_MODULE_ID_PC == modid ||
       NAI_MODULE_ID_PD == modid || NAI_MODULE_ID_Px == modid ||
       NAI_MODULE_ID_KB == modid)
   {
      nai_msDelay(20);  /* Allow 20ms for HW to acknowledge (GEN 2/3 only) */
   }

   check_status(naibrd_SER_SetReceiverEnable(cardIndex, module, channel, 1));
}

The configuration steps are:

  1. Reset and clear — naibrd_SER_ChannelReset() resets the channel, and naibrd_SER_ClearRxFifo() flushes any stale data. The sample polls the control register to confirm the FIFO clear completes before proceeding.

  2. Protocol — NAI_SER_PROTOCOL_ASYNC for asynchronous serial communication.

  3. Interface level — NAI_SER_INTF_LOOPBACK routes transmitted data back to the receive FIFO without external wiring. In a production application, set this to the physical interface level your hardware uses (RS-232, RS-422, RS-485, etc.).

  4. Clock mode — TXINT_RXINT uses the internal clock for both transmit and receive.

  5. Frame format — 8 data bits, no parity, 1 stop bit (8N1) at 9600 baud.

  6. GEN 2/3 delay — older module generations (P8, PC, PD, Px, KB) require a 20ms delay for the hardware to process the configuration. Newer modules (SC, CMH) do not need this delay.

  7. Enable receiver — naibrd_SER_SetReceiverEnable() activates the receive path so the FIFO begins accepting data.

Important

Common Errors

  • Failed to clear FIFO — the FIFO clear operation timed out. The channel may be in an unexpected state. Try resetting the channel again or power-cycling the board.

  • NAI_ERROR_NOT_SUPPORTED — the selected protocol or interface level is not supported by this module type. Consult your module’s manual for supported configurations.

IDR Configuration

After channel configuration, the sample clears any pending interrupt status and builds the IDR commands:

/* Clear pending Rx interrupt status */
check_status(naibrd_SER_ClearChannelStatus(cardIndex, module, type,
   NAI_SER_GEN5_INT_RXBUF_ALMOST_FULL | NAI_SER_GEN5_INT_RXAVAILABLE));

/* Clear any existing IDR configuration */
status = check_status(naibrd_Ether_ClearIDRConfig(cardIndex, (uint16_t)DEF_SER_RX_IDR_ID));

/* Build IDR commands */
InitSERRxIDRCommands(cardIndex, module, channel, commands, &cmdcount, &cmdlength);

/* Configure the IDR */
status = check_status(naibrd_Ether_SetIDRConfig(cardIndex, (uint16_t)DEF_SER_RX_IDR_ID,
   protocol, iplen, ip, port, vector, cmdcount, cmdlength, commands));

Building IDR Commands

InitSERRxIDRCommands() constructs two embedded Ethernet commands that the board executes when the serial Rx interrupt fires:

Command 1 — Read channel latched status:

static void MakeSerialRxReadRegsCommand(bool_t bGen4Ether, uint16_t startIndex,
   uint32_t moduleOffset, int32_t channel, uint8_t commands[], uint16_t *cmdlen)
{
   seqno = 0;
   regaddr = moduleOffset + NAI_SER_GEN5_ChanStatusLatchedAddr_4Ch[channel - 1];
   count = 1;
   stride = 4;
   msgIndex = (uint16_t)nai_ether_MakeReadMessage(&commands[startIndex], seqno,
      NAI_ETHER_GEN4, NAI_INTF_ONBOARD, regaddr, stride, count, NAI_REG32);
}

This reads the channel’s latched status register so the IDR response packet contains the interrupt status bits that fired.

Command 2 — Write to clear latched status:

static void MakeSerialRxWriteRegsCommand(bool_t bGen4Ether, uint16_t startIndex,
   uint32_t moduleOffset, int32_t channel, uint8_t commands[], uint16_t *cmdlen)
{
   uint32_t data = NAI_SER_GEN5_INT_RXBUF_ALMOST_FULL | NAI_SER_GEN5_INT_RXAVAILABLE;
   seqno = 0;
   regaddr = moduleOffset + NAI_SER_GEN5_ChanStatusLatchedAddr_4Ch[channel - 1];
   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);
}

This writes the combined Rx status bitmask (NAI_SER_GEN5_INT_RXBUF_ALMOST_FULL | NAI_SER_GEN5_INT_RXAVAILABLE) to the latched status register, clearing both flags and re-arming the interrupt. The write executes on the board immediately after the read, so the interrupt is cleared atomically as part of the IDR response.

Configuring Module Interrupts

After the IDR is configured, the sample sets up the serial module’s interrupt registers:

check_status(naibrd_SER_SetInterruptEdgeLevel(cardIndex, module, type, 0));  /* Edge triggered */
check_status(naibrd_SER_SetInterruptVector(cardIndex, module, type, vector));
check_status(naibrd_SER_SetInterruptSteering(cardIndex, module, type,
   NAIBRD_INT_STEERING_ON_BOARD_1));
check_status(naibrd_SER_SetInterruptEnable(cardIndex, module, type,
   NAI_SER_GEN5_INT_RXBUF_ALMOST_FULL | NAI_SER_GEN5_INT_RXAVAILABLE));
  • Edge/level trigger — set to 0 for edge-triggered interrupts. The interrupt fires once when the status condition transitions from inactive to active.

  • Interrupt vector — must match the vector passed to naibrd_Ether_SetIDRConfig() so the IDR engine knows which IDR to invoke.

  • Interrupt steering — NAIBRD_INT_STEERING_ON_BOARD_1 routes the interrupt to the board’s onboard Ethernet IDR engine.

  • Interrupt enable — enables both NAI_SER_GEN5_INT_RXBUF_ALMOST_FULL and NAI_SER_GEN5_INT_RXAVAILABLE status flags.

Starting the IDR

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

Once started, the IDR is armed and will fire whenever the configured interrupt occurs.

Important

Common Errors

  • NAI_ERROR_NOT_SUPPORTED — the board does not support IDR. Verify Gen4 compatibility.

  • IDR ID conflict — if another application or IDR session is already using IDR ID 2, the configuration will fail. Ensure the Tx sample uses IDR ID 1 and the Rx sample uses IDR ID 2.

  • Network unreachable — the board cannot reach the specified response IP address. Verify network connectivity and that the IP/port are correct.

IDR Server and Response Handling

After the IDR is started, the sample spawns a listener thread and waits for the user to exit:

terminateSerialRxThread = FALSE;

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

The handler thread (UPR_SerialRxEthInt_Handler) selects the appropriate server based on the configured protocol:

  • UDP (ETHER_GEN4_UDP_PROTOCOL) — CreateSerialRxIDRUDPServer() creates a UDP socket, binds to DEF_SER_RX_RESPONSE_PORT, and calls recvfrom() in a loop.

  • TCP (ETHER_GEN4_TCP_PROTOCOL) — CreateSerialRxIDRTCPServer() creates a TCP listener socket, accepts a connection, and calls recv() in a loop.

Both servers pass received data to DecodeUPRSerialRxIDRMessages() for parsing.

Decoding IDR Responses

DecodeUPRSerialRxIDRMessages() iterates through the received buffer, extracting individual Gen4 Ethernet messages delimited by preamble/postamble markers:

static void DecodeUPRSerialRxIDRMessages(const uint8_t msg[], int32_t msgsize)
{
   startIndex = 0;
   while (startIndex < msgsize)
   {
      if (ParseSerialRxIDRResponse(startIndex, arraysize, (uint8_t *)msg,
                                    &responselen, MAX_ETHER_BLOCK_REG_CNT, response))
      {
         startIndex += responselen;
         DisplayDecodedSerialRxIDRResponse(responselen, response);
      }
   }
}

ParseSerialRxIDRResponse() scans for the Gen4 preamble (ETHER_GEN4_PREAMBLE) and copies bytes until it finds the postamble (ETHER_GEN4_POSTAMBLE), isolating each embedded Ethernet message.

Displaying Decoded Status

DisplayDecodedSerialRxIDRResponse() decodes each embedded Ethernet 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;

switch (tc)
{
   case NAI_ETHER_TYPECODE_RSP_COMMAND_COMPLETE_READ_4:
      /* Display read data -- this is the channel latched status */
      datacnt = (responselen - 10) / NAI_REG32;
      for (i = 0; i < datacnt; i++)
      {
         data = response[offset++] << 24;
         data |= response[offset++] << 16;
         data |= response[offset++] << 8;
         data |= response[offset++];
         printf("0x%08X", data);
      }
      break;
   case NAI_ETHER_TYPECODE_RSP_COMMAND_COMPLETE_WRITE_4:
      /* Write complete -- status has been cleared */
      break;
}

The sequence field is parsed to extract:

  • Response type (bit 15-14) — distinguishes unprompted TDR vs. IDR responses.

  • IDR ID index (bits 13-10) — identifies which IDR generated the response.

  • Command index (bits 9-6) — identifies which embedded command within the IDR this response corresponds to.

For the read response (Command 1), the 32-bit data value is the channel latched status register. Bits corresponding to NAI_SER_GEN5_INT_RXBUF_ALMOST_FULL and NAI_SER_GEN5_INT_RXAVAILABLE indicate which Rx event triggered the interrupt. For the write response (Command 2), a successful write-complete typecode confirms the status was cleared.

Important

Common Errors

  • No IDR packets received — verify that the board can reach the response IP address, the IDR is started, and the module interrupts are enabled. Check firewall rules on the receiving host. If using this with the Tx sample, make sure the Tx application is actually sending data.

  • Socket bind error — another application (or a previous instance) may already be bound to port 52802. Close the other instance or change the port.

  • Unexpected typecode — if the response typecode is not a read-complete or write-complete, the embedded command may have been constructed incorrectly. Verify the IDR command configuration.

  • "Block Error (Not Configured)" — the module offset or register address in the IDR command is incorrect. Verify the module is present at the expected slot.

Cleanup

When the user types 'Q' to exit, the main thread signals the listener thread to terminate and stops the IDR:

terminateSerialRxThread = TRUE;

status = check_status(naibrd_Ether_StopIDR(cardIndex, (uint16_t)DEF_SER_RX_IDR_ID));

Always stop the IDR session when your application exits. Leaving IDR sessions active can prevent other applications from using that IDR ID and may generate unwanted network traffic.

Troubleshooting Reference

This section summarizes common issues. Consult your module’s manual for hardware-specific diagnostics.

Error / Symptom Possible Causes Suggested Resolution

"Serial 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.

No IDR packets received

Board cannot reach response IP; IDR not started; interrupts not enabled; firewall blocking; no data being transmitted to generate Rx interrupt.

Verify network connectivity, confirm IDR is started, check interrupt enable state, review firewall rules. Run the SER_EtherInterrupts_Tx sample to generate test data.

Failed to clear FIFO

Channel in unexpected state; hardware fault.

Reset the channel again. Power-cycle the board if the problem persists.

Socket bind error (port in use)

Previous instance still running or another application using port 52802.

Close the other application or change DEF_SER_RX_RESPONSE_PORT.

Status reads all zeros in IDR response

Interrupt condition cleared before IDR read; transient event.

Verify test data is being transmitted continuously. Check edge vs. level trigger.

No board found / Connection timeout

Board not powered; incorrect interface or address; network misconfiguration.

Verify physical connection, check configuration file, confirm network settings.

IDR configuration fails

IDR ID already in use; invalid network parameters.

Ensure IDR IDs are unique (Rx uses ID 2, Tx uses ID 1). Verify IP address and port values.

20ms delay hangs

Older module (P8, PC, PD, Px, KB) configuration acknowledgment issue.

This delay is normal for GEN 2/3 modules. If it exceeds 1 second, the module may be unresponsive.

Full Source

Full Source — SER_EtherInterrupts_Rx.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_ser.h"
#include "naibrd_ether.h"
#include "advanced/nai_ether_adv.h"
#include "maps/nai_map_ser.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_SerEthInterruptsRx.txt";

/* Function prototypes */
static bool_t Run_SER_EthInterrupt_Rx(int32_t cardIndex, int32_t module, uint32_t modid);
static bool_t QueryEthInterruptRxIPAddr(void);
static void Cfg_Rx_Serial(int32_t cardIndex, int32_t module, int32_t channel, uint32_t modid);
static nai_status_t InitSERRxIDRCommands(int32_t cardIndex, int32_t module, int32_t channel, uint8_t commands[], uint16_t *cmdcount, uint16_t *cmdlen);
static void MakeSerialRxReadRegsCommand(bool_t bGen4Ether, uint16_t startIndex, uint32_t moduleOffset, int32_t channel, uint8_t commands[], uint16_t *cmdlen);
static void MakeSerialRxWriteRegsCommand(bool_t bGen4Ether, uint16_t startIndex, uint32_t moduleOffset, int32_t channel, uint8_t commands[], uint16_t *cmdlen);
static void CreateSerialRxIDRTCPServer(void);
static void CreateSerialRxIDRUDPServer(void);
static void DecodeUPRSerialRxIDRMessages(const uint8_t msg[], int32_t msgsize);
static bool_t ParseSerialRxIDRResponse(int32_t startIndex, int32_t rsparraysize, uint8_t response[], uint16_t *idrrsplen, int32_t idrrsparraysize, uint8_t idr_response[]);
static void DisplayDecodedSerialRxIDRResponse(uint16_t responselen, uint8_t response[]);

#define NAI_SER_CHANNEL1_INTERRUPT_VECTOR             0x000000A1u
#define NAI_SER_CHANNEL2_INTERRUPT_VECTOR             0x000000A2u
#define NAI_SER_CHANNEL3_INTERRUPT_VECTOR             0x000000A3u
#define NAI_SER_CHANNEL4_INTERRUPT_VECTOR             0x000000A4u

static bool_t bGen4SerialIDRCommands = FALSE;
/* Default Ethernet Command Count specified for the IDR Commands.
   Change this to match the Command Count specified in InitSERRxIDRCommands() routine
*/
#define DEF_SER_RX_IDR_ID                           2
#define DEF_SER_RX_ETHER_CMD_COUNT                  2   /* Number of Ethernet Commands in IDR Configuration */
#define DEF_SER_RX_RESPONSE_PROTOCOL                ETHER_GEN4_UDP_PROTOCOL
#define DEF_SER_RX_RESPONSE_IP_LEN                  ETHER_GEN4_IPv4_ADDR_LEN

static uint16_t DEF_SER_RX_RESPONSE_PORT = 52802;
static uint8_t  DEF_SER_RX_RESPONSE_IP_ADDR[] = { 192,168,1,100 };

#define CLEAR_FIFO_TIMEOUT 1000  /* 1 second */

static const uint32_t NAI_SER_GEN5_ChanStatusLatchedAddr_4Ch[] = NAI_SER_GEN5_CHAN_STATUS_LATCHED_ADD;

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

/**************************************************************************************************************/
/** \defgroup SEREthIntRx Serial Ethernets Interrupts - Receive
The purpose of the SER_EtherInterrupts_Rx is to demonstrate the methods to call in the naibrd library to
perform Ethernet interrupt operations using NAI's protocol with a given serial module.

\note The SER_Ether_Interrupts_Rx can run in conjunction with the SER_Ether_Interrupts_Tx applications to
illustrate Tx and Rx operations Serial Loopback together with the Serial module. When running both applications,
be sure to run the SER_Ether_Interrupts_Rx is running and configured before running the SER_Ether_Interrupts_Tx application.
*/
/**************************************************************************************************************/
#if defined (__VXWORKS__)
int32_t SER_EtherInterrupts_Rx(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_SER_EthInterrupt_Rx(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 SEREthIntRx
Queries the user for which channel they would like to configure interrupts on the following two Serial module
statuses: Receive Buffer Almost Empty and Receive Buffer Data Available. This routine will create a thread to
wait and receive the unprompted interrupt ethernet messages.
\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_SER_EthInterrupt_Rx(int32_t cardIndex, int32_t module, uint32_t modid)
{
   bool_t bQuit = FALSE;
   nai_status_t status;
   int32_t MAX_CHANNELS = naibrd_SER_GetChannelCount(modid);
   int32_t channel;
   nai_ser_status_type_t type;
   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_SER_RX_RESPONSE_PROTOCOL;
   uint16_t iplen = DEF_SER_RX_RESPONSE_IP_LEN;
   uint16_t port;
   uint8_t ip[DEF_SER_RX_RESPONSE_IP_LEN];
   int32_t i;
   int8_t inputBuffer[80];
   int32_t inputResponseCnt;

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

   /* Get the Serial channel to configure for Rx Ethernet interrupts */
   printf("Serial Rx Channel Selection:");
   bQuit = naiapp_query_ChannelNumber(MAX_CHANNELS, 1, &channel);

   if (!bQuit)
      bQuit = QueryEthInterruptRxIPAddr();

   if (!bQuit)
   {
      if (bGen4SerialIDRCommands)
      {
         /* Make sure there are no pending interrupts before interrupts are enabled */
         switch (channel)
         {
            case 1:
               type = NAI_SER_STATUS_CHANNEL1_LATCHED;
               vector = NAI_SER_CHANNEL1_INTERRUPT_VECTOR;
               break;
            case 2:
               type = NAI_SER_STATUS_CHANNEL2_LATCHED;
               vector = NAI_SER_CHANNEL2_INTERRUPT_VECTOR;
               break;
            case 3:
               type = NAI_SER_STATUS_CHANNEL3_LATCHED;
               vector = NAI_SER_CHANNEL3_INTERRUPT_VECTOR;
               break;
            case 4:
               type = NAI_SER_STATUS_CHANNEL4_LATCHED;
               vector = NAI_SER_CHANNEL4_INTERRUPT_VECTOR;
               break;
            default:
               type = NAI_SER_STATUS_CHANNEL1_LATCHED;
               vector = NAI_SER_CHANNEL1_INTERRUPT_VECTOR;
               break;
         }

         /* Clear NAI_SER_GEN5_INT_RXBUF_ALMOST_FULL and NAI_SER_GEN5_INT_RXAVAILABLE channel status */
         check_status(naibrd_SER_ClearChannelStatus(cardIndex, module, type, NAI_SER_GEN5_INT_RXBUF_ALMOST_FULL | NAI_SER_GEN5_INT_RXAVAILABLE));

         /* Configure the Serial channel for transmission */
         Cfg_Rx_Serial(cardIndex, module, channel, modid);

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

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

         /* Configure to generate interrupts on NAI_SER_GEN5_INT_RXBUF_ALMOST_FULL and NAI_SER_GEN5_INT_RXAVAILABLE */
         check_status(naibrd_SER_SetInterruptEdgeLevel(cardIndex, module, type, 0));  /* Edge triggered */
         check_status(naibrd_SER_SetInterruptVector(cardIndex, module, type, vector));
         check_status(naibrd_SER_SetInterruptSteering(cardIndex, module, type, NAIBRD_INT_STEERING_ON_BOARD_1));
         check_status(naibrd_SER_SetInterruptEnable(cardIndex, module, type, NAI_SER_GEN5_INT_RXBUF_ALMOST_FULL | NAI_SER_GEN5_INT_RXAVAILABLE));

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

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

#if defined (__VXWORKS__)
         taskSpawn("uprSerialRxHandler", 100, 0, 10000, (FUNCPTR)UPR_SerialRxEthInt_Handler, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0);
#elif defined (LINUX)
         pthread_create(&interruptSerialRxThread, NULL, (void*)UPR_SerialRxEthInt_Handler, NULL);
#else /* Default Windows */
         CreateThread(NULL, 0, UPR_SerialRxEthInt_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)
            {
               terminateSerialRxThread = TRUE;

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

/**************************************************************************************************************/
/** \ingroup SEREthIntRx
This function queries the user to enter the IP address associated with receiving Serial Interrupt commands.
*/
/**************************************************************************************************************/
static bool_t QueryEthInterruptRxIPAddr(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_SER_RX_RESPONSE_IP_ADDR[0], DEF_SER_RX_RESPONSE_IP_ADDR[1], DEF_SER_RX_RESPONSE_IP_ADDR[2], DEF_SER_RX_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_SER_RX_RESPONSE_IP_ADDR);
            }
         }
         if (!bQuit)
         {
            /* Get the Response Port */
            printf("Please Enter IDR Response Port: [default=%d]): ", DEF_SER_RX_RESPONSE_PORT);
            bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
            if (bQuit)
               bContinue = FALSE;
            else
            {
               if (inputResponseCnt > 0)
                  DEF_SER_RX_RESPONSE_PORT = (uint16_t)atol((const char *)inputBuffer);
               bContinue = FALSE;
            }

         }
      }
   }
   return bQuit;
}

/**************************************************************************************************************/
/** \ingroup SEREthIntRx
This function configures the Serial channel. The Serial channel is configured to run in async mode, loopback
(so that external cabling is not needed to generate Rx interrupts), internal clock, no parity, 8 data bits,
1 stop bit, and 9600 baud.
\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 modid     (Input) The ID of the module.
*/
/**************************************************************************************************************/
static void Cfg_Rx_Serial(int32_t cardIndex, int32_t module, int32_t channel, uint32_t modid)
{
   int32_t nCntlValueLo;
   int32_t i;

   check_status(naibrd_SER_ChannelReset(cardIndex, module, channel));
   check_status(naibrd_SER_ClearRxFifo(cardIndex, module, channel));
   nCntlValueLo = NAI_SER_CTRLLO_CLEAR_RX_FIFO;
   for (i = 0; i < CLEAR_FIFO_TIMEOUT && (nCntlValueLo & NAI_SER_CTRLLO_CLEAR_RX_FIFO); i++)
   {
      nai_ser_chanctrl chanCtrlRaw;
      check_status(naibrd_SER_GetChannelControlRaw(cardIndex, module, channel, &chanCtrlRaw));
      nCntlValueLo = chanCtrlRaw & 0x0000FFFF;
      nai_msDelay(1);
   }
   if (i == CLEAR_FIFO_TIMEOUT)
   {
      printf("Failed to clear FIFO for channel:%d\n", channel);
   }

   check_status(naibrd_SER_SetProtocol(cardIndex, module, channel, NAI_SER_PROTOCOL_ASYNC));       /* async mode */
   check_status(naibrd_SER_SetInterfaceLevel(cardIndex, module, channel, NAI_SER_INTF_LOOPBACK));  /* LoopBack */
   check_status(naibrd_SER_SetClockMode(cardIndex, module, channel, TXINT_RXINT));                 /* Tx and Rx internal */
   check_status(naibrd_SER_SetParity(cardIndex, module, channel, NAI_SER_PARITY_NONE));            /* No Parity */
   check_status(naibrd_SER_SetDataBits(cardIndex, module, channel, 8));                            /* 8 Data Bits */
   check_status(naibrd_SER_SetStopBits(cardIndex, module, channel, 1));                            /* 1 Stop Bit */
   check_status(naibrd_SER_SetBaudrate(cardIndex, module, channel, 9600));                         /* 9600, async mode */

   if (NAI_MODULE_ID_P8 == modid || NAI_MODULE_ID_PC == modid || NAI_MODULE_ID_PD == modid ||
      NAI_MODULE_ID_Px == modid || NAI_MODULE_ID_KB == modid)
   {
      nai_msDelay(20);     /* Allow 20ms for the HW to acknowledge the configuration (GEN 2/3 only)*/
   }

   check_status(naibrd_SER_SetReceiverEnable(cardIndex, module, channel, 1));                      /* Enable Receiver */
}

/**************************************************************************************************************/
/** \ingroup SEREthIntRx
This function configures the IDR (Interrupt Driven Response) commands when a Serial Rx interrupt occurs.
There are two Ethernet commands that will be processed by the board when a Serial Rx interrupt occurs. This
routine calls the MakeSerialRxReadRegsCommand() and MakeSerialRxWriteRegsCommand() 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 InitSERRxIDRCommands(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 (bGen4SerialIDRCommands)
      {
         /* First command */
         MakeSerialRxReadRegsCommand(bGen4SerialIDRCommands, msgIndex, moduleOffset, channel, commands, &ethcmdlen);
         msgIndex += ethcmdlen;
         idrcmdlen += ethcmdlen;
         idrcmdcnt++;

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

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

/**************************************************************************************************************/
/** \ingroup SEREthIntRx
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) Serial channel.
\param commands     (Input) Message buffer to fill with the Ethernet Read Registers elements.
\param cmdlen       (Output) Message size.
*/
/**************************************************************************************************************/
static void MakeSerialRxReadRegsCommand(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;

   if (bGen4Ether)
   {
      seqno = 0;
      regaddr = moduleOffset + NAI_SER_GEN5_ChanStatusLatchedAddr_4Ch[channel - 1];
      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 SEREthIntRx
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) Serial channel.
\param commands     (Input) Message buffer to fill with the Ethernet Write Registers elements.
\param cmdlen       (Output) Message size.
*/
/**************************************************************************************************************/
static void MakeSerialRxWriteRegsCommand(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 data = NAI_SER_GEN5_INT_RXBUF_ALMOST_FULL | NAI_SER_GEN5_INT_RXAVAILABLE;  /* Clear interrupt bits */

   if (bGen4Ether)
   {
      seqno = 0;
      regaddr = moduleOffset + NAI_SER_GEN5_ChanStatusLatchedAddr_4Ch[channel - 1];
      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 SEREthIntRx
This thread calls CreateSerialRxIDRTCPServer or CreateSerialRxIDRUDPServer depending on the
DEF_SER_RESPONSE_PROTOCOL to receive the Interrupt Driven Response (IDR) Unprompted Ethernet Replies.
*/
/**************************************************************************************************************/
#if defined (__VXWORKS__)
int UPR_SerialRxEthInt_Handler(int32_t nParam)
{
#elif LINUX
static void* UPR_SerialRxEthInt_Handler(void *lpParam)
{
#else /* Default Windows */
DWORD WINAPI UPR_SerialRxEthInt_Handler(LPVOID lpParam)
{
#endif

   uint16_t protocol = DEF_SER_RX_RESPONSE_PROTOCOL;

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

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

/**************************************************************************************************************/
/** \ingroup SEREthIntRx
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 CreateSerialRxIDRTCPServer(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;

   sprintf((char *)portbuf, "%d", DEF_SER_RX_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;
   }

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

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

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

   closesocket(listensock);

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

   do
   {
      result = recv(clientsock, (char *)recvbuf, recvbuflen, 0);
      if (result > 0)
      {
         printf("\n\nTCP Server: Bytes received: %d\n", result);
         DecodeUPRSerialRxIDRMessages(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) && (terminateSerialRxThread == FALSE));

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

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

/**************************************************************************************************************/
/** \ingroup SEREthIntRx
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 CreateSerialRxIDRUDPServer()
{
   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

   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_SER_RX_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 (terminateSerialRxThread == 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);
         DecodeUPRSerialRxIDRMessages(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 SEREthIntRx
This function calls ParseSerialRxIDRResponse() to break down the IDR message received to individual Ethernet messages
and calls DisplayDecodedSerialRxIDRResponse() to display the data in the Ethernet message.
\param msg     (Input) Input message buffer.
\param msgsize (Input) Size of input message buffer.
*/
/**************************************************************************************************************/
static void DecodeUPRSerialRxIDRMessages(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 (ParseSerialRxIDRResponse(startIndex, arraysize, (uint8_t *)msg, &responselen, MAX_ETHER_BLOCK_REG_CNT, response))
      {
         startIndex += responselen;
         DisplayDecodedSerialRxIDRResponse(responselen, response);
      }
   }
}

/**************************************************************************************************************/
/** \ingroup SEREthIntRx
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 ParseSerialRxIDRResponse(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 (bGen4SerialIDRCommands)
   {
      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("Serial Ethernet Interrupt Support Prior to Generation 4 Ethernet commands currently not supported\n");
   }
   return bParsed;
}

/**************************************************************************************************************/
/** \ingroup SEREthIntRx
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 DisplayDecodedSerialRxIDRResponse(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 (bGen4SerialIDRCommands)
      gen = NAI_ETHER_GEN4;
   else
      gen = NAI_ETHER_GEN3;

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

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

   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