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

Explanation

About This Application Code

This C application code is designed to interact with North Atlantic Industries (NAI) embedded function modules. It demonstrates how to handle Ethernet interrupts using NAI�s protocol with a given IRIG module. Below is a detailed explanation and walk-through of the code.

File Inclusions and Definitions

  • Standard Libraries: The code includes standard C libraries for input/output operations, memory management, string operations, and time handling.

  • NAI Specific Headers: Multiple header files from the NAI software suite are included to provide access to board, module, Ethernet, and utility functions.

  • Platform Specific Headers: Depending on the OS (Windows, Linux, or VXWorks), it includes specific headers and defines certain macros for socket operations.

Global Variables and Macros

  • 'CONFIG_FILE': Specifies the default configuration file to be used.

  • Function Prototypes: A list of static functions used within the file for handling various operations.

  • 'NAI_IRIG_CHANNEL1_INTERRUPT_VECTOR': Defines the interrupt vector for channel 1 of the IRIG module.

  • Boolean Flags and Defaults: Flags and default values for Ethernet responses, communication protocols, and other configurations.

Function Definitions and Main Application

Main Function (Operating as 'RG_EtherInterrupts' in VXWorks or 'main' in other OS)

  • Environment Setup: The application initializes by calling 'naiapp_RunBoardMenu' with the configuration file.

  • Card and Module Querying: It queries the user for the card index and module number.

  • Module ID Check: If a valid module ID is returned, it proceeds to run the 'Run_IRIG_EthInterrupt' function.

  • Termination Handling: Provides options to restart the application or quit based on user input.

Key Functionality

'Run_IRIG_EthInterrupt'

  • Channel Selection: Queries the user for the IRIG channel to configure for Ethernet interrupts.

  • IP Address Query: Calls 'QueryEthInterruptIPAddr' to get the IP address for receiving IRIG interrupt commands.

  • Interrupt Configuration: Configures the interrupt settings and sets up Ethernet commands for IRIG event handling. It involves clearing pending interrupts, setting up event-mapped interrupt triggers, and enabling the interrupt.

  • Thread Creation: Starts a thread using 'UPR_IRIGEthInt_Handler' to handle incoming Ethernet messages.

'QueryEthInterruptIPAddr'

  • User Prompt: Queries the user for the IP address and port for receiving IRIG interrupt responses. This allows customization to different network setups.

'Init_IRIG_IDRCommands'

  • Command Initialization: Initializes Ethernet commands (read and write commands) to be processed upon an IRIG interrupt. Calls 'MakeIRIGReadRegsCommand' and 'MakeIRIGWriteRegsCommand' to format the buffers.

'MakeIRIGReadRegsCommand' & 'MakeIRIGWriteRegsCommand'

  • Command Formatting: Format and fill buffers with information for the Ethernet read and write registers command, respectively. These include register addresses and data to read/write.

Unprompted Reply Handling

'UPR_IRIGEthInt_Handler'

  • Protocol Handling: Based on the protocol defined ('DEF_IRIG_RESPONSE_PROTOCOL'), it calls either 'Create_IRIG_IDR_TCPServer' or 'Create_IRIG_IDR_UDPServer' to start the appropriate server for handling incoming messages.

'Create_IRIG_IDR_TCPServer'

  • TCP Server Creation: Sets up and binds a TCP server to listen for incoming interrupt-driven responses. Handles incoming messages by calling 'Decode_UPR_IRIG_IDRMessages'.

'Create_IRIG_IDR_UDPServer'

  • UDP Server Creation: Sets up and binds a UDP server to listen for incoming interrupt-driven responses. Handles incoming messages similarly to the TCP server.

'Decode_UPR_IRIG_IDRMessages'

  • Message Decoding: Parses the received messages, calling 'Parse_IRIG_IDRResponse' to break down messages into Ethernet commands and then displaying them using 'Display_Decoded_IRIG_IDRResponse'.

'Parse_IRIG_IDRResponse' & 'Display_Decoded_IRIG_IDRResponse'

  • Response Parsing: Identifies and extracts individual Ethernet messages from the response buffer.

  • Data Display: Outputs the decoded Ethernet message data to the console for user inspection.

Summary

This application code provides a comprehensive example of using NAI�s library functions to interact with and handle interrupts from IRIG function modules over Ethernet. The code sets up necessary configurations, manages user input for customizing the operation, handles Ethernet communication (both TCP and UDP), and processes the incoming interrupt messages effectively. The detailed implementation ensures robust interaction with NAI�s hardware modules, making it an essential tool for developers working with embedded systems using IRIG standards.

