DVX_GUI/packet/packet.h

98 lines
4 KiB
C

// Packetized serial transport with HDLC-style framing and sliding window
// Provides reliable, ordered delivery over an unreliable serial link.
//
// Why HDLC framing:
// HDLC's flag-byte + byte-stuffing scheme is the simplest way to delimit
// variable-length frames on a raw byte stream. The 0x7E flag byte marks
// frame boundaries; byte-stuffing (escaping 0x7E and 0x7D within data)
// ensures the flag is unambiguous. This is proven, lightweight, and
// requires zero buffering at the framing layer. Alternative approaches
// like length-prefixed framing are fragile on noisy links because a
// corrupted length field desynchronizes the receiver permanently.
//
// Why Go-Back-N ARQ:
// Go-Back-N is simpler than Selective Repeat (no out-of-order reassembly
// buffer needed on the receiver) and works well for the low bandwidth-delay
// product of a serial link. On a 115200 bps local serial connection, the
// round-trip time is negligible, so the window rarely fills. GBN's
// retransmit-all-from-NAK behavior wastes bandwidth on lossy links, but
// serial links are nearly lossless -- the CRC check is primarily a safety
// net for electrical noise, not a routine error recovery mechanism.
//
// CRC-16-CCITT:
// Standard polynomial used in HDLC/X.25. Table-driven for speed
// (256-entry lookup table trades 512 bytes of ROM for ~10x faster CRC
// than the bit-by-bit method, important on a 486).
#ifndef PACKET_H
#define PACKET_H
#include <stdint.h>
#include <stdbool.h>
// Maximum payload per packet (excluding header/CRC). 255 bytes fits in a
// single LEN byte and keeps total frame size manageable for serial buffers.
#define PKT_MAX_PAYLOAD 255
// Default sliding window size (1-8). 4 allows up to 4 unacknowledged
// frames in flight, providing good throughput without excessive buffering.
#define PKT_DEFAULT_WINDOW 4
// Maximum window size. Limited to 8 because sequence numbers are 8-bit
// and we need seq space > 2*window to avoid ambiguity (255 > 2*8).
#define PKT_MAX_WINDOW 8
// Error codes
#define PKT_SUCCESS 0
#define PKT_ERR_INVALID_PORT -1
#define PKT_ERR_NOT_OPEN -2
#define PKT_ERR_ALREADY_OPEN -3
#define PKT_ERR_WOULD_BLOCK -4
#define PKT_ERR_OVERFLOW -5
#define PKT_ERR_INVALID_PARAM -6
#define PKT_ERR_TX_FULL -7
#define PKT_ERR_NO_DATA -8
#define PKT_ERR_DISCONNECTED -9
// Callback for received packets
// ctx: user context pointer
// data: payload (valid only during callback)
// len: payload length
typedef void (*PktRecvCallbackT)(void *ctx, const uint8_t *data, int len);
// Connection handle (opaque)
typedef struct PktConnS PktConnT;
// Open a packetized connection over a COM port.
// com: RS232 COM port index (RS232_COM1..RS232_COM4), must already be open
// windowSize: sliding window size (1..PKT_MAX_WINDOW), 0 for default
// callback: called when a complete, verified packet is received
// callbackCtx: user pointer passed to callback
// Returns connection handle, or 0 on failure.
PktConnT *pktOpen(int com, int windowSize, PktRecvCallbackT callback, void *callbackCtx);
// Close a packetized connection. Does not close the underlying COM port.
void pktClose(PktConnT *conn);
// Send a packet. Returns PKT_SUCCESS or error.
// Blocks if the transmit window is full and block is true.
// Returns PKT_ERR_TX_FULL if window is full and block is false.
int pktSend(PktConnT *conn, const uint8_t *data, int len, bool block);
// Poll for incoming data, process received frames, handle retransmits.
// Must be called frequently (e.g. in your main loop).
// Returns the number of valid data packets delivered to the callback.
int pktPoll(PktConnT *conn);
// Reset the connection state (sequence numbers, buffers).
// Sends a RST frame to the remote side.
int pktReset(PktConnT *conn);
// Check if there is room in the transmit window.
bool pktCanSend(PktConnT *conn);
// Get number of unacknowledged packets in the transmit window.
int pktGetPending(PktConnT *conn);
#endif