DIF EtherInterrupts
Edit this on GitLab
DIF EtherInterrupts Sample Application (SSK 1.x)
Overview
The DIF EtherInterrupts sample application demonstrates how to configure and handle Ethernet-based interrupts on differential (DIF) I/O modules using the NAI Software Support Kit (SSK 1.x). The sample uses the Ethernet IDR (Interrupt Driven Response) mechanism: when an interrupt fires on the module, the board’s onboard processor executes a set of pre-configured Ethernet commands (reading and clearing status registers) and sends the results to your application over TCP or UDP. This eliminates the need for a hardware ISR on the host and delivers all status values in a single network message.
The sample covers three DIF interrupt status types: BIT (Built-In Test) fault, low-to-high transition, and high-to-low transition. It configures a single DIF channel as an input, sets up the IDR command payload, enables interrupts, and spawns a listener thread that receives and decodes the Ethernet interrupt messages.
This sample supports DIF module types such as DF1 and DF2. Consult your module’s manual for the specific DIF module types supported by your hardware.
For background on interrupt concepts — including edge vs. level triggering, interrupt vector numbering, steering architecture, and Ethernet IDR fundamentals — see the Interrupts API Guide. This guide focuses on how those concepts apply specifically to DIF modules and walks through the practical Ethernet IDR implementation using the SSK 1.x API.
Prerequisites
Before running this sample, make sure you have:
-
An NAI board with a DIF module installed (DF1, DF2, or a compatible module type).
-
A Gen4 or later board with Ethernet support — Ethernet IDR requires Gen4 Ethernet commands.
-
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 and the board, with no firewall blocking the configured IDR response port.
How to Run
Launch DIF_EtherInterrupts from your build output directory. On startup the application looks for a configuration file (default_DIFEthInterrupts.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 a DIF channel, IDR response IP address and port, and interrupt steering mode, then begins listening for Ethernet interrupt messages.
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 DIF. For details on board connection configuration, see the First Time Setup Guide. |
The main() function follows a standard SSK 1.x startup flow:
-
Call
naiapp_RunBoardMenu()to load a saved configuration file (if one exists) or present the interactive board menu. The configuration file (default_DIFEthInterrupts.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. -
Call
naiapp_query_CardIndex()to query the user for the card index. -
Call
naibrd_GetModuleCount()andnaiapp_query_ModuleNumber()to query for the module slot. -
Retrieve the module ID with
naibrd_GetModuleID()and pass control toRun_DIF_EthInterrupt().
#if defined (__VXWORKS__)
int32_t DIF_EtherInterrupts(void)
#else
int32_t main(void)
#endif
{
bool_t stop = FALSE;
int32_t cardIndex;
int32_t moduleCnt;
int32_t module;
uint32_t moduleID = 0;
int8_t inputBuffer[80];
int32_t inputResponseCnt;
if (naiapp_RunBoardMenu(CONFIG_FILE) == TRUE)
{
while (stop != TRUE)
{
stop = naiapp_query_CardIndex(naiapp_GetBoardCnt(), 0, &cardIndex);
if (stop != TRUE)
{
check_status(naibrd_GetModuleCount(cardIndex, &moduleCnt));
stop = naiapp_query_ModuleNumber(moduleCnt, 1, &module);
if (stop != TRUE)
{
moduleID = naibrd_GetModuleID(cardIndex, module);
if (moduleID != 0)
{
Run_DIF_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);
}
}
naiapp_access_CloseAllOpenCards();
return 0;
}
|
Important
|
Common connection errors you may encounter at this stage:
|
Program Structure
The DIF EtherInterrupts sample is contained in a single source file, DIF_EtherInterrupts.c. The program flow is:
-
Board connection and module selection — the standard SSK 1.x startup sequence described above.
-
Channel and network configuration — the user selects a DIF channel, enters the IDR response IP address and port, and chooses onboard or offboard interrupt steering.
-
Module configuration — the application clears pending status, configures the channel as an input, builds the IDR command payload, and enables interrupts.
-
IDR start and listener thread — the IDR is started on the board and a TCP or UDP listener thread is spawned to receive interrupt messages.
-
Decode and display — incoming messages are parsed into individual Ethernet responses and their register data is displayed.
The entry point is main() (or DIF_EtherInterrupts() on VxWorks). The core configuration and interrupt handling logic lives in the static Run_DIF_EthInterrupt() function.
Key Constants
The sample defines several constants that control IDR behavior:
#define DEF_DIF_IDR_ID 3
#define DEF_DIF_ETHER_CMD_COUNT 3
#define DEF_DIF_RESPONSE_PROTOCOL ETHER_GEN4_TCP_PROTOCOL
#define DEF_DIF_RESPONSE_IP_LEN ETHER_GEN4_IPv4_ADDR_LEN
static uint16_t DEF_DIF_RESPONSE_PORT = 52801;
static uint8_t DEF_DIF_RESPONSE_IP_ADDR[] = {192,168,1,100};
-
DEF_DIF_IDR_ID— the IDR identifier (ID 3) used to register and manage this IDR configuration on the board. Each IDR on a board must have a unique ID. -
DEF_DIF_ETHER_CMD_COUNT— the number of Ethernet commands in the IDR payload (three: read statuses, write to clear, read again to confirm). -
DEF_DIF_RESPONSE_PROTOCOL— TCP by default. Change toETHER_GEN4_UDP_PROTOCOLfor UDP delivery. -
DEF_DIF_RESPONSE_PORTandDEF_DIF_RESPONSE_IP_ADDR— the IP address and port where your application listens for IDR responses. The user is prompted to override these at runtime.
Interrupt Status Types
The DIF module exposes three interrupt status types that this sample configures. Each type monitors a different hardware condition and latches when that condition is detected.
BIT (Built-In Test) / Fault
NAI_DIF_STATUS_BIT_LATCHED
The module’s internal self-test has detected an anomaly on a channel. The Built-In Test circuitry continuously monitors the health of each differential channel. When BIT detects a problem, it latches the corresponding status bit and, if enabled, raises an interrupt. Enable BIT interrupts for health monitoring or safety-critical applications where a failed channel must be flagged immediately.
Low-to-High Transition
NAI_DIF_STATUS_LO_HI_TRANS_LATCHED
A differential input transitioned from low to high. The module captures the rising edge and latches the status bit so the event is not lost even if the input changes again before the software reads the register. Enable low-to-high transition interrupts for rising-edge detection: signal activation, event counting, or detecting when an external condition becomes active.
High-to-Low Transition
NAI_DIF_STATUS_HI_LO_TRANS_LATCHED
A differential input transitioned from high to low. Like its low-to-high counterpart, the module captures the falling edge and latches it. Enable high-to-low transition interrupts for falling-edge detection: signal deactivation or detecting when an external condition clears. Used together with low-to-high interrupts, the two transition types give full visibility into both edges of a signal.
The sample enables all three status types for demonstration. In your own application, enable only the status types relevant to your use case to reduce interrupt traffic and simplify your handler logic.
Interrupt Configuration
The Run_DIF_EthInterrupt() function performs the complete module configuration sequence before starting the IDR. Each step is explained below with the corresponding code.
Step 1: Check Gen4 Ethernet Support
Ethernet IDR requires Generation 4 or later Ethernet commands. The application checks this before proceeding:
bGen4DIFIDRCommands = SupportsGen4Ether(cardIndex);
If the board does not support Gen4, the application prints an error and exits. You may need to update your board firmware to use Ethernet IDR.
Step 2: Configure Channel as Input
To receive transition interrupts, the DIF channel must be configured as an input. The sample sets both the operating mode and the I/O format:
check_status(naibrd_DIF_SetOpMode(cardIndex, module, channel, NAI_DIF_MODE_STD_INPUT_OUTPUT));
check_status(naibrd_DIF_SetIOFormat(cardIndex, module, channel, NAI_DIF_IOFORMAT_INPUT));
naibrd_DIF_SetOpMode() sets the channel to standard input/output mode. naibrd_DIF_SetIOFormat() then configures the direction as input. If your channel is already configured as an input from a previous setup, these calls are still safe — they confirm the desired state.
Step 3: Clear Pending Status
Stale latched status from a previous run will trigger an immediate interrupt if not cleared. The sample clears all three status types before enabling interrupts:
check_status(naibrd_DIF_ClearStatus(cardIndex, module, channel, NAI_DIF_STATUS_BIT_LATCHED));
check_status(naibrd_DIF_ClearStatus(cardIndex, module, channel, NAI_DIF_STATUS_LO_HI_TRANS_LATCHED));
check_status(naibrd_DIF_ClearStatus(cardIndex, module, channel, NAI_DIF_STATUS_HI_LO_TRANS_LATCHED));
Always clear status registers before enabling interrupts. If you skip this step, stale status bits will not generate a new edge and the interrupt will never fire.
Step 4: Set Trigger Mode
The sample configures all three status types for level-triggered interrupts:
check_status(naibrd_DIF_SetEdgeLevelInterrupt(cardIndex, module, channel, NAI_DIF_STATUS_BIT_LATCHED, NAI_DIF_LEVEL_INTERRUPT));
check_status(naibrd_DIF_SetEdgeLevelInterrupt(cardIndex, module, channel, NAI_DIF_STATUS_LO_HI_TRANS_LATCHED, NAI_DIF_LEVEL_INTERRUPT));
check_status(naibrd_DIF_SetEdgeLevelInterrupt(cardIndex, module, channel, NAI_DIF_STATUS_HI_LO_TRANS_LATCHED, NAI_DIF_LEVEL_INTERRUPT));
NAI_DIF_LEVEL_INTERRUPT selects level-triggered mode, where the interrupt continues to assert as long as the latched condition remains active. For edge-triggered mode (fires once per event), use NAI_DIF_EDGE_INTERRUPT instead. For background on edge vs. level triggering, see the Interrupts API Guide.
Step 5: Set Interrupt Vectors
The sample assigns the same interrupt vector (NAI_DIF_INTERRUPT_VECTOR) to all three status types so that a single IDR handles every DIF interrupt on the module:
check_status(naibrd_DIF_SetGroupInterruptVector(cardIndex, module, 1, NAI_DIF_STATUS_BIT_LATCHED, NAI_DIF_INTERRUPT_VECTOR));
check_status(naibrd_DIF_SetGroupInterruptVector(cardIndex, module, 1, NAI_DIF_STATUS_LO_HI_TRANS_LATCHED, NAI_DIF_INTERRUPT_VECTOR));
check_status(naibrd_DIF_SetGroupInterruptVector(cardIndex, module, 1, NAI_DIF_STATUS_HI_LO_TRANS_LATCHED, NAI_DIF_INTERRUPT_VECTOR));
The vector identifies which interrupt source generated the event. The 1 parameter is the group number (group 1 covers all channels). If you need to distinguish status types inside your handler without parsing the IDR message, you can assign a different vector to each type and register separate IDRs.
Step 6: Set Interrupt Steering
Interrupt steering determines how the interrupt signal is routed from the module. The user selects onboard or offboard at runtime:
check_status(naibrd_DIF_SetGroupInterruptSteering(cardIndex, module, 1, NAI_DIF_STATUS_BIT_LATCHED, steering));
check_status(naibrd_DIF_SetGroupInterruptSteering(cardIndex, module, 1, NAI_DIF_STATUS_LO_HI_TRANS_LATCHED, steering));
check_status(naibrd_DIF_SetGroupInterruptSteering(cardIndex, module, 1, NAI_DIF_STATUS_HI_LO_TRANS_LATCHED, steering));
The steering value is determined by the user’s onboard/offboard selection:
-
Onboard (
NAIBRD_INT_STEERING_ON_BOARD_1) — triggers an Ethernet IDR response. The board’s onboard processor intercepts the interrupt and sends a notification over the network. This is the typical choice for Ethernet IDR. -
Offboard cPCI (
NAIBRD_INT_STEERING_CPCI_APP) — routes the interrupt to the CompactPCI backplane host, which can then process it locally.
For Ethernet IDR, onboard steering is the standard path. The interrupt fires on the board’s processor, which then executes the IDR commands and sends the result to your application over the network.
Step 7: Enable Interrupts
After configuring vectors, trigger mode, and steering, enable interrupt generation for the selected channel across all three status types:
check_status(naibrd_DIF_SetInterruptEnable(cardIndex, module, channel, NAI_DIF_STATUS_BIT_LATCHED, TRUE));
check_status(naibrd_DIF_SetInterruptEnable(cardIndex, module, channel, NAI_DIF_STATUS_LO_HI_TRANS_LATCHED, TRUE));
check_status(naibrd_DIF_SetInterruptEnable(cardIndex, module, channel, NAI_DIF_STATUS_HI_LO_TRANS_LATCHED, TRUE));
In your own application, enable only the status types and channels you need rather than enabling everything.
|
Important
|
Common interrupt configuration errors:
|
Ethernet IDR Configuration
After the module is configured, the application builds and registers the IDR command payload. The IDR tells the board: "When this interrupt vector fires, execute these Ethernet commands and send the results to this IP address and port."
IDR Command Payload
The InitDIFIDRCommands() function builds a three-command IDR payload:
-
Read status registers — reads the four latched status registers (BIT, low-to-high, high-to-low, and a fourth stride position) starting at
NAI_DIF_GEN5_REG_BIT_LATCHED_STATUS_ADDwith a stride of0x10. -
Write to clear status — writes
0x00FFFFFFto the same four register addresses, clearing all latched bits across all channels. -
Read status registers again — re-reads the same registers to confirm the clear took effect.
status = check_status(naibrd_GetModuleOffset(cardIndex, module, &moduleOffset));
if (status == NAI_SUCCESS)
{
/* First command: Read status registers */
MakeDIFReadRegsCommand(bGen4DIFIDRCommands, msgIndex, moduleOffset, commands, ðcmdlen, boardInterface);
msgIndex += ethcmdlen;
idrcmdcnt++;
/* Second command: Write to clear status */
MakeDIFWriteRegsCommand(bGen4DIFIDRCommands, msgIndex, moduleOffset, commands, ðcmdlen, boardInterface);
msgIndex += ethcmdlen;
idrcmdcnt++;
/* Third command: Read status registers again to confirm */
MakeDIFReadRegsCommand(bGen4DIFIDRCommands, msgIndex, moduleOffset, commands, ðcmdlen, boardInterface);
msgIndex += ethcmdlen;
idrcmdcnt++;
}
naibrd_GetModuleOffset() retrieves the base register address for the installed module, which is needed to build absolute register addresses for the IDR commands. MakeDIFReadRegsCommand() calls nai_ether_MakeReadMessage() to construct a Gen4 Ethernet read command. MakeDIFWriteRegsCommand() uses nai_ether_BeginWriteMessage(), nai_ether_WriteMessageData(), and nai_ether_FinishMessage() to construct a Gen4 Ethernet write command.
The write command clears all interrupt bits by writing 0x00FFFFFF to each of the four status register positions. This means the IDR is self-clearing — the board reads the status, clears it, and reads again, all in a single atomic IDR execution. Your application receives the pre-clear and post-clear values without needing to issue any additional commands.
Registering the IDR
Once the command payload is built, register it with the board:
status = check_status(naibrd_Ether_ClearIDRConfig(IDRcardIndex, (uint16_t)DEF_DIF_IDR_ID));
status = check_status(naibrd_Ether_SetIDRConfig(IDRcardIndex, (uint16_t)DEF_DIF_IDR_ID,
protocol, iplen, ip, port, NAI_DIF_INTERRUPT_VECTOR,
cmdcount, cmdlength, commands));
naibrd_Ether_ClearIDRConfig() removes any existing IDR registered under DEF_DIF_IDR_ID so the new configuration starts clean. naibrd_Ether_SetIDRConfig() programs the board with the complete IDR definition: the response protocol (TCP or UDP), IP address and port, the interrupt vector that triggers this IDR, and the command payload to execute.
Starting and Stopping the IDR
Start the IDR so the board begins sending messages when interrupts occur:
status = check_status(naibrd_Ether_StartIDR(IDRcardIndex, (uint16_t)DEF_DIF_IDR_ID));
When the user quits, stop the IDR before closing the connection:
status = check_status(naibrd_Ether_StopIDR(cardIndex, (uint16_t)DEF_DIF_IDR_ID));
Always stop the IDR before closing the connection. If you close the connection without stopping the IDR, the board will continue sending messages to an address that is no longer listening.
|
Important
|
Common IDR configuration errors:
|
Network Configuration
The sample prompts the user for the IDR response IP address and port at runtime through QueryEthInterruptDIFIPAddr():
printf("Please Enter IDR Response IP Address: [default=%d.%d.%d.%d]): ",
DEF_DIF_RESPONSE_IP_ADDR[0], DEF_DIF_RESPONSE_IP_ADDR[1],
DEF_DIF_RESPONSE_IP_ADDR[2], DEF_DIF_RESPONSE_IP_ADDR[3]);
The default IP address is 192.168.1.100 and the default port is 52801. Press Enter to accept the defaults, or enter your own values. The IP address must be the address of the machine where the sample is running (the machine that will receive the IDR responses). The port must not conflict with other services on that machine.
In your own application, set these values programmatically rather than prompting the user. The IP address should be the address of the network interface on your host that is reachable from the board.
Ethernet Listener Thread
After the IDR is started, the application spawns a background thread to receive the Ethernet interrupt messages. The protocol (TCP or UDP) is selected by DEF_DIF_RESPONSE_PROTOCOL:
if (protocol == ETHER_GEN4_TCP_PROTOCOL)
CreateDIFIDRTCPServer();
else
CreateDIFIDRUDPServer();
TCP Listener
CreateDIFIDRTCPServer() sets up a standard TCP listener: it binds to the configured port, listens for an incoming connection from the board, and enters a receive loop. Each received message is passed to DecodeUPRDIFIDRMessages() for parsing.
The TCP path provides reliable, ordered delivery. The board connects to your listener when the first IDR fires, and subsequent messages arrive on the same connection. If the connection drops, no further IDR messages will be delivered until the IDR is reconfigured and restarted.
UDP Listener
CreateDIFIDRUDPServer() sets up a UDP datagram receiver: it binds to the configured port and enters a recvfrom() loop. Each received datagram is passed to DecodeUPRDIFIDRMessages().
UDP provides lower latency but no delivery guarantee. If a datagram is lost, the corresponding interrupt event is missed. For most real-time monitoring applications, UDP is preferred because the lower overhead aligns with interrupt-driven workloads where timeliness matters more than guaranteed delivery.
Message Decoding
DecodeUPRDIFIDRMessages() iterates through the received buffer, calling ParseDIFIDRResponse() to extract individual Ethernet message frames. Each frame is delimited by Gen4 preamble and postamble markers (ETHER_GEN4_PREAMBLE and ETHER_GEN4_POSTAMBLE). The parser copies each complete frame and passes it to DisplayDecodedDIFIDRResponse().
DisplayDecodedDIFIDRResponse() decodes the message header using nai_ether_DecodeMessageHeader() and extracts the sequence number, typecode, and payload:
offset = nai_ether_DecodeMessageHeader(response, responselen, &seq, &tc, gen, &size);
/* Parse IDR Response Sequence Value */
seqrsptype = seq & ETHER_GEN4_SEQ_UPR_MASK;
seqrspidindex = (seq & (ETHER_GEN4_SEQ_ID_MASK)) >> ETHER_GEN4_SEQ_ID_SHIFT;
seqcmdindex = (seq & (ETHER_GEN4_SEQ_CMD_MASK)) >> ETHER_GEN4_SEQ_CMD_SHIFT;
The sequence number encodes three fields:
-
Response type (bit 15-14) — identifies whether this is a TDR (Time Driven Response) or IDR (Interrupt Driven Response) message.
-
ID index (bits 13-10) — the IDR ID that generated this response (value is ID minus 1).
-
Command index (bits 9-6) — which command in the IDR payload produced this response.
For read responses (NAI_ETHER_TYPECODE_RSP_COMMAND_COMPLETE_READ_4), the handler extracts 32-bit register values from the payload in big-endian order. These values correspond to the latched status registers that were read by the IDR command. A non-zero value in any position indicates that the corresponding status type has active latched bits.
|
Important
|
Common Ethernet listener errors:
|
Troubleshooting Reference
This table summarizes common errors and symptoms covered in the sections above. For detailed context, refer to the relevant section. Consult your module’s manual for hardware-specific diagnostic procedures.
| Error / Symptom | Possible Causes | Suggested Resolution |
|---|---|---|
No board found or connection timeout |
Board not powered, incorrect or missing configuration file, network issue |
Verify hardware is powered and connected. If the configuration file exists, check that it lists the correct interface and address. If it does not exist, the board menu will appear — configure and save your connection settings. |
Module not detected or not recognized as DIF |
No DIF module installed at the selected slot, incorrect module number |
Verify hardware configuration. |
Gen4 Ethernet not supported |
Board Ethernet firmware is older than Gen4 |
Consult board documentation for Ethernet generation support. Firmware update may be required. |
Interrupts not firing after enable |
Status registers not cleared before enabling, wrong steering mode, channel not configured as input |
Clear all status types before enabling interrupts. Verify steering is set to |
Wrong steering mode |
Using offboard steering when Ethernet IDR requires onboard, or vice versa |
For Ethernet IDR, use |
No IDR messages received |
IP/port mismatch, firewall blocking traffic, IDR not started |
Verify network configuration matches IDR setup. Check firewall rules. Confirm |
Socket bind failure |
Port already in use from a previous run or another process |
Use a different port, or wait for the OS to release the socket. On Windows, check for lingering connections with |
Ethernet message parsing errors |
Protocol generation mismatch between board and application, truncated message |
Verify both use Gen4 protocol. Check that the receive buffer is large enough ( |
IDR ID conflict |
Another IDR already registered with the same ID |
Call |
Board continues sending after application exits |
|
Always call |
Full Source
The complete source for this sample is provided below for reference. The sections above explain each part in detail.
Full Source — DIF_EtherInterrupts.c (SSK 1.x)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
/* Common Sample Program include files */
#include "include/naiapp_interrupt.h"
#include "include/naiapp_interrupt_ether.h"
#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_dif.h"
#include "naibrd_ether.h"
#include "advanced/nai_ether_adv.h"
#if defined (WIN32)
#include "maps/nai_map_dif.h"
#else
#include "nai_map_dif.h"
#endif
#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 = (int8_t *)"default_DIFEthInterrupts.txt";
/* Function prototypes */
static bool_t Run_DIF_EthInterrupt(int32_t cardIndex, int32_t module, uint32_t modid);
static bool_t QueryEthInterruptDIFIPAddr(void);
static nai_status_t InitDIFIDRCommands(int32_t cardIndex, int32_t module, uint8_t commands[], uint16_t *cmdcount, uint16_t *cmdlen,nai_intf_t boardInterface);
static void MakeDIFReadRegsCommand(bool_t bGen4Ether, uint16_t startIndex, uint32_t moduleOffset, uint8_t commands[], uint16_t *cmdlen,nai_intf_t boardInterface);
static void MakeDIFWriteRegsCommand(bool_t bGen4Ether, uint16_t startIndex, uint32_t moduleOffset, uint8_t commands[], uint16_t *cmdlen,nai_intf_t boardInterface);
static void CreateDIFIDRTCPServer(void);
static void CreateDIFIDRUDPServer(void);
static void DecodeUPRDIFIDRMessages(const uint8_t msg[], int32_t msgsize);
static bool_t ParseDIFIDRResponse(int32_t startIndex, int32_t rsparraysize, uint8_t response[], uint16_t *idrrsplen, int32_t idrrsparraysize, uint8_t idr_response[]);
static void DisplayDecodedDIFIDRResponse(uint16_t responselen, uint8_t response[]);
static bool_t bGen4DIFIDRCommands = FALSE;
/* Default Ethernet Command Count specified for the IDR Commands.
Change this to match the Command Count specified in InitDIFIDRCommands() routine
*/
#define DEF_DIF_IDR_ID 3
#define DEF_DIF_ETHER_CMD_COUNT 3 /* Number of Ethernet Commands in IDR Configuration */
#define DEF_DIF_RESPONSE_PROTOCOL ETHER_GEN4_TCP_PROTOCOL
#define DEF_DIF_RESPONSE_IP_LEN ETHER_GEN4_IPv4_ADDR_LEN
static uint16_t DEF_DIF_RESPONSE_PORT = 52801;
static uint8_t DEF_DIF_RESPONSE_IP_ADDR[] = {192,168,1,100};
#if defined (__VXWORKS__)
int UPR_DIFEthInt_Handler( int Param );
#elif LINUX
pthread_t interruptDIFThread;
static void* UPR_DIFEthInt_Handler(void* lpParam );
#else /* Default Windows */
DWORD WINAPI UPR_DIFEthInt_Handler( LPVOID lpParam );
#endif
static int terminateDIFThread;
/*****************************************************************************/
/**
<summary>
The purpose of the DIF_EtherInterrupts is to demonstrate the methods to call in the
naibrd library to perform Ethernet interrupt operations with the Differential modules.
The following system configuration routines from the nai_sys_cfg.c file are
called to assist with the configuration setup for this program prior to
calling the naibrd AD routines.
- ConfigDevice
- DisplayDeviceCfg
- GetBoardSNModCfg
- CheckModule
</summary>
*/
/*****************************************************************************/
#if defined (__VXWORKS__)
int32_t DIF_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_DIF_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;
}
/**************************************************************************************************************/
/**
<summary>
This function runs handles configuring a DIF channel to receive Differential BIT, Low-to-High and
High-to-Low Ethernet Interrupt commands. This routine will create a thread to wait to
receive the unprompted interrupt ethernet messages.
</summary>
*/
/**************************************************************************************************************/
static bool_t Run_DIF_EthInterrupt(int32_t cardIndex, int32_t module, uint32_t modid)
{
bool_t bQuit = FALSE;
nai_status_t status;
int32_t MAX_CHANNELS = naibrd_DIF_GetChannelCount(modid);
int32_t channel;
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_DIF_RESPONSE_PROTOCOL;
uint16_t iplen = DEF_DIF_RESPONSE_IP_LEN;
uint16_t port;
uint8_t ip[DEF_DIF_RESPONSE_IP_LEN];
int32_t i;
nai_intf_t boardInterface=0;
naibrd_int_steering_t steering=0;
bool_t Query_steering= FALSE;
int32_t IDRcardIndex=0;
int8_t inputBuffer[80];
int32_t inputResponseCnt;
/* Determine if the board selected supports the Generation 4 Ethernet Commands */
bGen4DIFIDRCommands = SupportsGen4Ether(cardIndex);
/* Get the Differential channel to configure for Ethernet interrupts */
printf("Differential Channel Selection:");
bQuit = naiapp_query_ChannelNumber(MAX_CHANNELS, 1, &channel);
if (!bQuit)
bQuit = QueryEthInterruptDIFIPAddr();
if(!bQuit){
bQuit = QueryUserForOnboardOffboardInterrupts(&Query_steering);
}
if(Query_steering == TRUE)
{
IDRcardIndex = 0;
boardInterface = NAI_INTF_ONBOARD;
steering = NAIBRD_INT_STEERING_ON_BOARD_1;
}
else /*OffBoard Interrupt*/
{
IDRcardIndex = 0;
boardInterface = NAI_INTF_PCI;
steering = NAIBRD_INT_STEERING_CPCI_APP;
}
if (!bQuit)
{
if (bGen4DIFIDRCommands)
{
/* Make sure there are no pending interrupts before interrupts are enabled */
/* Clear NAI_DIF_STATUS_BIT_LATCHED, NAI_DIF_STATUS_LO_HI_TRANS_LATCHED
and NAI_DIF_STATUS_HI_LO_TRANS_LATCHED channel status */
check_status(naibrd_DIF_ClearStatus(cardIndex, module, channel, NAI_DIF_STATUS_BIT_LATCHED));
check_status(naibrd_DIF_ClearStatus(cardIndex, module, channel, NAI_DIF_STATUS_LO_HI_TRANS_LATCHED));
check_status(naibrd_DIF_ClearStatus(cardIndex, module, channel, NAI_DIF_STATUS_HI_LO_TRANS_LATCHED));
/* Configure the Differential channel to Input */
check_status(naibrd_DIF_SetOpMode(cardIndex, module, channel, NAI_DIF_MODE_STD_INPUT_OUTPUT));
check_status(naibrd_DIF_SetIOFormat(cardIndex, module, channel, NAI_DIF_IOFORMAT_INPUT));
/* Configure the Interrupt Driven Response Ethernet Command */
status = check_status(naibrd_Ether_ClearIDRConfig(IDRcardIndex,(uint16_t)DEF_DIF_IDR_ID));
InitDIFIDRCommands(IDRcardIndex, module, commands, &cmdcount, &cmdlength,boardInterface);
for (i = 0; i < DEF_DIF_RESPONSE_IP_LEN; i++)
ip[i] = DEF_DIF_RESPONSE_IP_ADDR[i];
port = DEF_DIF_RESPONSE_PORT;
status = check_status(naibrd_Ether_SetIDRConfig(IDRcardIndex,(uint16_t)DEF_DIF_IDR_ID,protocol,iplen,ip,port,NAI_DIF_INTERRUPT_VECTOR,cmdcount,cmdlength,commands));
if (status == NAI_SUCCESS)
{
printf("IDR ID = %d configured.\n", DEF_DIF_IDR_ID);
}
/* Start the IDR */
status = check_status(naibrd_Ether_StartIDR(IDRcardIndex, (uint16_t)DEF_DIF_IDR_ID));
if (status == NAI_SUCCESS)
{
printf("IDR ID = %d Started\n", DEF_DIF_IDR_ID);
}
/* Configure to generate interrupts on NAI_DIF_STATUS_BIT_LATCHED, NAI_DIF_STATUS_LO_HI_TRANS_LATCHED and
NAI_DIF_STATUS_HI_LO_TRANS_LATCHED */
/* Set the Interrupt Type (Edge/Level) */
check_status(naibrd_DIF_SetEdgeLevelInterrupt(cardIndex, module, channel, NAI_DIF_STATUS_BIT_LATCHED, NAI_DIF_LEVEL_INTERRUPT));
check_status(naibrd_DIF_SetEdgeLevelInterrupt(cardIndex, module, channel, NAI_DIF_STATUS_LO_HI_TRANS_LATCHED, NAI_DIF_LEVEL_INTERRUPT));
check_status(naibrd_DIF_SetEdgeLevelInterrupt(cardIndex, module, channel, NAI_DIF_STATUS_HI_LO_TRANS_LATCHED, NAI_DIF_LEVEL_INTERRUPT));
/* Set the Interrupt Vectors for BIT, Lo-Hi and Hi-Lo Transition */
/* Note, set to same vector since one IDR will handle all the interrupts */
check_status(naibrd_DIF_SetGroupInterruptVector(cardIndex, module, 1, NAI_DIF_STATUS_BIT_LATCHED, NAI_DIF_INTERRUPT_VECTOR));
check_status(naibrd_DIF_SetGroupInterruptVector(cardIndex, module, 1, NAI_DIF_STATUS_LO_HI_TRANS_LATCHED, NAI_DIF_INTERRUPT_VECTOR));
check_status(naibrd_DIF_SetGroupInterruptVector(cardIndex, module, 1, NAI_DIF_STATUS_HI_LO_TRANS_LATCHED, NAI_DIF_INTERRUPT_VECTOR));
/* Set the Interrupt Steering */
check_status(naibrd_DIF_SetGroupInterruptSteering(cardIndex, module, 1, NAI_DIF_STATUS_BIT_LATCHED, steering));
check_status(naibrd_DIF_SetGroupInterruptSteering(cardIndex, module, 1, NAI_DIF_STATUS_LO_HI_TRANS_LATCHED, steering));
check_status(naibrd_DIF_SetGroupInterruptSteering(cardIndex, module, 1, NAI_DIF_STATUS_HI_LO_TRANS_LATCHED, steering));
/* Enable BIT, Lo-Hi and Hi-Lo Transition Interrupts */
check_status(naibrd_DIF_SetInterruptEnable(cardIndex, module, channel, NAI_DIF_STATUS_BIT_LATCHED, TRUE));
check_status(naibrd_DIF_SetInterruptEnable(cardIndex, module, channel, NAI_DIF_STATUS_LO_HI_TRANS_LATCHED, TRUE));
check_status(naibrd_DIF_SetInterruptEnable(cardIndex, module, channel, NAI_DIF_STATUS_HI_LO_TRANS_LATCHED, TRUE));
/* Create a thread that will receive Unprompted Reply interrupt messages */
terminateDIFThread = FALSE;
#if defined (__VXWORKS__)
taskSpawn("uprDIFHandler", 100, 0, 10000, (FUNCPTR)UPR_DIFEthInt_Handler, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0 );
#elif defined (LINUX)
pthread_create(&interruptDIFThread, NULL, (void*)UPR_DIFEthInt_Handler, NULL);
#else /* Default Windows */
CreateThread( NULL, 0, UPR_DIFEthInt_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)
{
terminateDIFThread = TRUE;
/* Stop the IDR */
status = check_status(naibrd_Ether_StopIDR(cardIndex, (uint16_t)DEF_DIF_IDR_ID));
if (status == NAI_SUCCESS)
{
printf("IDR ID = %d Stopped\n", DEF_DIF_IDR_ID);
}
}
}
}
else
{
printf("DIF Ethernet Interrupt Support Prior to Generation 4 Ethernet commands currently not supported\n");
bQuit = TRUE;
}
}
return bQuit;
}
/**************************************************************************************************************/
/**
<summary>
This function queries the user to enter the IP address associated with receiving DIF Interrupt commands.
</summary>
*/
/**************************************************************************************************************/
static bool_t QueryEthInterruptDIFIPAddr(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_DIF_RESPONSE_IP_ADDR[0],DEF_DIF_RESPONSE_IP_ADDR[1],DEF_DIF_RESPONSE_IP_ADDR[2],DEF_DIF_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_DIF_RESPONSE_IP_ADDR);
}
}
if (!bQuit)
{
/* Get the Response Port */
printf("Please Enter IDR Response Port: [default=%d]): ", DEF_DIF_RESPONSE_PORT);
bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
if (bQuit)
bContinue = FALSE;
else
{
if (inputResponseCnt > 0)
DEF_DIF_RESPONSE_PORT = (uint16_t)atol((const char *)inputBuffer);
bContinue = FALSE;
}
}
}
}
return bQuit;
}
/**************************************************************************************************************/
/**
<summary>
This function configures the IDR (Interrupt Driven Response) commands when a DIF interrupt occurs.
There are two Ethernet commands that will be processed by the board when a DIF interrupt occurs. This
routine calls the MakeDIFReadRegsCommand() and MakeDIFWriteRegsCommand() to configure the two
Ethernet commands to include when setting the IDR.
</summary>
*/
/**************************************************************************************************************/
static nai_status_t InitDIFIDRCommands(int32_t cardIndex, int32_t module, uint8_t commands[], uint16_t *cmdcount, uint16_t *cmdlen,nai_intf_t boardInterface)
{
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 (bGen4DIFIDRCommands)
{
/* First command */
MakeDIFReadRegsCommand(bGen4DIFIDRCommands, msgIndex, moduleOffset, commands, ðcmdlen,boardInterface);
msgIndex += ethcmdlen;
idrcmdlen += ethcmdlen;
idrcmdcnt++;
/* Next command */
MakeDIFWriteRegsCommand(bGen4DIFIDRCommands, msgIndex, moduleOffset, commands, ðcmdlen,boardInterface);
msgIndex += ethcmdlen;
idrcmdlen += ethcmdlen;
idrcmdcnt++;
/* Next command */
MakeDIFReadRegsCommand(bGen4DIFIDRCommands, msgIndex, moduleOffset, commands, ðcmdlen,boardInterface);
msgIndex += ethcmdlen;
idrcmdlen += ethcmdlen;
idrcmdcnt++;
*cmdcount = idrcmdcnt;
*cmdlen = msgIndex;
}
else
{
printf("DIF Ethernet Interrupt Support Prior to Generation 4 Ethernet commands currently not supported\n");
status = NAI_ERROR_NOT_SUPPORTED;
}
}
return status;
}
/**************************************************************************************************************/
/**
<summary>
This function formats the buffer for the Ethernet Read Registers command.
</summary>
*/
/**************************************************************************************************************/
static void MakeDIFReadRegsCommand(bool_t bGen4Ether, uint16_t startIndex, uint32_t moduleOffset, uint8_t commands[], uint16_t *cmdlen,nai_intf_t boardInterface)
{
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_DIF_GEN5_REG_BIT_LATCHED_STATUS_ADD (to read the BIT, Lo-Hi, Hi-Lo and Overcurrent statuses)
Count: 4, Stride 0x10
*/
seqno = 0;
regaddr = moduleOffset + NAI_DIF_GEN5_REG_BIT_LATCHED_STATUS_ADD;
count = 4;
stride = 0x10;
msgIndex = (uint16_t)nai_ether_MakeReadMessage(&commands[startIndex],seqno,NAI_ETHER_GEN4,boardInterface,regaddr,stride,count,NAI_REG32);
*cmdlen = msgIndex;
}
}
/**************************************************************************************************************/
/**
<summary>
This function formats the buffer for the Ethernet Write Registers command.
</summary>
*/
/**************************************************************************************************************/
static void MakeDIFWriteRegsCommand(bool_t bGen4Ether, uint16_t startIndex, uint32_t moduleOffset, uint8_t commands[], uint16_t *cmdlen,nai_intf_t boardInterface)
{
uint16_t msgIndex = startIndex;
uint16_t seqno;
uint32_t count, stride;
uint32_t regaddr;
uint32_t data[4] = {0x00FFFFFF, 0x00FFFFFF, 0x00FFFFFF, 0x00FFFFFF}; /* Clear all interrupt bits */
if (bGen4Ether)
{
/* Create a WriteRegs command with the following attributes:
Onboard Access, 32-bit Register Size,
Reg Address: NAI_DIF_GEN5_REG_BIT_LATCHED_STATUS_ADD (to read the BIT, Lo-Hi, Hi-Lo and Overcurrent statuses)
Count: 4, Stride 0x10
Data: 0xFFFFFFFF (clear all interrupts)
*/
seqno = 0;
regaddr = moduleOffset + NAI_DIF_GEN5_REG_BIT_LATCHED_STATUS_ADD;
count = 4;
stride = 0x10;
msgIndex = (uint16_t)nai_ether_BeginWriteMessage(&commands[startIndex],seqno,NAI_ETHER_GEN4,boardInterface,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;
}
}
/**************************************************************************************************************/
/**
<summary>
This thread calls CreateDIFIDRTCPServer or CreateDIFIDRUDPServer depending on the
DEF_SER_RESPONSE_PROTOCOL to receive the Interrupt Driven Response (IDR) Unprompted Ethernet Replies.
</summary>
*/
/**************************************************************************************************************/
#if defined (__VXWORKS__)
int UPR_DIFEthInt_Handler( int32_t nParam )
{
#elif LINUX
static void* UPR_DIFEthInt_Handler(void *lpParam )
{
#else /* Default Windows */
DWORD WINAPI UPR_DIFEthInt_Handler( LPVOID lpParam )
{
#endif
uint16_t protocol = DEF_DIF_RESPONSE_PROTOCOL;
#if defined (WIN32)
UNREFERENCED_PARAMETER(lpParam);
#endif
printf("\nUnprompted Reply Thread Started....\n");
if (protocol == ETHER_GEN4_TCP_PROTOCOL)
CreateDIFIDRTCPServer();
else
CreateDIFIDRUDPServer();
printf("\nUnprompted Reply Thread Terminated\n");
return 0;
}
/**************************************************************************************************************/
/**
<summary>
This function creates a TCP Listener socket to receive the Interrupt Driven Response (IDR) Unprompted Ethernet Replies
and waits for the Ethernet messages.
</summary>
*/
/**************************************************************************************************************/
static void CreateDIFIDRTCPServer(void)
{
nai_socket_t listensock = INVALID_SOCKET, clientsock = INVALID_SOCKET;
struct addrinfo hints;
struct addrinfo *info;
char_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_DIF_RESPONSE_PORT);
#if defined (WIN32)
result = getaddrinfo(NULL, (PCSTR)portbuf, &hints, &info);
#elif defined(LINUX) || defined(__VXWORKS__)
result = getaddrinfo(NULL, 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);
DecodeUPRDIFIDRMessages(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) && (terminateDIFThread == 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
}
/**************************************************************************************************************/
/**
<summary>
This function creates a UDP Listener socket to receive the Interrupt Driven Response (IDR) Unprompted Ethernet Replies
and waits for the Ethernet messages.
</summary>
*/
/**************************************************************************************************************/
static void CreateDIFIDRUDPServer()
{
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
socklen_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_DIF_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 (terminateDIFThread == 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, &sendaddrsize);
#endif
if ( result > 0 )
{
printf("\n\nUDP Server: Bytes received: %d\n", result);
DecodeUPRDIFIDRMessages(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;
}
/**************************************************************************************************************/
/**
<summary>
This function calls ParseDIFIDRResponse() to break down the IDR message received to individual Ethernet messages
and calls DisplayDecodedDIFIDRResponse() to display the data in the Ethernet message.
</summary>
*/
/**************************************************************************************************************/
static void DecodeUPRDIFIDRMessages(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 (ParseDIFIDRResponse(startIndex, arraysize, (uint8_t *)msg, &responselen, MAX_ETHER_BLOCK_REG_CNT, response))
{
startIndex += responselen;
DisplayDecodedDIFIDRResponse(responselen, response);
}
}
}
/**************************************************************************************************************/
/**
<summary>
This function break downs the IDR message received to individual Ethernet messages.
</summary>
*/
/**************************************************************************************************************/
static bool_t ParseDIFIDRResponse(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 (bGen4DIFIDRCommands)
{
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("DIF Ethernet Interrupt Support Prior to Generation 4 Ethernet commands currently not supported\n");
}
return bParsed;
}
/**************************************************************************************************************/
/**
<summary>
This function displays the data in the Ethernet message.
</summary>
*/
/**************************************************************************************************************/
static void DisplayDecodedDIFIDRResponse(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 (bGen4DIFIDRCommands)
gen = NAI_ETHER_GEN4;
else
gen = NAI_ETHER_GEN3;
offset = nai_ether_DecodeMessageHeader(response, responselen, &seq, &tc, gen, &size);
printf("Size=%d ", size);
/* Parse IDR Response Sequence Value */
seqrsptype = seq & ETHER_GEN4_SEQ_UPR_MASK;
seqrspidindex = (seq & (ETHER_GEN4_SEQ_ID_MASK)) >> ETHER_GEN4_SEQ_ID_SHIFT;
seqcmdindex = (seq & (ETHER_GEN4_SEQ_CMD_MASK)) >> ETHER_GEN4_SEQ_CMD_SHIFT;
printf("seq:0x%04X (", seq);
if (seqrsptype == ETHER_GEN4_SEQ_UPR_TDR)
printf("Type=Unprompted TDR ");
else if (seqrsptype == ETHER_GEN4_SEQ_UPR_IDR)
printf("Type=Unprompted IDR ");
else
printf("Type=UNKNOWN(0x%X) ", seqrsptype);
printf("ID=%d Cmd=%d) ", seqrspidindex+1, seqcmdindex+1);
switch (tc)
{
case NAI_ETHER_TYPECODE_RSP_COMMAND_COMPLETE_READ_4:
printf("Typecode: Read Regs (0x%4X)\n", tc);
datacnt = (responselen - 10)/NAI_REG32;
printf(" Data (Count=%d): ", datacnt);
for (i = 0; i < datacnt; i++)
{
if (i != 0)
printf(",");
data = 0;
data = response[offset++] << 24;
data |= response[offset++] << 16;
data |= response[offset++] << 8;
data |= response[offset++];
printf("0x%08X",data);
}
printf("\n");
break;
case NAI_ETHER_TYPECODE_RSP_COMMAND_COMPLETE_WRITE_4:
printf("Typecode: Write Regs (0x%4X)\n", tc);
break;
case NAI_ETHER_TYPECODE_RSP_COMMAND_COMPLETE_READ_BLOCK_4:
printf("Typecode: Read Block (0x%4X)\n", tc);
datacnt = (responselen - 10)/NAI_REG32;
printf(" Data (Count=%d): ", datacnt);
for (i = 0; i < datacnt; i++)
{
if (i != 0)
printf(",");
data = 0;
data = response[offset++] << 24;
data |= response[offset++] << 16;
data |= response[offset++] << 8;
data |= response[offset++];
printf("0x%08X",data);
}
printf("\n");
break;
case NAI_ETHER_TYPECODE_RSP_COMMAND_COMPLETE_WRITE_BLOCK_4:
printf("Typecode: Write Block (0x%4X)\n", tc);
break;
case NAI_ETHER_TYPECODE_RSP_ERROR_BLOCK_NOT_CONFIG_4:
printf("Typecode: Block Error (Not Configured) (0x%4X)\n", tc);
break;
default:
printf("Typecode: Unknown (0x%4X)\n", tc);
break;
}
}