DVX_GUI/packet
2026-03-17 23:51:01 -05:00
..
Makefile Reliable, secure, serial link protocol added. 2026-03-10 21:05:52 -05:00
packet.c Much new documentation added as comments to code. 2026-03-17 23:51:01 -05:00
packet.h Much new documentation added as comments to code. 2026-03-17 23:51:01 -05:00
README.md Added Linux proxy server to test seclink. 2026-03-10 21:48:08 -05:00

Packet — Reliable Serial Transport

Packetized serial transport with HDLC-style framing, CRC-16 error detection, and a Go-Back-N sliding window protocol for reliable, ordered delivery over an unreliable serial link.

Architecture

 Application
     |
 [packet]     framing, CRC, retransmit, ordering
     |
 [rs232]      raw byte I/O

The packet layer sits on top of an already-open rs232 COM port. It does not open or close the serial port itself.

Frame Format

Before byte stuffing:

[0x7E] [SEQ] [TYPE] [LEN] [PAYLOAD...] [CRC_LO] [CRC_HI]
Field Size Description
0x7E 1 byte Frame delimiter (flag byte)
SEQ 1 byte Sequence number (wrapping uint8)
TYPE 1 byte Frame type (see below)
LEN 1 byte Payload length (0-255)
Payload 0-255 Application data
CRC 2 bytes CRC-16-CCITT over SEQ+TYPE+LEN+payload

Frame Types

Type Value Description
DATA 0x00 Data frame carrying application payload
ACK 0x01 Cumulative acknowledgment (next expected seq)
NAK 0x02 Negative ack (request retransmit from seq)
RST 0x03 Connection reset

Byte Stuffing

The flag byte (0x7E) and escape byte (0x7D) are escaped within frame data:

  • 0x7E becomes 0x7D 0x5E
  • 0x7D becomes 0x7D 0x5D

Reliability

The protocol uses Go-Back-N with a configurable sliding window (1-8 slots, default 4):

  • Sender assigns sequential numbers to each DATA frame and retains a copy in the retransmit buffer until acknowledged.
  • Receiver delivers frames in order. Out-of-order frames trigger a NAK for the expected sequence number.
  • ACK carries the next expected sequence number (cumulative).
  • NAK triggers retransmission of the requested frame and all subsequent unacknowledged frames.
  • Timer-based retransmit fires after 500 poll cycles if no ACK or NAK has been received.

API Reference

Types

// Receive callback — called for each verified, in-order packet
typedef void (*PktRecvCallbackT)(void *ctx, const uint8_t *data, int len);

// Opaque connection handle
typedef struct PktConnS PktConnT;

Constants

Name Value Description
PKT_MAX_PAYLOAD 255 Max payload bytes per packet
PKT_DEFAULT_WINDOW 4 Default sliding window size
PKT_MAX_WINDOW 8 Maximum sliding window size
PKT_SUCCESS 0 Success
PKT_ERR_INVALID_PORT -1 Invalid COM port
PKT_ERR_NOT_OPEN -2 Connection not open
PKT_ERR_ALREADY_OPEN -3 Connection already open
PKT_ERR_WOULD_BLOCK -4 Operation would block
PKT_ERR_OVERFLOW -5 Buffer overflow
PKT_ERR_INVALID_PARAM -6 Invalid parameter
PKT_ERR_TX_FULL -7 Transmit window full
PKT_ERR_NO_DATA -8 No data available

Functions

pktOpen

PktConnT *pktOpen(int com, int windowSize,
                  PktRecvCallbackT callback, void *callbackCtx);

Creates a packetized connection over an already-open COM port.

  • com — RS232 port index (RS232_COM1..RS232_COM4)
  • windowSize — sliding window size (1-8), 0 for default (4)
  • callback — called from pktPoll() for each received packet
  • callbackCtx — user pointer passed to callback

Returns a connection handle, or NULL on failure.

pktClose

void pktClose(PktConnT *conn);

Frees the connection state. Does not close the underlying COM port.

pktSend

int pktSend(PktConnT *conn, const uint8_t *data, int len, bool block);

Sends a packet. len must be 1..PKT_MAX_PAYLOAD.

  • block = true — waits for window space, polling for ACKs internally
  • block = false — returns PKT_ERR_TX_FULL if the window is full

The packet is stored in the retransmit buffer until acknowledged.

pktPoll

int pktPoll(PktConnT *conn);

Reads available serial data, processes received frames, sends ACKs and NAKs, and checks retransmit timers. Returns the number of DATA packets delivered to the callback.

Must be called frequently (e.g. in your main loop).

pktReset

int pktReset(PktConnT *conn);

Resets all sequence numbers and buffers to zero. Sends a RST frame to the remote side so it resets as well.

pktGetPending

int pktGetPending(PktConnT *conn);

Returns the number of unacknowledged packets currently in the transmit window. Useful for throttling sends in non-blocking mode.

Example

#include "packet.h"
#include "../rs232/rs232.h"

void onPacket(void *ctx, const uint8_t *data, int len) {
    // process received packet
}

int main(void) {
    // Open serial port first
    rs232Open(RS232_COM1, 115200, 8, 'N', 1, RS232_HANDSHAKE_NONE);

    // Create packet connection with default window size
    PktConnT *conn = pktOpen(RS232_COM1, 0, onPacket, NULL);

    // Send a packet (blocking)
    uint8_t msg[] = "Hello, packets!";
    pktSend(conn, msg, sizeof(msg), true);

    // Main loop
    while (1) {
        int delivered = pktPoll(conn);
        // delivered = number of packets received this iteration
    }

    pktClose(conn);
    rs232Close(RS232_COM1);
    return 0;
}

CRC

CRC-16-CCITT (polynomial 0x1021, init 0xFFFF) computed via a 256-entry lookup table (512 bytes). The CRC covers the SEQ, TYPE, LEN, and payload fields.

Building

make        # builds ../lib/libpacket.a
make clean  # removes objects and library

Requires librs232.a at link time.

Target: DJGPP cross-compiler, 486+ CPU.