cb243c36a8
* Change MIT license texts to SPDX-License-Identifier SPDX-License-Identifier is much easier to understand and grep than license text so use that instead. * Change GPL exception license texts to SPDX-License-Identifier SPDX-License-Identifier is much easier to understand and grep than license text so use that instead. * Change misc license texts to SPDX-License-Identifier There are some external code in repo which are not licenses as most of the stuff in this repo. We still want every file to have SPDX identifier to easily grep licenses. * Add currently used license files Even though Bacnet-Stack is using SPDX identifiers we still need to give those license files with source. For this reason add all license files to license/ folder. SPDX has also files which would make same thing but this is style which example Linux kernel is using and it is quite clear so I choose that one for now. I choosed not yet bring CC-PDDC as that is not right license for those files. --------- Co-authored-by: Kari Argillander <kari.argillander@fidelix.com>
301 lines
8.3 KiB
C
301 lines
8.3 KiB
C
/**************************************************************************
|
|
*
|
|
* Copyright (C) 2008 Steve Karg
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0
|
|
*
|
|
*********************************************************************/
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#/* BACnet Stack defines - first */
|
|
#include "bacnet/bacdef.h"
|
|
/* BACnet Stack API */
|
|
#include "bacnet/basic/sys/mstimer.h"
|
|
#include "bacnet/basic/sys/bytes.h"
|
|
#include "bacnet/datalink/crc.h"
|
|
#include "bacnet/datalink/mstp.h"
|
|
#include "bacnet/datalink/dlmstp.h"
|
|
#include "bacnet/datalink/mstptext.h"
|
|
#include "bacnet/bacint.h"
|
|
/* OS specific include*/
|
|
#include "bacport.h"
|
|
/* local includes */
|
|
#include "rs485.h"
|
|
|
|
/** @file linux/mstpsnap.c Example application testing BACnet MS/TP on Linux.
|
|
*/
|
|
|
|
#ifndef max
|
|
#define max(a, b) (((a)(b)) ? (a) : (b))
|
|
#define min(a, b) (((a) < (b)) ? (a) : (b))
|
|
#endif
|
|
|
|
/* local port data - shared with RS-485 */
|
|
static struct mstp_port_struct_t MSTP_Port;
|
|
/* buffers needed by mstp port struct */
|
|
static uint8_t RxBuffer[DLMSTP_MPDU_MAX];
|
|
static uint8_t TxBuffer[DLMSTP_MPDU_MAX];
|
|
static struct mstimer Silence_Timer;
|
|
|
|
static uint32_t Timer_Silence(void *pArg)
|
|
{
|
|
uint32_t delta_time = 0;
|
|
|
|
delta_time = mstimer_elapsed(&Silence_Timer);
|
|
if (delta_time > 0xFFFF) {
|
|
delta_time = 0xFFFF;
|
|
}
|
|
|
|
return delta_time;
|
|
}
|
|
|
|
static void Timer_Silence_Reset(void *pArg)
|
|
{
|
|
mstimer_set(&Silence_Timer, 0);
|
|
}
|
|
|
|
/* functions used by the MS/TP state machine to put or get data */
|
|
uint16_t MSTP_Put_Receive(struct mstp_port_struct_t *mstp_port)
|
|
{
|
|
(void)mstp_port;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* for the MS/TP state machine to use for getting data to send */
|
|
/* Return: amount of PDU data */
|
|
uint16_t MSTP_Get_Send(
|
|
struct mstp_port_struct_t *mstp_port, unsigned timeout)
|
|
{ /* milliseconds to wait for a packet */
|
|
(void)mstp_port;
|
|
(void)timeout;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Send an MSTP frame
|
|
* @param mstp_port - port specific data
|
|
* @param buffer - data to send
|
|
* @param nbytes - number of bytes of data to send
|
|
*/
|
|
void MSTP_Send_Frame(
|
|
struct mstp_port_struct_t *mstp_port,
|
|
uint8_t * buffer,
|
|
uint16_t nbytes)
|
|
{
|
|
(void)mstp_port;
|
|
(void)buffer;
|
|
(void)nbytes;
|
|
}
|
|
|
|
uint16_t MSTP_Get_Reply(
|
|
struct mstp_port_struct_t *mstp_port, unsigned timeout)
|
|
{ /* milliseconds to wait for a packet */
|
|
(void)mstp_port;
|
|
(void)timeout;
|
|
return 0;
|
|
}
|
|
|
|
static int network_init(const char *name, int protocol)
|
|
{
|
|
/* check to see if we are being run as root */
|
|
if (getuid() != 0) {
|
|
fprintf(stderr, "Requires root priveleges.\n");
|
|
return -1;
|
|
}
|
|
|
|
int sockfd = socket(PF_PACKET, SOCK_RAW, htons(protocol));
|
|
|
|
if (sockfd == -1) {
|
|
perror("Unable to create socket");
|
|
return sockfd;
|
|
}
|
|
|
|
struct ifreq ifr;
|
|
|
|
memset(&ifr, 0, sizeof(ifr));
|
|
strncpy(ifr.ifr_name, name, strlen(name));
|
|
|
|
if (ioctl(sockfd, SIOCGIFINDEX, &ifr) == -1) {
|
|
perror("Unable to get interface index");
|
|
return -1;
|
|
}
|
|
|
|
struct sockaddr_ll sll;
|
|
|
|
memset(&sll, 0, sizeof(sll));
|
|
sll.sll_family = AF_PACKET;
|
|
sll.sll_ifindex = ifr.ifr_ifindex;
|
|
sll.sll_protocol = htons(protocol);
|
|
|
|
if (bind(sockfd, (struct sockaddr *)&sll, sizeof(sll)) == -1) {
|
|
perror("Unable to bind socket");
|
|
return -1;
|
|
}
|
|
|
|
return sockfd;
|
|
}
|
|
|
|
static void snap_received_packet(
|
|
struct mstp_port_struct_t *mstp_port, int sockfd)
|
|
{
|
|
uint16_t mtu_len = 0; /* number of octets of packet saved in file */
|
|
unsigned i = 0; /* counter */
|
|
static uint8_t mtu[1500] = { 0 };
|
|
uint16_t max_data = 0;
|
|
|
|
mtu[0] = 0;
|
|
mtu[1] = 0;
|
|
mtu[2] = 0;
|
|
mtu[3] = 0;
|
|
mtu[4] = 0;
|
|
mtu[5] = mstp_port->DestinationAddress;
|
|
mtu[6] = 0;
|
|
mtu[7] = 0;
|
|
mtu[8] = 0;
|
|
mtu[9] = 0;
|
|
mtu[10] = 0;
|
|
mtu[11] = mstp_port->SourceAddress;
|
|
/* length - 12, 13 */
|
|
mtu[14] = 0xaa; /* DSAP for SNAP */
|
|
mtu[15] = 0xaa; /* SSAP for SNAP */
|
|
mtu[16] = 0x03; /* Control Field for SNAP */
|
|
mtu[17] = 0x00; /* Organization Code: Cimetrics */
|
|
mtu[18] = 0x10; /* Organization Code: Cimetrics */
|
|
mtu[19] = 0x90; /* Organization Code: Cimetrics */
|
|
mtu[20] = 0x00; /* Protocol ID */
|
|
mtu[21] = 0x01; /* Protocol ID */
|
|
mtu[22] = 0x00; /* delta time */
|
|
mtu[23] = 0x00; /* delta time */
|
|
mtu[24] = 0x80; /* unknown byte */
|
|
mtu[25] = mstp_port->FrameType;
|
|
mtu[26] = mstp_port->DestinationAddress;
|
|
mtu[27] = mstp_port->SourceAddress;
|
|
mtu[28] = HI_BYTE(mstp_port->DataLength);
|
|
mtu[29] = LO_BYTE(mstp_port->DataLength);
|
|
mtu[30] = mstp_port->HeaderCRCActual;
|
|
mtu_len = 31;
|
|
if (mstp_port->DataLength) {
|
|
max_data = min(mstp_port->InputBufferSize, mstp_port->DataLength);
|
|
for (i = 0; i < max_data; i++) {
|
|
mtu[31 + i] = mstp_port->InputBuffer[i];
|
|
}
|
|
mtu[31 + max_data] = mstp_port->DataCRCActualMSB;
|
|
mtu[31 + max_data + 1] = mstp_port->DataCRCActualLSB;
|
|
mtu_len += (max_data + 2);
|
|
}
|
|
/* Ethernet length is data only - not address or length bytes */
|
|
encode_unsigned16(&mtu[12], mtu_len - 14);
|
|
(void)write(sockfd, &mtu[0], mtu_len);
|
|
}
|
|
|
|
static void cleanup(void)
|
|
{
|
|
}
|
|
|
|
#if (!defined(_WIN32))
|
|
static void sig_int(int signo)
|
|
{
|
|
(void)signo;
|
|
|
|
cleanup();
|
|
exit(0);
|
|
}
|
|
|
|
void signal_init(void)
|
|
{
|
|
signal(SIGINT, sig_int);
|
|
signal(SIGHUP, sig_int);
|
|
signal(SIGTERM, sig_int);
|
|
}
|
|
#endif
|
|
|
|
/* simple test to packetize the data and print it */
|
|
int main(int argc, char *argv[])
|
|
{
|
|
struct mstp_port_struct_t *mstp_port;
|
|
long my_baud = 38400;
|
|
uint32_t packet_count = 0;
|
|
int sockfd = -1;
|
|
char *my_interface = "eth0";
|
|
|
|
/* mimic our pointer in the state machine */
|
|
mstp_port = &MSTP_Port;
|
|
if ((argc > 1) && (strcmp(argv[1], "--help") == 0)) {
|
|
printf("mstsnap [serial] [baud] [network]\r\n"
|
|
"Captures MS/TP packets from a serial interface\r\n"
|
|
"and sends them to a network interface using SNAP \r\n"
|
|
"protocol packets (mimics Cimetrics U+4 packet).\r\n"
|
|
"\r\n"
|
|
"Command line options:\r\n"
|
|
"[serial] - serial interface.\r\n"
|
|
" defaults to /dev/ttyUSB0.\r\n"
|
|
"[baud] - baud rate. 9600, 19200, 38400, 57600, 115200\r\n"
|
|
" defaults to 38400.\r\n"
|
|
"[network] - network interface.\r\n"
|
|
" defaults to eth0.\r\n"
|
|
"");
|
|
return 0;
|
|
}
|
|
/* initialize our interface */
|
|
if (argc > 1) {
|
|
RS485_Set_Interface(argv[1]);
|
|
}
|
|
if (argc > 2) {
|
|
my_baud = strtol(argv[2], NULL, 0);
|
|
}
|
|
if (argc > 3) {
|
|
my_interface = argv[3];
|
|
}
|
|
sockfd = network_init(my_interface, ETH_P_ALL);
|
|
if (sockfd == -1) {
|
|
return 1;
|
|
}
|
|
RS485_Set_Baud_Rate(my_baud);
|
|
RS485_Initialize();
|
|
MSTP_Port.InputBuffer = &RxBuffer[0];
|
|
MSTP_Port.InputBufferSize = sizeof(RxBuffer);
|
|
MSTP_Port.OutputBuffer = &TxBuffer[0];
|
|
MSTP_Port.OutputBufferSize = sizeof(TxBuffer);
|
|
MSTP_Port.This_Station = 127;
|
|
MSTP_Port.Nmax_info_frames = 1;
|
|
MSTP_Port.Nmax_master = 127;
|
|
MSTP_Port.SilenceTimer = Timer_Silence;
|
|
MSTP_Port.SilenceTimerReset = Timer_Silence_Reset;
|
|
MSTP_Init(mstp_port);
|
|
fprintf(stdout, "mstpcap: Using %s for capture at %ld bps.\n",
|
|
RS485_Interface(), (long)RS485_Get_Baud_Rate());
|
|
atexit(cleanup);
|
|
#if defined(_WIN32)
|
|
SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), ENABLE_PROCESSED_INPUT);
|
|
SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlCHandler, TRUE);
|
|
#else
|
|
signal_init();
|
|
#endif
|
|
/* run forever */
|
|
for (;;) {
|
|
RS485_Check_UART_Data(mstp_port);
|
|
MSTP_Receive_Frame_FSM(mstp_port);
|
|
/* process the data portion of the frame */
|
|
if (mstp_port->ReceivedValidFrame) {
|
|
mstp_port->ReceivedValidFrame = false;
|
|
snap_received_packet(mstp_port, sockfd);
|
|
packet_count++;
|
|
} else if (mstp_port->ReceivedInvalidFrame) {
|
|
mstp_port->ReceivedInvalidFrame = false;
|
|
fprintf(stderr, "ReceivedInvalidFrame\n");
|
|
snap_received_packet(mstp_port, sockfd);
|
|
packet_count++;
|
|
}
|
|
if (!(packet_count % 100)) {
|
|
fprintf(stdout, "\r%hu packets", packet_count);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|