Released V1.0 Pi Code

This commit is contained in:
adrcs 2025-03-28 13:01:50 -06:00 committed by GitHub
parent d3c660acee
commit abc0abeeb5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
27 changed files with 1861 additions and 0 deletions

7
Raspberry Pi/Read.Me Normal file
View file

@ -0,0 +1,7 @@
This code runs on the raspberry Pi.
IP400Spi is an SPI host for the node SPI. It implements an SPI master to exchange data with the
node, and both sends and receives ethernet packets using UDP. It can be used as a code base
for implementing other applications.
Details can be found in the documentation directory.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,59 @@
/*---------------------------------------------------------------------------
Project: WL33_NUCLEO_UART
Module: <module description here>
File Name: queue.h
Date Created: Jan 9, 2025
Author: MartinA
Description: <what it does>
Copyright © 2024-25, Alberta Digital Radio Communications Society,
All rights reserved
Revision History:
---------------------------------------------------------------------------*/
#ifndef INC_DATAQ_H_
#define INC_DATAQ_H_
#ifndef __CCALL
#ifdef __cplusplus
#define __CCALL extern "C"
#else
#define __CCALL
#endif
#endif
#define Q_TYPECAST (struct qelem *)
// queue structureBOOL checkQueue(FRAME_QUEUE *que)
typedef struct qelem {
struct qelem *q_forw;
struct qelem *q_back;
} QUEUE_ELEM;
// frame data queue
typedef struct frame_data_queue_t {
struct frame_data_queue_t *q_forw; // forwared pointer
struct frame_data_queue_t *q_back; // backward pointer
void *data; // Frame data
uint16_t length; // data length
} FRAME_QUEUE;
// queue functions
BOOL enqueFrame(FRAME_QUEUE *que, void *fr);
void *dequeFrame(FRAME_QUEUE *que);
BOOL checkQueue(FRAME_QUEUE *que);
// queue management
void insque (struct qelem *elem, struct qelem *pred);
void remque (struct qelem *elem);
#endif /* INC_DATAQ_H_ */

View file

@ -0,0 +1,206 @@
/*---------------------------------------------------------------------------
Project: WL33_NUCLEO_UART
Module: <module description here>
File Name: frame.h
Date Created: Jan 8, 2025
Author: MartinA
Description: <what it does>
Copyright © 2024-25, Alberta Digital Radio Communications Society,
All rights reserved
Revision History:
---------------------------------------------------------------------------*/
#ifndef FRAME_H_
#define FRAME_H_
#include <stdint.h>
#include "types.h"
// frame defines
#define N_CALL 4 // octets in the excess-40 compressed callsign
#define MAX_CALL 6 // max callsign size
#define EXT_CALL 4*MAX_CALL // extended call sign field
#define PAYLOAD_MIN 56 // min octets in payload
#define PAYLOAD_MAX 1053 // max octets in payload
#define MAX_CALL_BUFFER 20 // max buffering for callsign in payload
#define N_FEC 4 // number of bytes in the FEC
#define BROADCAST_ADDR 0xFF // broadcast address
#define MAX_HOP_COUNT 15 // max hop count
// transmit states
enum {
TX_IDLE=0, // idle - waiting for work
TX_SENDING, // sending a frame
TX_DONE // done
};
// radio error register
#define SEQ_COMPLETE_ERR 0x8000 // Sequencer error
#define SEQ_ACT_TIMEOUT 0x4000 // Sequencer action timeout
#define PLL_CALAMP_ERR 0x0800 // VCO amplitude calibration error
#define PLL_CALFREQ_ERR 0x0400 // VCO frequency calibration error
#define PLL_UNLOCK_ERR 0x0200 // PLL is unlocked
#define PLL_LOCK_FAIL 0x0100 // PLL lock failure
#define DBM_FIFO_ERR 0x0020 // Data buffer failure
#define N_RADIO_ERRS 7 // number of the above
// radio FSM states
enum fsm_states_e {
FSM_IDLE=0, // idle
FSM_ENA_RF_REG, // enable RF registers
FSM_WAIT_ACTIVE2, // wait for active 2
FSM_ACTIVE2, // active 2
FSM_ENA_CURR, // enable current
FSM_SYNTH_SETUP, // synth setup
FSM_CALIB_VCO, // VCO calibration
FSM_LOCKRXTX, // lock Rx and Rx
FSM_LOCKONTX, // lock on Rx
FSM_EN_PA, // enable PA
FSM_TX, // transmit
FSM_PA_DWN_ANA, // Analog power down
FSM_END_TX, // end transmit
FSM_LOCKONRX, // lock on Rx
FSM_EN_RX, // Enable Rx
FSM_EN_LNA, // enable LNA
FSM_RX, // recieve
FSM_END_RX, // end rx
FSM_SYNTH_PWDN, // synth power down
FSM_N_FSM_STATES
};
// header flags
typedef struct frame_flags_t {
unsigned hop_count: 4; // hop count
unsigned coding: 4; // coding method
unsigned compression: 2; // compression method
unsigned unused: 1; // unused
unsigned srcExt: 1; // source call is extended
unsigned destExt: 1; // dest call is extended
unsigned command: 1; // command packet
unsigned noconnect: 1; // connectionless
unsigned repeat: 1; // repeat flag
} IP400_FLAGS;
// callsign field
typedef struct callsign_t {
union {
uint8_t bytes[N_CALL]; // compressed callsign
uint32_t encoded;
} callbytes;
uint16_t port; // port information
} IP400_CALL;
// complete frame
typedef struct ip400_frame_t {
IP400_CALL source; // source call sign
IP400_CALL dest; // destination call sign
union {
IP400_FLAGS flags; // flag bit field
uint16_t allflags; // all flags
} flagfld;
uint32_t seqNum; // packet sequence number
void *buf; // data to send
uint16_t length; // data length
} IP400_FRAME;
// min/max payload sizes
#define IP_400_PORT_SIZE sizeof(uint16_t)
#define IP_400_CALL_SIZE (N_CALL + IP_400_PORT_SIZE)
#define IP_400_FLAG_SIZE sizeof(uint16_t)
#define IP_400_HDR_SIZE (2*IP_400_CALL_SIZE + IP_400_FLAG_SIZE)
#define IP_400_LEN_SIZE sizeof(uint16_t)
#define MIN_FRAME_SIZE sizeof(IP400_FRAME) + PAYLOAD_MIN + N_FEC
#define MAX_FRAME_SIZE MIN_FRAME_SIZE - PAYLOAD_MIN + PAYLOAD_MAX
// packet types
enum {
UTF8_TEXT_PACKET=0, // Text packet
COMPRESSED_AUDIO, // compressed audio packet
COMPREESSD_VIDEO, // compressed video packet
DATA_PACKET, // data packet
PING_PACKET, // ping packet
IP_ENCAPSULATED, // IP encapsulated packet
AX_25_PACKET, // AX.25 encapsulated packet
RFC4733_DTMF, // DTMF packet
DMR_FRAME, // DMR Frame
DSTAR_FRAME, // Dstar Frame
P25_FRAME, // TIA project 25
NXDN_FRAME, // NXDN
M17_FRAME, // M17
TBD_1,
TBD_2,
LOCAL_COMMAND // local command frame
};
// audio compression types
enum {
AUDIO_RAW, // raw 16-bit PCM
AUDIO_ULAW, // mu law compression
AUDIO_CODEC2, // codec 2 encoded
AUDIO_AMBE // AMBE encoded
};
// H.246 Video compression
enum {
H264_240_180_24, // H.264: 240x180, 24FPS
H264_320_240_24, // H.264: 320x240, 24FPS
H264_480_360_12, // H.264: 480x360, 12FPS
H264_640_480_6 // H.264: 640x480, 6FPS
};
// callsign fields
enum {
SRC_CALLSIGN=0, // dest for encode is source callsign
DEST_CALLSIGN // dest for encode is dest callsign
};
// frame stats
typedef struct frame_stats_t {
uint32_t TxFrameCnt; // transmit frame count
uint32_t RxFrameCnt; // good receive frame count
uint32_t CRCErrors; // CRC Errors
uint32_t TimeOuts; // Timeouts
uint32_t lastRSSI; // last RSSI reading
uint32_t framesOK; // processed frames
uint32_t dropped; // rejected frames
uint32_t duplicates; // duplicates
uint32_t nBeacons; // number of beacons processed
uint32_t nRepeated; // repeated frames
} FRAME_STATS;
// packet types
#define ICMP_TYPE 1 // control message type
#define ENC_IP_TYPE 4 // encapsulated IP
#define AUDIO_TYPE 11 // audio packets
#define TEXT_TYPE 17 // port for text chat (UDP)
#define VIDEO_TYPE 75 // H.264 video
#define ENC_AX25_TYPE 93 // encapsulated AX25 packets
uint8_t getFrameStatus(void);
// references
uint8_t callEncode(char *callsign, uint16_t port, IP400_FRAME *frame, uint8_t dest, uint8_t offset);
BOOL callDecode(IP400_CALL *encCall, char *callsign, uint16_t *port);
// frame senders
BOOL SendTextFrame(char *srcCall, uint16_t srcPort, char *destCall, uint16_t dstPort, char *buf, uint16_t length, BOOL repeat);
void SendBeaconFrame(char *srcCall, uint8_t *payload, int bcnlen);
//
BOOL EnqueChatFrame(void *Frame); // queue a chat frame
FRAME_STATS *GetFrameStats(void); // return the frame stats
uint32_t GetRadioStatus(void); // get the radio status
uint8_t GetFSMState(void); // get FSM state
//
uint8_t getFrameStatus(void); // get the frame status
#endif /* FRAME_H_ */

