diff --git a/Raspberry Pi/Read.Me b/Raspberry Pi/Read.Me new file mode 100644 index 0000000..04fdc4c --- /dev/null +++ b/Raspberry Pi/Read.Me @@ -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. \ No newline at end of file diff --git a/Raspberry Pi/ip400spi/Debug/Ip400Spi b/Raspberry Pi/ip400spi/Debug/Ip400Spi new file mode 100644 index 0000000..9f5b01f Binary files /dev/null and b/Raspberry Pi/ip400spi/Debug/Ip400Spi differ diff --git a/Raspberry Pi/ip400spi/Ip400Spi b/Raspberry Pi/ip400spi/Ip400Spi new file mode 100644 index 0000000..fda3cc8 Binary files /dev/null and b/Raspberry Pi/ip400spi/Ip400Spi differ diff --git a/Raspberry Pi/ip400spi/include/dataq.h b/Raspberry Pi/ip400spi/include/dataq.h new file mode 100644 index 0000000..8c8a55f --- /dev/null +++ b/Raspberry Pi/ip400spi/include/dataq.h @@ -0,0 +1,59 @@ +/*--------------------------------------------------------------------------- + Project: WL33_NUCLEO_UART + + Module: + + File Name: queue.h + + Date Created: Jan 9, 2025 + + Author: MartinA + + Description: + + 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_ */ diff --git a/Raspberry Pi/ip400spi/include/frame.h b/Raspberry Pi/ip400spi/include/frame.h new file mode 100644 index 0000000..b256961 --- /dev/null +++ b/Raspberry Pi/ip400spi/include/frame.h @@ -0,0 +1,206 @@ +/*--------------------------------------------------------------------------- + Project: WL33_NUCLEO_UART + + Module: + + File Name: frame.h + + Date Created: Jan 8, 2025 + + Author: MartinA + + Description: + + Copyright © 2024-25, Alberta Digital Radio Communications Society, + All rights reserved + + + Revision History: + +---------------------------------------------------------------------------*/ + +#ifndef FRAME_H_ +#define FRAME_H_ + +#include +#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_ */ diff --git a/Raspberry Pi/ip400spi/include/logger.h b/Raspberry Pi/ip400spi/include/logger.h new file mode 100644 index 0000000..4461d13 --- /dev/null +++ b/Raspberry Pi/ip400spi/include/logger.h @@ -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_ */ diff --git a/Raspberry Pi/ip400spi/include/spidefs.h b/Raspberry Pi/ip400spi/include/spidefs.h new file mode 100644 index 0000000..8e32825 --- /dev/null +++ b/Raspberry Pi/ip400spi/include/spidefs.h @@ -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 +#include +#include +#include +#include +#include +#include +#include + +#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 + diff --git a/Raspberry Pi/ip400spi/include/timer.h b/Raspberry Pi/ip400spi/include/timer.h new file mode 100644 index 0000000..9455ae6 --- /dev/null +++ b/Raspberry Pi/ip400spi/include/timer.h @@ -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_ */ diff --git a/Raspberry Pi/ip400spi/include/types.h b/Raspberry Pi/ip400spi/include/types.h new file mode 100644 index 0000000..2c45535 --- /dev/null +++ b/Raspberry Pi/ip400spi/include/types.h @@ -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 +#include + +// 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_ */ diff --git a/Raspberry Pi/ip400spi/makefile b/Raspberry Pi/ip400spi/makefile new file mode 100644 index 0000000..4c637af --- /dev/null +++ b/Raspberry Pi/ip400spi/makefile @@ -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 diff --git a/Raspberry Pi/ip400spi/runSpi.sh b/Raspberry Pi/ip400spi/runSpi.sh new file mode 100644 index 0000000..9488695 --- /dev/null +++ b/Raspberry Pi/ip400spi/runSpi.sh @@ -0,0 +1 @@ +./Ip400Spi -s /dev/spidev0.0 -n 10.93.13.104 -p 8200 -m 8400 -d 1 diff --git a/Raspberry Pi/ip400spi/src/dataq.c b/Raspberry Pi/ip400spi/src/dataq.c new file mode 100644 index 0000000..e0e64f6 --- /dev/null +++ b/Raspberry Pi/ip400spi/src/dataq.c @@ -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 + +#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); +} diff --git a/Raspberry Pi/ip400spi/src/dataq.o b/Raspberry Pi/ip400spi/src/dataq.o new file mode 100644 index 0000000..04cbe45 Binary files /dev/null and b/Raspberry Pi/ip400spi/src/dataq.o differ diff --git a/Raspberry Pi/ip400spi/src/errno.c b/Raspberry Pi/ip400spi/src/errno.c new file mode 100644 index 0000000..c34d80b --- /dev/null +++ b/Raspberry Pi/ip400spi/src/errno.c @@ -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]); +} diff --git a/Raspberry Pi/ip400spi/src/errno.o b/Raspberry Pi/ip400spi/src/errno.o new file mode 100644 index 0000000..316ea21 Binary files /dev/null and b/Raspberry Pi/ip400spi/src/errno.o differ diff --git a/Raspberry Pi/ip400spi/src/logger.c b/Raspberry Pi/ip400spi/src/logger.c new file mode 100644 index 0000000..dbd1073 --- /dev/null +++ b/Raspberry Pi/ip400spi/src/logger.c @@ -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 +#include + +#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); +} + + diff --git a/Raspberry Pi/ip400spi/src/logger.o b/Raspberry Pi/ip400spi/src/logger.o new file mode 100644 index 0000000..d6c64e2 Binary files /dev/null and b/Raspberry Pi/ip400spi/src/logger.o differ diff --git a/Raspberry Pi/ip400spi/src/main.c b/Raspberry Pi/ip400spi/src/main.c new file mode 100644 index 0000000..d300cac --- /dev/null +++ b/Raspberry Pi/ip400spi/src/main.c @@ -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 +#include +#include +#include +#include +#include + +#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); +} diff --git a/Raspberry Pi/ip400spi/src/main.o b/Raspberry Pi/ip400spi/src/main.o new file mode 100644 index 0000000..23f0bba Binary files /dev/null and b/Raspberry Pi/ip400spi/src/main.o differ diff --git a/Raspberry Pi/ip400spi/src/spi.c b/Raspberry Pi/ip400spi/src/spi.c new file mode 100644 index 0000000..cd5a32d --- /dev/null +++ b/Raspberry Pi/ip400spi/src/spi.c @@ -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 + +#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 +#include +#include + +#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; + +} diff --git a/Raspberry Pi/ip400spi/src/spitask.o b/Raspberry Pi/ip400spi/src/spitask.o new file mode 100644 index 0000000..173024b Binary files /dev/null and b/Raspberry Pi/ip400spi/src/spitask.o differ diff --git a/Raspberry Pi/ip400spi/src/timer.c b/Raspberry Pi/ip400spi/src/timer.c new file mode 100644 index 0000000..ca63b02 --- /dev/null +++ b/Raspberry Pi/ip400spi/src/timer.c @@ -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 +#include +#include +#include +#include + +#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; +} diff --git a/Raspberry Pi/ip400spi/src/timer.o b/Raspberry Pi/ip400spi/src/timer.o new file mode 100644 index 0000000..225f333 Binary files /dev/null and b/Raspberry Pi/ip400spi/src/timer.o differ diff --git a/Raspberry Pi/ip400spi/src/udp.c b/Raspberry Pi/ip400spi/src/udp.c new file mode 100644 index 0000000..3925aa7 --- /dev/null +++ b/Raspberry Pi/ip400spi/src/udp.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +} + diff --git a/Raspberry Pi/ip400spi/src/udp.o b/Raspberry Pi/ip400spi/src/udp.o new file mode 100644 index 0000000..3f45c70 Binary files /dev/null and b/Raspberry Pi/ip400spi/src/udp.o differ