#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
//#define NAI_IRIG_CHANNEL2_INTERRUPT_VECTOR             0x000000A2u

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; /* TODO: Status Type */
               categoryType = NAIBRD_IRIG_EVTMAP_CATEGORY_BIT; /* TODO: Category Type */
               vector = NAI_IRIG_CHANNEL1_INTERRUPT_VECTOR;
               break;
            //case 2:
               //type = NAI_IRIG_STATUS_CHANNEL2_LATCHED;
               //vector = NAI_IRIG_CHANNEL2_INTERRUPT_VECTOR;
               //break;
            default:
               statusType = NAIBRD_IRIG_EVTMAP_STATUS_BIT_DATA_LOSS_LATCHED; /* TODO: Status Type */
               categoryType = NAIBRD_IRIG_EVTMAP_CATEGORY_BIT; /* TODO: Category Type */
               vector = NAI_IRIG_CHANNEL1_INTERRUPT_VECTOR;
               break;
         }

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

         /* Configure the IRIG channel for transmission *
         Cfg_IRIG(cardIndex, module, channel, modid);*/

         /* 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 on TODO */
         check_status(naibrd_IRIG_SetEventMappedInterruptTriggerType(cardIndex, module, channel, statusType, NAIBRD_IRIG_EDGE_INTERRUPT));  /* Edge triggered */
         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 IRIG channel.
\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_IRIG(int32_t cardIndex, int32_t module, int32_t channel, uint32_t modid)
{
   TODO: Configure IRIG for Interrupt
   int32_t nCntlValueLo;
   int32_t i;

   check_status(naibrd_IRIG_ChannelReset(cardIndex, module, channel));
   check_status(naibrd_IRIG_ClearFifo(cardIndex, module, channel));
   nCntlValueLo = NAI_IRIG_CTRLLO_CLEAR_FIFO;
   for (i = 0; i < CLEAR_FIFO_TIMEOUT && (nCntlValueLo & NAI_IRIG_CTRLLO_CLEAR_FIFO); i++)
   {
      nai_irig_chanctrl chanCtrlRaw;
      check_status(naibrd_IRIG_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_IRIG_SetProtocol(cardIndex, module, channel, NAI_IRIG_PROTOCOL_ASYNC));
   check_status(naibrd_IRIG_SetInterfaceLevel(cardIndex, module, channel, NAI_IRIG_INTF_LOOPBACK));
   check_status(naibrd_IRIG_SetClockMode(cardIndex, module, channel, CLOCKMODE));
   check_status(naibrd_IRIG_SetParity(cardIndex, module, channel, NAI_IRIG_PARITY_NONE));
   check_status(naibrd_IRIG_SetDataBits(cardIndex, module, channel, 8));
   check_status(naibrd_IRIG_SetStopBits(cardIndex, module, channel, 1));
   check_status(naibrd_IRIG_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 the HW to acknowledge the configuration (GEN 2/3 only)*
   }

   check_status(naibrd_IRIG_SetReceiverEnable(cardIndex, module, channel, 1));
}*/

/**************************************************************************************************************/
/** \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)
   {
      /* Create a ReadRegs command with the following attributes:
            Onboard Access, 32-bit Register Size,
            Reg Address: NAI_IRIG_GEN5_REG_COM_BIT_INTERRUPTS_LAT_ADD (to read the channel status)
            Count: 1, Stride 4
      */
      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)
   {
      /* Create a WriteRegs command with the following attributes:
            Onboard Access, 32-bit Register Size,
            Reg Address: NAI_IRIG_GEN5_REG_COM_BIT_INTERRUPTS_LAT_ADD, Count: 1, Stride 4,
            Data: 0xFFFFFFFF (to clear the interrupts)
      */
      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;
   }

   /* Set up a SOCKADDR_IN structure that will tell bind that we
      want to receive datagrams from all interfaces using port DEF_IRIG_RESPONSE_PORT.
   */
   /* The IPv4 family */
   recvaddr.sin_family = AF_INET;
   recvaddr.sin_port = htons(DEF_IRIG_RESPONSE_PORT);
   /* From all interface (0.0.0.0) */
   recvaddr.sin_addr.s_addr = htonl(INADDR_ANY);

   /* Associate the address information with the socket using bind.
      At this point you can receive datagrams on your bound socket.
   */
#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)
   {
      /* Call recvfrom() to get it then display the received data. */
#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
   }

   /* When application is finished receiving datagrams close the socket. */
   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;                                     /* Bit 15: Unprompted Response  Bit 14: TDR(0)/IDR(1) Response */
   seqrspidindex = (seq & (ETHER_GEN4_SEQ_ID_MASK)) >> ETHER_GEN4_SEQ_ID_SHIFT;    /* Bits 13-10: TDR/IDR ID Index (Note, value is ID-1) */
   seqcmdindex = (seq & (ETHER_GEN4_SEQ_CMD_MASK)) >> ETHER_GEN4_SEQ_CMD_SHIFT;    /* Bits 9-6: Command Index */
   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