View file

@ -0,0 +1,35 @@
/*---------------------------------------------------------------------------
Project: Ip400Spi
File Name: logger.h
Author: root
Creation Date: Mar. 6, 2025
Description: Definitions for the logger
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version, provided this copyright notice
is included.
Copyright (c) 2024-25 Alberta Digital Radio Communications Society
------------------------------------------------------------------char *geterrno(int errnum)---------*/
#ifndef INCLUDE_LOGGER_H_
#define INCLUDE_LOGGER_H_
// severity
#define LOG_DEBUG 0 // debug mode
#define LOG_NOTICE 1 // information
#define LOG_ERROR 2 // error message
#define LOG_FATAL 3 // fatal error (causes exit)
void openLog(uint8_t debug);
void logger(int severity, char *format, ...);
char *geterrno(int errnum);
#endif /* INCLUDE_LOGGER_H_ */

View file

@ -0,0 +1,143 @@
/*---------------------------------------------------------------------------
Project: Ip400Spi
File Name: spidefs.h
Author: root
Creation Date: Mar. 6, 2025
Description: Definitions for the SPI interface
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version, provided this copyright notice
is included.
Copyright (c) 2024-25 Alberta Digital Radio Communications Society
---------------------------------------------------------------------------*/
#ifndef SPIDEFSH
#define SPIDEFSH
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/spi/spidev.h>
#include <stdint.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include "types.h"
#include "frame.h"
// typedefs
typedef uint8_t SPI_WORD; // single byte
typedef uint16_t SPI_DWORD; // double byte
typedef uint32_t SPI_QWORD; // quad word
// SPI defines
enum {
SPI_0_0=0, // SPI device 0 CS 0
SPI_0_1, // SPI device 0 CS 1
SPI_1_0, // SPI device 1 CS 0
SPI_1_1, // SPI device 1 CS 1
SPI_1_2, // SPI device 1 CS 2
N_SPI // number of SPI devices
};
#define SPI_0_DEV_0 "/dev/spidev0.0"
#define SPI_0_DEV_1 "/dev/spidev0.1"
#define SPI_1_DEV_0 "/dev/spidev1.0"
#define SPI_1_DEV_1 "/dev/spidev1.1"
#define SPI_1_DEV_2 "/dev/spidev1.2"
#define SPI_SPEED 500000
#define SPI_SLOW_SPEED 100000
#define SPI_NBITS 8 // All Spi's are 8 bits
#define SPI_RESET_COUNT 10 // number of times to hold spi reset
// status returns from updates
#define SPI_REG_SUCCESS 0 // successful completion
#define SPI_REG_INVALID -1 // invalid register called
// new def's imported from node code
#define SPI_BUFFER_LEN 400 // 400 bytes/transfer
#define SPI_RAW_LEN SPI_BUFFER_LEN + sizeof(struct spi_hdr_t)
// status values
enum {
NO_FRAME=0, // no frame data to process
SINGLE_FRAME, // single frame
FRAME_FRAGMENT, // fragment
LAST_FRAGMENT, // last fragment
N_FRAGS // number of frags
};
#define PAYLOAD_MAX 1053 // max frame payload
// SPI frame header
struct spi_hdr_t {
uint8_t eye[4]; // 'IP4X'
uint8_t status; // status byte
uint8_t offset_hi; // high offset
uint8_t offset_lo; // low offset
uint8_t length_hi; // length hi
uint8_t length_lo; // length lo
uint8_t fromCall[N_CALL]; // from callsign
uint8_t fromPort[IP_400_PORT_SIZE]; // from port
uint8_t toCall[N_CALL]; // to callsign
uint8_t toPort[IP_400_PORT_SIZE]; // to port
uint8_t coding; // packet coding
uint8_t hopCount; // hop count
uint8_t flags; // remaining flags
};
// data buffer struct
typedef union {
struct {
struct spi_hdr_t hdr; // header
uint8_t buffer[SPI_BUFFER_LEN];
} spiData;
uint8_t rawData[SPI_RAW_LEN];
} SPI_BUFFER;
// SPI struct
typedef struct spi_config_t {
uint8_t mode; // mode
uint8_t bitsPerWord; // bits/word
uint32_t speed; // speed
int fd; // file descriptor
uint8_t debug; // debug
} SPI_CONFIG;
// SPI data frame
typedef struct spi_data_frame_t {
void *buffer; // data buffer
uint16_t length; // length
} SPI_DATA_FRAME;
// SPI task
int spi_lookup(char *devName);
BOOL spiTaskInit(int spiDev);
void spiTask(void);
// SPI functions
void spi_setup(int device, uint8_t spiMode, uint8_t spibitsPerWord, int spiSpeed, uint8_t debug);
int spi_open(int device);
int spi_close(int device);
int spi_fdtransfer(int device, SPI_WORD *txdata, SPI_WORD *rxdata, int length);
int spi_hdread(int fd, uint8_t *data, uint16_t length);
int spi_hdwrite(int fd, uint8_t *data, uint16_t length);
// data queuing functions
BOOL EnqueSPIFrame(void *spiFrame);
BOOL isIP400Frame(uint8_t *eye);
// UDP stuff
BOOL setup_udp_socket(char *hostname, int hostport, int localport);
BOOL send_udp_packet(void *data, uint16_t length);
#endif

