215 lines
6 KiB
Markdown
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.
|