DVX_GUI/packet/README.md

215 lines
6 KiB
Markdown

# 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
```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` | 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
```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.