View file

@ -0,0 +1,38 @@
/*---------------------------------------------------------------------------
Project: Ip400Spi
File Name: timer.h
Author: root
Creation Date: Mar. 6, 2025
Description: Definitions for the timer and associated tasks
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version, provided this copyright notice
is included.
Copyright (c) 2024-25 Alberta Digital Radio Communications Society
---------------------------------------------------------------------------*/
#ifndef INCLUDE_TIMER_H_
#define INCLUDE_TIMER_H_
#include "types.h"
// definitions
#define TIMER_VALUE 20 // 20 ms between polls
#define MSTOUS(x) ((int)x*(int)1000)
#define MSTONS(x) ((long long)x*(long long)1000000)
// functions
BOOL startTimer(int interval, void (*exec_function)(void));
void stopTimer(void);
#endif /* INCLUDE_TIMER_H_ */

View file

@ -0,0 +1,58 @@
/*---------------------------------------------------------------------------
Project: Ip400Spi
Module: Type definitions
File Name: types.h
Date Created: Jan 9, 2025
Author: MartinA
Description: Useful data types
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version, provided this copyright notice
is included.
Copyright (c) 2024-25 Alberta Digital Radio Communications Society
Revision History:
---------------------------------------------------------------------------*/
#ifndef INC_TYPES_H_
#define INC_TYPES_H_
#include <stdint.h>
#include <stddef.h>
// boolean type
typedef uint8_t BOOL; // boolean
typedef uint8_t BOOLEAN; // alternate
typedef uint8_t boolean; // another alternate
#ifndef TRUE
#define TRUE ((BOOL)1U)
#endif
#ifndef FALSE
#define FALSE ((BOOL)0U)
#endif
#ifndef __CCALL
#ifdef __cplusplus
#define __CCALL extern "C"
#else
#define __CCALL
#endif
#endif
// macros
#define islower(c) ((c >= 'a') && (c <= 'z'))
#define toupper(c) (c-0x20)
#endif /* INC_TYPES_H_ */

View file

@ -0,0 +1,56 @@
################################################################################
# Automatically-generated file. Do not edit!
################################################################################
RM := rm -rf
C_SRCS += \
./src/dataq.c \
./src/errno.c \
./src/logger.c \
./src/main.c \
./src/spi.c \
./src/spitask.c \
./src/timer.c \
./src/udp.c
OBJS += \
./src/dataq.o \
./src/errno.o \
./src/logger.o \
./src/main.o \
./src/spi.o \
./src/spitask.o \
./src/timer.o \
./src/udp.o
# Add inputs and outputs from these tool invocations to the build variables
# All Target
all: Ip400Spi
# Tool invocations
Ip400Spi: $(OBJS) makefile
@echo 'Building target: $@'
@echo 'Invoking: Cross GCC Linker'
arm-linux-gnueabihf-gcc -o "Ip400Spi" $(OBJS) -lpthread -lm -lrt
@echo 'Finished building target: $@'
@echo ' '
# Each subdirectory must supply rules for building sources it contributes
src/%.o: src/%.c
@echo 'Building file: $<'
@echo 'Invoking: Cross GCC Compiler'
arm-linux-gnueabihf-gcc -I"./include" -O0 -g3 -Wall -c -fmessage-length=0 -pthread -o "$@" "$<"
@echo 'Finished building: $<'
@echo ' '
# Other Targets
clean:
-$(RM) Ip400Spi
-$(RM) src/*.o
-@echo ' '
-include ../makefile.targets

View file

@ -0,0 +1 @@
./Ip400Spi -s /dev/spidev0.0 -n 10.93.13.104 -p 8200 -m 8400 -d 1

View file

@ -0,0 +1,70 @@
/*---------------------------------------------------------------------------
Project: ip400SPI
File Name: dataq.c
Author: MartinA
Description: This module enques and deques an IP400 Frame type on a queue
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version, provided this copyright notice
is included.
Copyright (c) Alberta Digital Radio Communications Society
All rights reserved.
Revision History:
---------------------------------------------------------------------------*/
#include <malloc.h>
#include "types.h"
#include "dataq.h"
/*
* Enque a frame. The frame data buffer must have
* alredy been allocated
*/
BOOL enqueFrame(FRAME_QUEUE *que, void *fr)
{
FRAME_QUEUE *f;
if((f = malloc(sizeof(FRAME_QUEUE))) == NULL)
return FALSE;
// set the frame buffer
f->data = fr;
insque((QUEUE_ELEM *)f, (QUEUE_ELEM *)que->q_back);
return TRUE;
}
/*
* Deqeue a frame
* Returns null if no frame is one the queue
*/
void *dequeFrame(FRAME_QUEUE *que)
{
void *ipFrame;
if(que->q_back == que)
return NULL;
FRAME_QUEUE *f = que->q_forw;
remque((struct qelem *)f);
ipFrame = f->data;
free(f);
return ipFrame;
}
// check to see if a transmit frame is queued up
BOOL checkQueue(FRAME_QUEUE *que)
{
return (que->q_back == que);
}

