# 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_LO] [LEN_HI] [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` | 2 bytes | Payload length, little-endian | | Payload | 0-256 | 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 ```c // 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` | 256 | 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 ```c 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 ```c void pktClose(PktConnT *conn); ``` Frees the connection state. Does **not** close the underlying COM port. #### pktSend ```c 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 ```c 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 ```c 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 ```c int pktGetPending(PktConnT *conn); ``` Returns the number of unacknowledged packets currently in the transmit window. Useful for throttling sends in non-blocking mode. ## Example ```c #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.