SER EtherInterrupts Rx
Edit this on GitLab
SER EtherInterrupts Rx
Explanation
About This Sample Application Code
This code is a sample application provided by North Atlantic Industries (NAI) to interact with their embedded function modules through their SSK (System Solutions Kit). The purpose of this application is to demonstrate methods for handling Serial Ethernet interrupt operations using NAI’s protocol with a given serial module.
Key Components of the Code
Included Libraries and Headers - Standard C libraries: 'stdio.h', 'stdlib.h', 'string.h', 'time.h' - NAI Sample Program header files: - 'naiapp_boardaccess_menu.h' - 'naiapp_boardaccess_query.h' - 'naiapp_boardaccess_access.h' - 'naiapp_boardaccess_display.h' - 'naiapp_boardaccess_utils.h' - Additional NAI provided header files for handling specific functionalities: - 'nai.h', 'naibrd.h' - 'naibrd_ser.h' - 'naibrd_ether.h' - 'nai_ether_adv.h' - 'nai_map_ser.h'
Platform-Specific Includes For different operating systems, the code includes: - Windows: 'winsock2.h', 'ws2tcpip.h', and related network functions. - Linux: Socket API includes from 'sys/errno.h' and 'pthread.h'. - VxWorks: 'netinet/ip.h' for network-related functions.
Function Prototypes - Functions for running, configuring and handling Ethernet and Serial interrupt operations. - 'Run_SER_EthInterrupt_Rx()': Main function to run the serial Ethernet interrupts. - 'QueryEthInterruptRxIPAddr()': Gets IP address from user. - 'Cfg_Rx_Serial()': Configures serial channel. - 'InitSERRxIDRCommands()': Initializes IDR commands for Ethernet interrupts. - 'MakeSerialRxReadRegsCommand()', 'MakeSerialRxWriteRegsCommand()': Create commands for reading and writing registers. - 'CreateSerialRxIDRTCPServer()', 'CreateSerialRxIDRUDPServer()': Create TCP/UDP servers to handle interrupt messages. - 'DecodeUPRSerialRxIDRMessages()', 'ParseSerialRxIDRResponse()', 'DisplayDecodedSerialRxIDRResponse()': Handle and display responses from interrupts.
Macros Definitions A few important macros defined are: - Serial Channel Interrupt vectors. - Default configurations such as ports and IP addresses. - Various fixed constants pertaining to interrupt handling like timeout and status addresses.
Main Function The main function initializes and runs the board access menu, queries the user for card and module information, and then runs the serial Ethernet interrupt handling routine. If the process is successful, it waits for user input to either quit or restart the application.
Interrupt Handling 'Run_SER_EthInterrupt_Rx()' is the function where the core logic to handle serial Ethernet interrupts is implemented. Here, the configuration is set up for the selected serial channel to handle interrupts and start IDR. It runs in a loop waiting for user input to either continue or quit.
Serial Configuration The function 'Cfg_Rx_Serial()' sets up the serial channel with specific parameters like async mode, loopback, internal clock, no parity, 8 data bits, 1 stop bit, and 9600 baud rate.
Preparing and Making Commands Commands for handling Ethernet and serial interactions: - 'InitSERRxIDRCommands()': Prepares the commands. - 'MakeSerialRxReadRegsCommand()', 'MakeSerialRxWriteRegsCommand()': Frame specific read and write commands.
Socket Servers Depending on the set protocol (TCP or UDP), the code creates appropriate server threads to handle incoming interrupt-driven responses.
Message Decoding Functions handle the parsing and display of interrupt-driven responses: - 'DecodeUPRSerialRxIDRMessages()': Process entire messages. - 'ParseSerialRxIDRResponse()': Breakdown messages. - 'DisplayDecodedSerialRxIDRResponse()': Display the parsed information.
Conclusion This sample application code is a comprehensive example of communicating with NAI’s embedded function modules using serial and Ethernet protocols. It covers initializing, configuring, handling, and displaying the interrupt responses, providing a practical guide for developers working with NAI�s SSK and similar embedded systems.
#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, ðcmdlen);
msgIndex += ethcmdlen;
idrcmdlen += ethcmdlen;
idrcmdcnt++;
/* Next command */
MakeSerialRxWriteRegsCommand(bGen4SerialIDRCommands, msgIndex, moduleOffset, channel, commands, ðcmdlen);
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)
{
/* Create a ReadRegs command with the following attributes:
Onboard Access, 32-bit Register Size,
Reg Address: NAI_SER_GEN5_ChanStatusLatchedAddr_4Ch[channel-1] (to read the channel status)
Count: 1, Stride 4
*/
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)
{
/* Create a WriteRegs command with the following attributes:
Onboard Access, 32-bit Register Size,
Reg Address: NAI_SER_GEN5_ChanStatusLatchedAddr_4Ch[channel-1], Count: 1, Stride 4,
Data: NAI_SER_GEN5_INT_RXBUF_ALMOST_FULL | NAI_SER_GEN5_INT_RXAVAILABLE (to clear the interrupts)
*/
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;
/* Resolve the server address and port */
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;
}
/* 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);
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));
/* 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 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
/* 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_RESPONSE_PORT.
*/
/* The IPv4 family */
recvaddr.sin_family = AF_INET;
recvaddr.sin_port = htons(DEF_SER_RX_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 (terminateSerialRxThread == 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);
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
}
/* 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 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);
/* 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;
}
}