Binary file not shown.

View file

@ -0,0 +1,137 @@
//
// return a string value of linux error codes
//
char *linux_errnos[] = {
"No error",
"Operation not permitted",
"No such file or directory",
"No such process",
"Interrupted system call",
"I/O error",
"No such device or address",
"Arg list too long",
"Exec format error",
"Bad file number",
"No child processes",
"Try again",
"Out of memory",
"Permission denied",
"Bad address",
"Block device required",
"Device or resource busy",
"File exists",
"Cross-device link",
"No such device",
"Not a directory",
"Is a directory",
"Invalid argument",
"File table overflow",
"Too many open files",
"Not a typewriter",
"Text file busy",
"File too large",
"No space left on device",
"Illegal seek",
"Read-only file system",
"Too many links",
"Broken pipe",
"Math argument out of domain of func",
"Math result not representable",
"Resource deadlock would occur",
"File name too long",
"No record locks available",
"Function not implemented",
"Directory not empty",
"Too many symbolic links encountered",
"Operation would block",
"No message of desired type",
"Identifier removed",
"Channel number out of range",
"Level 2 not synchronized",
"Level 3 halted",
"Level 3 reset",
"Link number out of range",
"Protocol driver not attached",
"No CSI structure available",
"Level 2 halted",
"Invalid exchange",
"Invalid request descriptor",
"Exchange full",
"No anode",
"Invalid request code",
"Invalid slot",
"Deadlock",
"Bad font file format",
"Device not a stream",
"No data available",
"Timer expired",
"Out of streams resources",
"Machine is not on the network",
"Package not installed",
"Object is remote",
"Link has been severed",
"Advertise error",
"Srmount error",
"Communication error on send",
"Protocol error",
"Multihop attempted",
"RFS specific error",
"Not a data message",
"Value too large for defined data type",
"Name not unique on network",
"File descriptor in bad state",
"Remote address changed",
"Can not access a needed shared library",
"Accessing a corrupted shared library",
".lib section in a.out corrupted",
"Attempting to link in too many shared libraries",
"Cannot exec a shared library directly",
"Illegal byte sequence",
"Interrupted system call should be restarted",
"Streams pipe error",
"Too many users",
"Socket operation on non-socket",
"Destination address required",
"Message too long",
"Protocol wrong type for socket",
"Protocol not available",
"Protocol not supported",
"Socket type not supported",
"Operation not supported on transport endpoint",
"Protocol family not supported",
"Address family not supported by protocol",
"Address already in use",
"Cannot assign requested address",
"Network is down",
"Network is unreachable",
"Network dropped connection because of reset",
"Software caused connection abort",
"Connection reset by peer",
"No buffer space available",
"Transport endpoint is already connected",
"Transport endpoint is not connected",
"Cannot send after transport endpoint shutdown",
"Too many references: cannot splice",
"Connection timed out",
"Connection refused",
"Host is down",
"No route to host",
"Operation already in progress",
"Operation now in progress",
"Stale NFS file handle",
"Structure needs cleaning",
"Not a XENIX named type file",
"No XENIX semaphores available",
"Is a named type file",
"Remote I/O error",
"Quota exceeded",
"No medium found",
"Wrong medium type"
};
char *geterrno(int errnum)
{
errnum = (errnum < 0) ? errnum * -1: errnum;
return(linux_errnos[errnum]);
}

Binary file not shown.

View file

@ -0,0 +1,69 @@
/*---------------------------------------------------------------------------
Project: Ip400spi
Module: Logger
File Name: logger.c
Author: MartinA
Revision: 1.00
Description: Log error messages in the system
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version, provided this copyright notice
is included.
Copyright (c) 2024-25 Alberta Digital Radio Communications Society
Revision History:
---------------------------------------------------------------------------*/
#include <stdio.h>
#include <stdarg.h>
#include "types.h"
#include "logger.h"
BOOL logDebug = FALSE;
void openLog(BOOL debug)
{
logDebug = debug;
}
// log an error or info message
void logger(int severity, char *format, ...)
{
switch(severity) {
case LOG_DEBUG:
if(!logDebug)
return;
fprintf(stderr, "DEBUG: ");
break;
case LOG_NOTICE:
fprintf(stderr, "Notice: ");
break;
case LOG_ERROR:
fprintf(stderr, "ERROR: ");
break;
case LOG_FATAL:
fprintf(stderr, "FATAL: ");
break;
}
// process the arg list
va_list argptr;
va_start(argptr, format);
vfprintf(stderr, format, argptr);
va_end(argptr);
}

Binary file not shown.

View file

@ -0,0 +1,164 @@
/*---------------------------------------------------------------------------
Project: Ip400Spi
File Name: main.c
Author: root
Creation Date: Mar. 6, 2025
Description: Mainline for SPI daemon
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version, provided this copyright notice
is included.
Copyright (c) 2024-25 Alberta Digital Radio Communications Society
---------------------------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include "types.h"
#include "logger.h"
#include "spidefs.h"
#include "timer.h"
// SPI device
char spiDev[20];
int debugFlag;
char hostname[50]; // name of remote host
uint16_t hostport; // host port
uint16_t localport; // my port
// debug modes
#define DEBUG_LOG 0x01 // debug log
#define DEBUG_SPI 0x02 // debug SPI
// timer or interrupt mode
#define NO_INTERRUPT 0 // do not use interrupts
// forward refs
void show_help(char *name);
// catch a control-c or other terminationTIMER_VALUE
void sighandler(int s)
{
logger(LOG_ERROR, "\n3000Caught signal %d\n",s);
stopTimer();
exit(1);
}
int main(int argc, char *argv[])
{
int c;
/*
// set up control/c catch
struct sigaction sigIntHandler;
sigIntHandler.sa_handler = sighandler;
sigemptyset(&sigIntHandler.sa_mask);
sigIntHandler.sa_flags = 0;
sigaction(SIGINT, &si3000gIntHandler, NULL);
*/
// set default SPI
strcpy(spiDev, SPI_0_DEV_0);
debugFlag = FALSE;
// parse command line parameters
while ((c = getopt(argc, argv, "s:d:hn:p:m:")) != -1) {
// process the command line
switch((char )c) {
// SPI device
case 's':
strcpy(spiDev, optarg);
break;
// host name
case 'n':
strcpy(hostname, optarg);
break;
// host port
case 'p':
sscanf(optarg, "%hd", &hostport);
break;
// my port
case 'm':
sscanf(optarg, "%hd", &localport);
break;
// debug
case 'd':
sscanf(optarg, "%d", &debugFlag);
break;
// help
case 'h':
show_help(argv[0]);
break;
}
}
// open the log and test enabled
openLog(debugFlag&DEBUG_LOG);
logger(LOG_DEBUG, "Debug logging enabled \n");
int spiDevNum = spi_lookup(spiDev);
if(spiDevNum == -1) {
logger(LOG_FATAL, "Cannot find device name %s\n", spiDev);
exit(100);
}
spi_setup(spiDevNum, SPI_MODE_0, SPI_NBITS, SPI_SPEED, debugFlag&DEBUG_SPI);
if(!spiTaskInit(spiDevNum)) {
logger(LOG_FATAL, "Cannot open SPI device %s\n", spiDev);
exit(100);
}
if(!setup_udp_socket(hostname, hostport, localport)) {
logger(LOG_FATAL, "Cannot create UDP socket to %s:%d\n", hostname, hostport, localport);
exit(100);
}
#if NO_INTERRUPT
while(1) {
spiTask();
// for(int i=0;i<2;i++)
usleep(MSTOUS(TIMER_VALUE));
}
forced syntax error; // force error if this mode is invoked
#else
if(!startTimer(TIMER_VALUE, &spiTask)) {
logger(LOG_FATAL, "Could not start the SPI task\n");
exit(101);
}
#endif
return EXIT_SUCCESS;
}
void show_help(char *name) {
fprintf(stderr,
"Usage: %s -[sdhnpm]\n"
"-s SPI device name\n"
"-d debug mode \n"
"-h print this help message\n"
"-n remote host name\n"
"-p remote port number"
"-m my port number",
name);
}

