98 lines
4 KiB
C
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
|