Binary file not shown.

View file

@ -0,0 +1,180 @@
/*---------------------------------------------------------------------------
Project: Ip400Spi
File Name: spi.c
Author: Martin VE6VH
Creation Date: Mar. 6, 2025
Description: {Definition here...)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version, provided this copyright notice
is included.
Copyright (c) 2024-25 Alberta Digital Radio Communications Society
---------------------------------------------------------------------------*/
#include <string.h>
#include "spidefs.h"
#include "logger.h"
// translate SPI logical devices to real devices
char *devnames[N_SPI] = {
SPI_0_DEV_0,
SPI_0_DEV_1,
SPI_1_DEV_0,
SPI_1_DEV_1,
SPI_1_DEV_2
};
SPI_CONFIG spi_config[N_SPI]; // SPI configurations
/*
* setup the SPI devices
*/
void spi_setup(int device, uint8_t spiMode, uint8_t spibitsPerWord, int spiSpeed, uint8_t debug)
{
spi_config[device].mode = spiMode;
spi_config[device].bitsPerWord = spibitsPerWord;
spi_config[device].speed = spiSpeed;
spi_config[device].debug = debug;
}
int spi_lookup(char *devName)
{
for(int i=0;i<N_SPI;i++)
if(!strcmp(devName, devnames[i]))
return i;
return -1;
}
/*
* open an SPI device
*/
int spi_open(int device)
{
int spifd = -1;
int statusVal = -1;
if(spi_config[device].debug)
logger(LOG_NOTICE, "SPI Open %d:\n", device);
spifd = open(devnames[device], O_RDWR);
if(spifd < 0) {
logger(LOG_FATAL, "OPEN failed on %s\n", devnames[device]);
return spifd;
}
statusVal = ioctl (spifd, SPI_IOC_WR_MODE, &spi_config[device].mode);
if(statusVal < 0) {
logger(LOG_ERROR, "IOCTL failed: wr_mode\n");
return(statusVal);
}
statusVal = ioctl (spifd, SPI_IOC_RD_MODE, &spi_config[device].mode);
if(statusVal < 0) {
logger(LOG_ERROR, "IOCTL failed: rd_mode\n");
return(statusVal);
}
statusVal = ioctl (spifd, SPI_IOC_WR_BITS_PER_WORD, &spi_config[device].bitsPerWord);
if(statusVal < 0) {
logger(LOG_ERROR, "IOCTL failed: wr_bits/word\n");
return(statusVal);
}
statusVal = ioctl (spifd, SPI_IOC_RD_BITS_PER_WORD, &spi_config[device].bitsPerWord);
if(statusVal < 0) {
logger(LOG_ERROR, "IOCTL failed: rd_bits/word\n");
return(statusVal);
}
statusVal = ioctl (spifd, SPI_IOC_WR_MAX_SPEED_HZ, &spi_config[device].speed);
if(statusVal < 0) {
logger(LOG_ERROR, "IOCTL failed: wr_speed\n");
return(statusVal);
}
statusVal = ioctl (spifd, SPI_IOC_RD_MAX_SPEED_HZ, &spi_config[device].speed);
if(statusVal < 0) {
logger(LOG_ERROR, "IOCTL failed: rd_speed\n");
return(statusVal);
}
spi_config[device].fd = spifd;
if(spi_config[device].debug) {
logger(LOG_NOTICE, "Opened device %s with fd %d\n", devnames[device], spifd);
}
return spifd;
}
/*
* close an SPI device
*/
int spi_close(int spifd)
{
int statusVal = -1;
statusVal = close(spifd);
return statusVal;
}
/*
* perform a half duplex write
*/
int spi_hdwrite(int fd, uint8_t *data, uint16_t length)
{
return write(fd, data, length);
}
/*
* perform a half duplex read
*/
int spi_hdread(int fd, uint8_t *data, uint16_t length)
{
return read(fd, data, length);
}
/*
* perform a full duplex read/write to the SPI using IOCTL
*/
int spi_fdtransfer(int device, SPI_WORD *txdata, SPI_WORD *rxdata, int length)
{
static struct spi_ioc_transfer spi;
int retVal = -1;
memset(&spi, 0, sizeof(spi));
spi.tx_buf = (unsigned long)(txdata);
spi.rx_buf = (unsigned long)(rxdata);
spi.len = sizeof(SPI_WORD)*length;
spi.delay_usecs = 0 ;
spi.speed_hz = spi_config[device].speed;
spi.bits_per_word = spi_config[device].bitsPerWord ;
spi.cs_change = 0;
spi.tx_nbits = 8;
spi.rx_nbits = 8;
spi.pad = 0;
retVal = ioctl (spi_config[device].fd, SPI_IOC_MESSAGE(1), &spi) ;
if(spi_config[device].debug) {
logger(LOG_NOTICE, "Did IOCTL on %s with fd %d\n", devnames[device], spi_config[device].fd);
logger(LOG_NOTICE, "Return value %d\n", retVal);
}
if(retVal < 0)
return retVal;
return retVal;
}

Binary file not shown.

View file

@ -0,0 +1,280 @@
/*---------------------------------------------------------------------------
Project: Ip400Spi
File Name: spitask.c
Author: VE6VH
Creation Date: Mar. 6, 2025
Description: {Definition here...)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version, provided this copyright notice
is included.
Copyright (c) 2024-25 Alberta Digital Radio Communications Society
---------------------------------------------------------------------------*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "types.h"
#include "spidefs.h"
#include "logger.h"
#include "dataq.h"
// locals
int spiDevFD; // spi device file descriptor
int spiDevNum; // device number
// transmittter states
enum {
SPITXIDLE=0, // idle
SPITXFRAG // next fragment
};
// receiver states
enum {
SPIRXIDLE=0, // idle
SPIRXFRAG // next fragment
};
uint8_t SPITxState; // transmitter state
uint8_t SPIRxState; // receiver state
// SPI transmit frame queue
FRAME_QUEUE SPITxQueue;
// frame buffers
static SPI_BUFFER spiTxBuffer;
static SPI_BUFFER spiRxBuffer;
static union {
struct {
struct spi_hdr_t hdr; // header
uint8_t buffer[PAYLOAD_MAX];
} spiData;
uint8_t rawData[PAYLOAD_MAX + sizeof(struct spi_hdr_t)];
} rxFrameBuffer;
// frame validator
BOOL isIP400Frame(uint8_t *eye);
BOOL EnqueSPIFrame(void *spiFrame)
{
SPI_DATA_FRAME *qFrame, *SrcFrame = (SPI_DATA_FRAME *)spiFrame;
uint8_t *frameBuffer;
// allocate an IP400 frame
if((qFrame=malloc(sizeof(SPI_DATA_FRAME)))== NULL)
return FALSE;
memcpy(qFrame, SrcFrame, sizeof(SPI_DATA_FRAME));
// alloc the data portion of the frame
if((frameBuffer=malloc(SrcFrame->length)) == NULL) {
return FALSE;
}
memcpy(frameBuffer, (uint8_t *)SrcFrame->buffer, SrcFrame->length);
qFrame->buffer = frameBuffer;
qFrame->length = SrcFrame->length;
if(!enqueFrame(&SPITxQueue, qFrame))
return FALSE;
return TRUE;
}
/*
* Initialize the SPI task
*/
BOOL spiTaskInit(int spiDev)
{
// open the SPI device
if((spiDevFD = spi_open(spiDev)) == -1) {
logger(LOG_ERROR, "Unable to open SPI device %d\n", spiDev);
return FALSE;
}
spiTxBuffer.spiData.hdr.status = NO_FRAME;
spiRxBuffer.spiData.hdr.status = NO_FRAME;
// init the tx queue
SPITxQueue.q_forw = &SPITxQueue;
SPITxQueue.q_back = &SPITxQueue;
// init vars
spiDevNum = spiDev;
SPITxState = SPITXIDLE;
SPIRxState = SPIRXIDLE;
rxFrameBuffer.spiData.hdr.eye[0] = 'I';
rxFrameBuffer.spiData.hdr.eye[1] = 'P';
rxFrameBuffer.spiData.hdr.eye[2] = (400 >> 8) & 0xff;
rxFrameBuffer.spiData.hdr.eye[3] = (400 & 0xff);
return TRUE;
}
/*
* Run the SPI functions.
* Poll receiver for an inbound frame and read it. Add it to the frame queue.
* Poll transmit queue for an outbound frame and send it.
*/
void spiTask(void)
{
static SPI_DATA_FRAME *txFrame;
static uint16_t txSegLength;
static uint8_t *prxData;
static uint16_t rxSegLen;
memset(spiRxBuffer.rawData, 0, SPI_RAW_LEN);
// do an exchange with the STM32
int nxferred = spi_fdtransfer(spiDevNum, spiTxBuffer.rawData, spiRxBuffer.rawData, SPI_RAW_LEN);
if(nxferred == -1) {
logger(LOG_ERROR, "SPI transmit error %d: %s\n", nxferred, geterrno(nxferred));
}
/*
* Inbound frame. Reassemble fragments if needed...
*/
// we have to declare these here, as teh !#)$*& compiler will not allow declarations in statements
uint8_t rstat = NO_FRAME;
uint8_t fragStat;
uint16_t offset;
uint16_t frameLen=0, prevLen=0;
switch(SPIRxState) {
case SPIRXIDLE:
rstat = spiRxBuffer.spiData.hdr.status;
// next validate the frame type
if((rstat == NO_FRAME) || (rstat >= N_FRAGS))
break;
// validate the eye of the frame
if(!isIP400Frame(spiRxBuffer.spiData.hdr.eye))
break;
// copy the header fields
memcpy(&rxFrameBuffer.spiData.hdr, &spiRxBuffer.spiData.hdr, sizeof(struct spi_hdr_t));
// copy data fields
prxData = rxFrameBuffer.spiData.buffer;
rxSegLen = (spiRxBuffer.spiData.hdr.length_hi << 8) + spiRxBuffer.spiData.hdr.length_lo;
memcpy(prxData, spiRxBuffer.spiData.buffer, rxSegLen);
if(rstat != SINGLE_FRAME) {
SPIRxState = SPIRXFRAG;
} else {
rxFrameBuffer.spiData.hdr.status = SINGLE_FRAME;
rxFrameBuffer.spiData.hdr.length_hi = (rxSegLen >> 8);
rxFrameBuffer.spiData.hdr.length_lo = (rxSegLen & 0xFF);
rxFrameBuffer.spiData.hdr.offset_hi = rxFrameBuffer.spiData.hdr.offset_lo = 0;
send_udp_packet(rxFrameBuffer.rawData, rxSegLen+sizeof(struct spi_hdr_t));
SPIRxState = SPIRXIDLE;
spiRxBuffer.spiData.hdr.status = N_FRAGS; // set an invalid frame type
}
break;
case SPIRXFRAG:
fragStat = spiRxBuffer.spiData.hdr.status;
offset = ((uint16_t)spiTxBuffer.spiData.hdr.offset_hi << 8) + (uint16_t)spiTxBuffer.spiData.hdr.offset_lo;
prxData = rxFrameBuffer.rawData + offset;
rxSegLen = (spiRxBuffer.spiData.hdr.length_hi << 8) + spiRxBuffer.spiData.hdr.length_lo;
memcpy(prxData, spiRxBuffer.spiData.buffer, rxSegLen);
if(fragStat == LAST_FRAGMENT) {
frameLen = offset + rxSegLen;
spiRxBuffer.spiData.hdr.length_hi = (frameLen >> 8);
spiRxBuffer.spiData.hdr.length_lo = (frameLen & 0xFF);
// process frame for tx here...
SPIRxState = SPIRXIDLE; // placeholder
}
break;
}
/*
* process an outbound frame
* Fragment it if longer that 500 byte, s->length);s
*/
switch(SPITxState) {
case SPITXIDLE:
if((txFrame=dequeFrame(&SPITxQueue)) == NULL) {
spiTxBuffer.spiData.hdr.status = NO_FRAME;
break;
}
txSegLength = txFrame->length;
spiTxBuffer.spiData.hdr.status = SINGLE_FRAME;
// fragment the frame if needed
if(txFrame->length > SPI_BUFFER_LEN) {
txSegLength = SPI_BUFFER_LEN;
spiTxBuffer.spiData.hdr.status = FRAME_FRAGMENT;
SPITxState = SPITXFRAG;
}
// send the frame and/or fragment
memcpy(spiTxBuffer.rawData, txFrame->buffer, txSegLength);
spiTxBuffer.spiData.hdr.offset_hi = spiTxBuffer.spiData.hdr.offset_lo = 0;
// release memory if only a single frame
if(spiTxBuffer.spiData.hdr.status == SINGLE_FRAME)
{
free(txFrame->buffer);
free(txFrame);
}
break;
case SPITXFRAG:
offset = ((uint16_t)(spiTxBuffer.spiData.hdr.offset_hi) << 8) + (uint16_t)spiTxBuffer.spiData.hdr.offset_lo;
prevLen = ((uint16_t)(spiTxBuffer.spiData.hdr.length_hi) << 8) + (uint16_t)spiTxBuffer.spiData.hdr.length_lo;
offset += prevLen;
txFrame->length -= prevLen;
txSegLength = txFrame->length;
if(txFrame->length > SPI_BUFFER_LEN) {
spiTxBuffer.spiData.hdr.status = FRAME_FRAGMENT;
txSegLength = SPI_BUFFER_LEN;
} else {
spiTxBuffer.spiData.hdr.status = LAST_FRAGMENT;
SPITxState = SPITXIDLE;
}
// send the next fragment
void *fragAddr = txFrame->buffer + offset;
memcpy(spiTxBuffer.spiData.buffer, fragAddr, txSegLength);
spiTxBuffer.spiData.hdr.offset_hi = (offset >> 8);
spiTxBuffer.spiData.hdr.offset_lo = (offset & 0xff);
spiTxBuffer.spiData.hdr.length_hi = (txSegLength >> 8);
spiTxBuffer.spiData.hdr.length_lo = (txSegLength & 0xFF);
// done with frame
if(spiTxBuffer.spiData.hdr.status == LAST_FRAGMENT)
{
free(txFrame->buffer);
free(txFrame);
}
break;
}
}
// validate the frame eye
BOOL isIP400Frame(uint8_t *eye)
{
if((eye[0] != 'I') || (eye[1] != 'P'))
return FALSE;
if((eye[2] != '4') || (eye[3] != 'C'))
return FALSE;
return TRUE;
}

Binary file not shown.

View file

@ -0,0 +1,182 @@
/*---------------------------------------------------------------------------
Project: Ip400Spi
File Name: timer.c
Author: root
Creation Date: Mar. 6, 2025
Description: Timer functions
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version, provided this copyright notice
is included.
Copyright (c) 2024-25 Alberta Digital Radio Communications Society
---------------------------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <signal.h>
#include <time.h>
#include "types.h"
#include "spidefs.h"
#include "logger.h"
#include "timer.h"
// local definitions
#define CLOCKID CLOCK_REALTIME
#define SIG_TIMER SIGUSR1 // signal to be used for timer
// Timer function threads
struct timer_threads_t {
BOOL exit;
void (*exec_func)(void); // function called by timer
pthread_t timer_fn;
pthread_mutex_t timer_mutex; // timer mutex
pthread_cond_t timer_wait_cond; // timer wait condition
pthread_mutex_t timer_exit_mutex; // timer exit mutex
pthread_cond_t timer_exit_wait_cond; // timbeforeer exit wait condition
} timer_threads;
// function called by timer
static void *timer_threads_fn(void *arg);
/*
* handle the timer signal
*/
static void timer_signal(int sig, siginfo_t *si, void *uc)
{
/* Not a timer signal? */
if (!si || si->si_code != SI_TIMER) {
return;
}
pthread_mutex_lock(&timer_threads.timer_mutex);
pthread_cond_signal(&timer_threads.timer_wait_cond);
pthread_mutex_unlock(&timer_threads.timer_mutex);
}
/*
* stop the timer
*/
void stopTimer(void)
{
pthread_mutex_lock(&timer_threads.timer_exit_mutex);
pthread_cond_signal(&timer_threads.timer_exit_wait_cond);
pthread_mutex_unlock(&timer_threads.timer_exit_mutex);
timer_threads.exit = TRUE;
}
/*
* timer process: does not return until signaled to do soTIMER_THREADS;
* call the function every interval in ms
*/
BOOL startTimer(int interval, void (*exec_function)(void))
{
struct sigaction sa;
struct sigevent sev;
static sigset_t mask;
long long freq_nanosecs;
struct itimerspec its;
timer_t timerid;
// init background threads
timer_threads.exit = FALSE;
timer_threads.exec_func = exec_function;
pthread_mutex_init(&timer_threads.timer_mutex, NULL);
pthread_mutex_init(&timer_threads.timer_exit_mutex, NULL);
pthread_cond_init(&timer_threads.timer_wait_cond, NULL);
pthread_cond_init(&timer_threads.timer_exit_wait_cond, NULL);
// step 1: establish a handle for timer signal
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = timer_signal;
sigemptyset(&sa.sa_mask);
if (sigaction(SIG_TIMER, &sa, NULL) == -1) {
logger(LOG_DEBUG,"sigaction Error\n");
return(FALSE);
}
// step 2: block it temporarily
sigemptyset(&mask);
sigaddset(&mask, SIG_TIMER);
if (pthread_sigmask(SIG_BLOCK, &mask, NULL) == -1) {
logger(LOG_DEBUG,"sigprocmask Error\n");
return(FALSE);
}
// step 3: create the timer
sev.sigev_notify = SIGEV_SIGNAL;
sev.sigev_signo = SIG_TIMER;
sev.sigev_value.sival_ptr = &timerid;
if (timer_create((clockid_t)CLOCKID, &sev, &timerid) == -1) {
logger(LOG_DEBUG,"timer_create Error\n");
return(FALSE);
}
// start the background thread
pthread_create(&timer_threads.timer_fn, NULL, timer_threads_fn, (void *)&timer_threads);
// now start the timer
freq_nanosecs = MSTONS(interval);
its.it_value.tv_sec = freq_nanosecs / 1000000000;
its.it_value.tv_nsec = freq_nanosecs % 1000000000;
its.it_interval.tv_sec = its.it_value.tv_sec;
its.it_interval.tv_nsec = its.it_value.tv_nsec;
if (timer_settime(timerid, 0, &its, NULL) == -1) {
logger(LOG_DEBUG,"timer_settime Error\n");
return(FALSE);
}
// unblock the signal
if (pthread_sigmask(SIG_UNBLOCK, &mask, NULL) == -1) {
logger(LOG_DEBUG,"sigprocmask Error\n");
return(FALSE);
}
// wait for the signal to stop
pthread_mutex_lock(&timer_threads.timer_exit_mutex);
while(!timer_threads.exit) {
pthread_cond_wait(&timer_threads.timer_exit_wait_cond, &timer_threads.timer_exit_mutex);
}
pthread_mutex_unlock(&timer_threads.timer_exit_mutex);
// stop the timer
its.it_value.tv_nsec = 0;
its.it_interval.tv_sec = 0;
its.it_interval.tv_nsec = 0;
timer_settime(timerid, 0, &its, NULL);
// stop the background thread
pthread_join(timer_threads.timer_fn, NULL);
logger(LOG_DEBUG,"Timer process stopped\n");
return TRUE;
}
// this is called at the timer interval...
static void *timer_threads_fn(void *arg)
{
struct timer_threads_t *s = arg;
// read the pipe every 20 ms
while (!s->exit) {
// wait for timer
pthread_mutex_lock(&s->timer_mutex);
pthread_cond_wait(&s->timer_wait_cond, &s->timer_mutex);
pthread_mutex_unlock(&s->timer_mutex);
// call function
(s->exec_func)();
}
return NULL;
}

Binary file not shown.

View file

@ -0,0 +1,176 @@
/*---------------------------------------------------------------------------
Project: Ip400Spi
File Name: udp.c
Author: root
Creation Date: Mar. 7, 2025
Description: THis module contains the UDP socket and code
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version, provided this copyright notice
is included.
Copyright (c) 2024-25 Alberta Digital Radio Communications Society
---------------------------------------------------------------------------*/
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>
#include <pthread.h>
#include "types.h"
#include "logger.h"
#include "spidefs.h"
// local defines
#define MAX_BUFFER 1024 // max buffer size
#define SOCKET_ERROR -1 // socket error
struct udp_threads_t {
int udpsock; // socket
struct sockaddr_in si_remote; // where to send it
// background task
BOOL exit; // exit
pthread_t rxfunc; // rx function
uint8_t buffer[MAX_BUFFER]; // rx bufferrxSegLen
int length; // length received
} udp_threads;
void *udp_receive_task(void *args);
/*
* set up the UDP Socket
*/
BOOL setup_udp_socket(char *hostname, int hostport, int localport)
{
struct sockaddr_in si_me;
struct hostent ah, *host;
memset(&ah,0,sizeof(ah));
host = gethostbyname(hostname);
if (!host)
{
logger(LOG_NOTICE, "Unable to find host %s\n", hostname);
return FALSE;
}
memset((char *) &udp_threads.si_remote, 0, sizeof(struct sockaddr_in));
udp_threads.si_remote.sin_addr = *(struct in_addr *)host->h_addr;
udp_threads.si_remote.sin_family = AF_INET;
udp_threads.si_remote.sin_port = htons(hostport);
if ((udp_threads.udpsock=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1)
{
logger(LOG_NOTICE, "Unable to create new socket for fpga udp_BOOL EnqueSPIFrame(void *buffer, uint16_t length)threads_tconnection\n");
return FALSE;
}
memset((char *) &si_me, 0, sizeof(si_me));
si_me.sin_family = AF_INET;
si_me.sin_port = htons(localport);
si_me.sin_addr.s_addr = htonl(INADDR_ANY);
if (!strncmp(inet_ntoa(udp_threads.si_remote.sin_addr),"127.",4))
si_me.sin_addr.s_addr = inet_addr("127.0.0.1");
if (bind(udp_threads.udpsock, (const struct sockaddr *)&si_me, sizeof(si_me))==-1)
{
logger(LOG_NOTICE, "Unable to bind to %s:%d for fpga connection\n",
inet_ntoa(si_me.sin_addr), ntohs(si_me.sin_port));
return FALSE;
}
if (!udp_threads.udpsock) {
logger(LOG_NOTICE, "Unable to create UDP socket forBOOL setup_udp_socket(char *hostname, int hostport, int localport) %s:%d\n", hostname, localport);
return FALSE;
}
// set the receive timeout on the read to one second
struct timeval timeout;
timeout.tv_sec = (__time_t)1;
timeout.tv_usec =(__suseconds_t)0;
if (setsockopt (udp_threads.udpsock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)) < 0) {
logger(LOG_NOTICE, "setsockopt timeout failed\n");
}
// start the rx background
udp_threads.exit = FALSE;
pthread_create(&udp_threads.rxfunc, NULL, &udp_receive_task, (void *)(&udp_threads));
return TRUE;
}
/*
* Close the socket
*/
void close_udp_socket(void)
{
udp_threads.exit = TRUE;
pthread_join(udp_threads.rxfunc, NULL);
if(udp_threads.udpsock)
close(udp_threads.udpsock);
}
/*
* Send a UDP packet
*/
BOOL send_udp_packet(void *data, uint16_t length)
{
int stat;
stat = sendto(udp_threads.udpsock, data, length, 0, (struct sockaddr *)&udp_threads.si_remote,sizeof(struct sockaddr_in));
if(stat == -1) {
logger(LOG_NOTICE, "Error on UDP packet: %d:%s\n", errno, geterrno(errno));
return FALSE;
}
logger(LOG_DEBUG, "UDP Packet Sent\n");
return TRUE;
}
/*
* Receive a UDP packet
*/
void *udp_receive_task(void *args)
{
struct sockaddr_in si_them;
unsigned int themlen = sizeof(struct sockaddr_in);
struct udp_threads_t *s = (struct udp_threads_t *)args;
SPI_DATA_FRAME *spiFrame;
while(!s->exit) {
if ((s->length = recvfrom(s->udpsock,s->buffer,MAX_BUFFER, 0,
(struct sockaddr *)&si_them,&themlen)) == SOCKET_ERROR)
{
/*
* process a non-timeout.
*/
if(errno != EAGAIN) {
logger(LOG_ERROR, "UDP Receive error %d\n", errno);
}
} else {
// check the packet header first
if(!isIP400Frame(s->buffer))
continue;
// rx a good packet
if((spiFrame = malloc(s->length)) == NULL) {
continue;
}
spiFrame->buffer = s->buffer;
spiFrame->length = s->length;
EnqueSPIFrame(spiFrame);
}
}
return NULL;
}

Binary file not shown.