DVX_GUI/serial/serial.dhs

644 lines
20 KiB
Text

.topic lib.serial
.title Serial Stack
.toc 1 Serial Stack
.index Serial
.index RS-232
.index HDLC
.index XTEA
.index Diffie-Hellman
.index secLink
.h1 Serial Stack
The DVX serial/networking stack provides reliable, optionally encrypted communication over RS-232 serial ports. It is composed of four layers, each building on the one below:
.list
.item rs232 -- ISR-driven UART driver with ring buffers and flow control
.item packet -- HDLC framing, CRC-16, Go-Back-N ARQ (reliable delivery)
.item security -- 1024-bit Diffie-Hellman key exchange, XTEA-CTR cipher, DRBG RNG
.item secLink -- Convenience wrapper: channel multiplexing, per-packet encryption
.endlist
Loaded as: bin/libs/kpunch/serial/serial.lib
.link lib.serial.rs232 Layer 1: RS-232 UART Driver
.link lib.serial.packet Layer 2: Packet Transport
.link lib.serial.security Layer 3: Security (DH + XTEA)
.link lib.serial.seclink Layer 4: Secure Link
.topic lib.serial.rs232
.title RS-232 UART Driver
.toc 1 RS-232 UART Driver
.index rs232
.index UART
.index COM Port
.index rs232Open
.index rs232Close
.index rs232Read
.index rs232Write
.h1 RS-232 UART Driver
ISR-driven serial port driver supporting up to 4 simultaneous COM ports. A shared ISR drains UART FIFOs into per-port ring buffers (2048 bytes, power-of-2 for fast index wrapping). Flow control (XON/XOFF, RTS/CTS, DTR/DSR) operates within the ISR using watermark thresholds. All ISR data structures are DPMI-locked to prevent page faults.
Header: rs232/rs232.h
.h2 Port Constants
.table
Constant Value Description
-------- ----- -----------
RS232_COM1 0 First serial port.
RS232_COM2 1 Second serial port.
RS232_COM3 2 Third serial port.
RS232_COM4 3 Fourth serial port.
.endtable
.h2 Handshake Modes
.table
Constant Value Description
-------- ----- -----------
RS232_HANDSHAKE_NONE 0 No flow control.
RS232_HANDSHAKE_XONXOFF 1 Software flow control (XON/XOFF characters).
RS232_HANDSHAKE_RTSCTS 2 Hardware flow control via RTS/CTS lines.
RS232_HANDSHAKE_DTRDSR 3 Hardware flow control via DTR/DSR lines.
.endtable
.h2 UART Types
Detected automatically by probing the scratch register and FIFO capability.
.table
Constant Value Description
-------- ----- -----------
RS232_UART_UNKNOWN 0 Detection failed.
RS232_UART_8250 1 Original IBM PC UART. No FIFO.
RS232_UART_16450 2 Has scratch register, no FIFO.
RS232_UART_16550 3 Has FIFO but buggy (rare).
RS232_UART_16550A 4 Working 16-byte FIFO. Most common in 486+ hardware.
.endtable
.h2 Error Codes
.table
Constant Value Description
-------- ----- -----------
RS232_SUCCESS 0 Operation succeeded.
RS232_ERR_UNKNOWN -1 Unknown error.
RS232_ERR_NOT_OPEN -2 Port is not open.
RS232_ERR_ALREADY_OPEN -3 Port is already open.
RS232_ERR_NO_UART -4 No UART detected at port address.
RS232_ERR_INVALID_PORT -5 Invalid port number.
RS232_ERR_INVALID_BASE -6 Invalid base I/O address.
RS232_ERR_INVALID_IRQ -7 Invalid IRQ number.
RS232_ERR_INVALID_BPS -8 Invalid baud rate.
RS232_ERR_INVALID_DATA -9 Invalid data bits value.
RS232_ERR_INVALID_PARITY -10 Invalid parity character.
RS232_ERR_INVALID_STOP -11 Invalid stop bits value.
RS232_ERR_INVALID_HANDSHAKE -12 Invalid handshake mode.
RS232_ERR_INVALID_FIFO -13 Invalid FIFO threshold.
RS232_ERR_NULL_PTR -14 NULL pointer argument.
RS232_ERR_IRQ_NOT_FOUND -15 Could not detect IRQ for port.
RS232_ERR_LOCK_MEM -16 DPMI memory lock failed.
.endtable
.h2 rs232Open
Open a COM port with the specified line parameters.
.code
int rs232Open(int com, int32_t bps, int dataBits,
char parity, int stopBits, int handshake);
.endcode
.table
Parameter Description
--------- -----------
com Port index (RS232_COM1..RS232_COM4).
bps Baud rate (e.g. 9600, 19200, 38400, 57600, 115200).
dataBits Data bits per character (5, 6, 7, or 8).
parity Parity mode: 'N' (none), 'O' (odd), 'E' (even), 'M' (mark), 'S' (space).
stopBits Stop bits (1 or 2).
handshake Flow control mode (RS232_HANDSHAKE_*).
.endtable
Returns RS232_SUCCESS or a negative error code.
.h2 rs232Close
Close a COM port and release its ISR resources.
.code
int rs232Close(int com);
.endcode
.h2 rs232Read
Non-blocking read from the receive ring buffer.
.code
int rs232Read(int com, char *data, int len);
.endcode
.table
Parameter Description
--------- -----------
com Port index.
data Buffer to receive data.
len Maximum bytes to read.
.endtable
Returns the number of bytes actually read (may be less than len).
.h2 rs232Write
Blocking polled write directly to UART transmit holding register. Bypasses the TX ring buffer. Use for small, immediate writes.
.code
int rs232Write(int com, const char *data, int len);
.endcode
Returns RS232_SUCCESS or a negative error code.
.h2 rs232WriteBuf
Non-blocking write to the transmit ring buffer. The ISR drains buffered data to the UART asynchronously.
.code
int rs232WriteBuf(int com, const char *data, int len);
.endcode
Returns the number of bytes actually queued (may be less than len if the buffer is full).
.h2 rs232Set
Change all line parameters on an open port.
.code
int rs232Set(int com, int32_t bps, int dataBits,
char parity, int stopBits, int handshake);
.endcode
.h2 Configuration Getters
.table
Function Returns
-------- -------
rs232GetBase(com) I/O base address.
rs232GetBps(com) Current baud rate.
rs232GetData(com) Data bits setting.
rs232GetParity(com) Parity character ('N', 'O', 'E', 'M', 'S').
rs232GetStop(com) Stop bits setting.
rs232GetHandshake(com) Handshake mode.
rs232GetIrq(com) IRQ number.
rs232GetUartType(com) Detected UART type (RS232_UART_*).
.endtable
.h2 Status Getters
.table
Function Returns
-------- -------
rs232GetRxBuffered(com) Bytes waiting in the receive ring buffer.
rs232GetTxBuffered(com) Bytes waiting in the transmit ring buffer.
rs232GetCts(com) CTS line state (0 or 1).
rs232GetDsr(com) DSR line state (0 or 1).
rs232GetDtr(com) DTR line state (0 or 1).
rs232GetRts(com) RTS line state (0 or 1).
rs232GetLsr(com) Line status register value.
rs232GetMcr(com) Modem control register value.
rs232GetMsr(com) Modem status register value.
.endtable
.h2 Configuration Setters
.table
Function Description
-------- -----------
rs232SetBase(com, base) Set I/O base address (before open).
rs232SetBps(com, bps) Change baud rate.
rs232SetData(com, dataBits) Change data bits.
rs232SetParity(com, parity) Change parity mode.
rs232SetStop(com, stopBits) Change stop bits.
rs232SetHandshake(com, handshake) Change flow control mode.
rs232SetIrq(com, irq) Set IRQ number (before open).
rs232SetMcr(com, mcr) Set modem control register directly.
rs232SetDtr(com, dtr) Set DTR line state.
rs232SetRts(com, rts) Set RTS line state.
rs232SetFifoThreshold(com, thr) Set FIFO trigger level (1, 4, 8, or 14 bytes).
.endtable
.h2 Buffer Management
.table
Function Description
-------- -----------
rs232ClearRxBuffer(com) Discard all data in the receive ring buffer.
rs232ClearTxBuffer(com) Discard all data in the transmit ring buffer.
.endtable
.topic lib.serial.packet
.title Packet Transport
.toc 1 Packet Transport
.index packet
.index HDLC
.index CRC-16
.index Go-Back-N
.index pktOpen
.index pktClose
.index pktSend
.index pktPoll
.h1 Packet Transport
Reliable, ordered packet delivery over an RS-232 serial link. Uses HDLC-style byte-stuffed framing for frame delimiting, CRC-16-CCITT for error detection, and Go-Back-N ARQ with a configurable sliding window for retransmission.
Header: packet/packet.h
.h2 Constants
.table
Constant Value Description
-------- ----- -----------
PKT_MAX_PAYLOAD 255 Maximum bytes per packet payload.
PKT_DEFAULT_WINDOW 4 Default sliding window size (unacknowledged frames in flight).
PKT_MAX_WINDOW 8 Maximum sliding window size.
.endtable
.h2 Error Codes
.table
Constant Value Description
-------- ----- -----------
PKT_SUCCESS 0 Operation succeeded.
PKT_ERR_INVALID_PORT -1 Invalid COM port index.
PKT_ERR_NOT_OPEN -2 Connection is not open.
PKT_ERR_ALREADY_OPEN -3 Connection is 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 is full.
PKT_ERR_NO_DATA -8 No data available.
PKT_ERR_DISCONNECTED -9 Remote side disconnected.
.endtable
.h2 PktRecvCallbackT
Callback type for received packets.
.code
typedef void (*PktRecvCallbackT)(void *ctx,
const uint8_t *data, int len);
.endcode
.table
Parameter Description
--------- -----------
ctx User context pointer (passed to pktOpen).
data Payload bytes (valid only during the callback).
len Payload length.
.endtable
.h2 pktOpen
Open a packetized connection over an already-open COM port.
.code
PktConnT *pktOpen(int com, int windowSize,
PktRecvCallbackT callback, void *callbackCtx);
.endcode
.table
Parameter Description
--------- -----------
com COM port index (RS232_COM1..RS232_COM4). Must already be open via rs232Open.
windowSize Sliding window size (1..PKT_MAX_WINDOW). Pass 0 for PKT_DEFAULT_WINDOW.
callback Called when a complete, CRC-verified packet is received.
callbackCtx User pointer passed to the callback.
.endtable
Returns an opaque PktConnT handle, or NULL on failure.
.h2 pktClose
Close a packetized connection. Does not close the underlying COM port.
.code
void pktClose(PktConnT *conn);
.endcode
.h2 pktSend
Send a data packet.
.code
int pktSend(PktConnT *conn, const uint8_t *data,
int len, bool block);
.endcode
.table
Parameter Description
--------- -----------
conn Connection handle from pktOpen.
data Payload bytes (up to PKT_MAX_PAYLOAD).
len Payload length.
block If true, block until transmit window has space. If false, return PKT_ERR_TX_FULL when the window is full.
.endtable
Returns PKT_SUCCESS or a negative error code.
.h2 pktPoll
Poll for incoming data, process received frames, and handle retransmits. Must be called frequently in the main loop.
.code
int pktPoll(PktConnT *conn);
.endcode
Returns the number of valid data packets delivered to the callback.
.h2 pktReset
Reset the connection state (sequence numbers, buffers) and send a RST frame to the remote side.
.code
int pktReset(PktConnT *conn);
.endcode
.h2 pktCanSend
Check whether there is room in the transmit window for another packet.
.code
bool pktCanSend(PktConnT *conn);
.endcode
.h2 pktGetPending
Get the number of unacknowledged packets currently in the transmit window.
.code
int pktGetPending(PktConnT *conn);
.endcode
.topic lib.serial.security
.title Security (DH + XTEA)
.toc 1 Security (DH + XTEA)
.index Security
.index Diffie-Hellman
.index XTEA
.index XTEA-CTR
.index DRBG
.index secDhCreate
.index secCipherCreate
.index secRngBytes
.h1 Security (DH + XTEA)
Cryptographic primitives for the serial stack: 1024-bit Diffie-Hellman key exchange (RFC 2409 Group 2), XTEA block cipher in CTR mode, and an XTEA-CTR DRBG random number generator.
XTEA requires no lookup tables and compiles to approximately 20 instructions per round, making it suitable for 486-class hardware where AES S-box tables would thrash the 8KB cache.
Header: security/security.h
.h2 Constants
.table
Constant Value Description
-------- ----- -----------
SEC_DH_KEY_SIZE 128 Size of a DH public key in bytes (1024 bits).
SEC_XTEA_KEY_SIZE 16 Size of an XTEA key in bytes (128 bits).
.endtable
.h2 Error Codes
.table
Constant Value Description
-------- ----- -----------
SEC_SUCCESS 0 Operation succeeded.
SEC_ERR_PARAM -1 Invalid parameter.
SEC_ERR_NOT_READY -2 DH context not ready (keys not generated or secret not computed).
SEC_ERR_ALLOC -3 Memory allocation failed.
.endtable
.h2 RNG Functions
The RNG uses XTEA-CTR as a DRBG. Hardware entropy from PIT timer jitter is weak (~20 bits); supplement with keyboard timing or mouse jitter before generating DH keys.
.code
void secRngSeed(const uint8_t *entropy, int len);
int secRngGatherEntropy(uint8_t *buf, int len);
void secRngAddEntropy(const uint8_t *data, int len);
void secRngBytes(uint8_t *buf, int len);
.endcode
.table
Function Description
-------- -----------
secRngSeed Seed the DRBG from an entropy buffer.
secRngGatherEntropy Gather hardware entropy (PIT jitter, BIOS tick count) into a buffer.
secRngAddEntropy Mix additional entropy into the DRBG state.
secRngBytes Generate pseudorandom bytes.
.endtable
.h2 Diffie-Hellman Key Exchange
1024-bit DH with 256-bit private exponents using the RFC 2409 Group 2 safe prime.
.code
SecDhT *secDhCreate(void);
int secDhGenerateKeys(SecDhT *dh);
int secDhGetPublicKey(SecDhT *dh, uint8_t *buf, int *len);
int secDhComputeSecret(SecDhT *dh,
const uint8_t *remotePub, int len);
int secDhDeriveKey(SecDhT *dh, uint8_t *key, int keyLen);
void secDhDestroy(SecDhT *dh);
.endcode
.table
Function Description
-------- -----------
secDhCreate Allocate a new DH context.
secDhGenerateKeys Generate a private exponent and public key. RNG must be seeded first.
secDhGetPublicKey Copy the local public key into buf. *len receives the key size.
secDhComputeSecret Compute the shared secret from the remote public key.
secDhDeriveKey Derive a symmetric key (XTEA key) from the shared secret via hashing.
secDhDestroy Free the DH context and all associated memory.
.endtable
Typical usage order: secDhCreate, secDhGenerateKeys, exchange public keys, secDhComputeSecret, secDhDeriveKey, secDhDestroy.
.h2 XTEA-CTR Cipher
XTEA in counter mode. Encrypt and decrypt are the same operation (XOR with keystream). The counter must never repeat with the same key.
.code
SecCipherT *secCipherCreate(const uint8_t *key);
void secCipherSetNonce(SecCipherT *c,
uint32_t nonceLo, uint32_t nonceHi);
void secCipherCrypt(SecCipherT *c,
uint8_t *data, int len);
void secCipherDestroy(SecCipherT *c);
.endcode
.table
Function Description
-------- -----------
secCipherCreate Allocate a cipher context with a 128-bit key.
secCipherSetNonce Set the initial counter value (nonce). Must be unique per session.
secCipherCrypt Encrypt or decrypt data in place. The counter increments automatically.
secCipherDestroy Free the cipher context.
.endtable
.topic lib.serial.seclink
.title Secure Link
.toc 1 Secure Link
.index secLink
.index secLinkOpen
.index secLinkClose
.index secLinkHandshake
.index secLinkSend
.index secLinkPoll
.h1 Secure Link
Top-level API composing all three lower layers (rs232, packet, security) into a single interface. Provides channel multiplexing (0-127) and per-packet encryption control over a reliable serial link.
Each packet carries a one-byte header: bit 7 is the encrypted flag, bits 6..0 are the channel number. Unencrypted packets can be sent before the handshake completes (e.g., for version negotiation).
Header: seclink/secLink.h
.h2 Constants
.table
Constant Value Description
-------- ----- -----------
SECLINK_MAX_PAYLOAD 254 Maximum plaintext bytes per send (PKT_MAX_PAYLOAD minus 1-byte channel header).
SECLINK_MAX_CHANNEL 127 Highest valid channel number.
.endtable
.h2 Error Codes
.table
Constant Value Description
-------- ----- -----------
SECLINK_SUCCESS 0 Operation succeeded.
SECLINK_ERR_PARAM -1 Invalid parameter.
SECLINK_ERR_SERIAL -2 RS-232 layer error.
SECLINK_ERR_ALLOC -3 Memory allocation failed.
SECLINK_ERR_HANDSHAKE -4 DH handshake failed.
SECLINK_ERR_NOT_READY -5 Handshake not yet completed (encrypted send attempted).
SECLINK_ERR_SEND -6 Packet send failed.
.endtable
.h2 SecLinkRecvT
Receive callback type. Delivers decrypted plaintext with the channel number.
.code
typedef void (*SecLinkRecvT)(void *ctx,
const uint8_t *data, int len, uint8_t channel);
.endcode
.h2 secLinkOpen
Open a secure serial link. Opens the COM port and packet layer internally.
.code
SecLinkT *secLinkOpen(int com, int32_t bps, int dataBits,
char parity, int stopBits, int handshake,
SecLinkRecvT callback, void *ctx);
.endcode
.table
Parameter Description
--------- -----------
com COM port index (RS232_COM1..RS232_COM4).
bps Baud rate.
dataBits Data bits per character (5-8).
parity Parity mode ('N', 'O', 'E', 'M', 'S').
stopBits Stop bits (1 or 2).
handshake Flow control mode (RS232_HANDSHAKE_*).
callback Called when a packet is received (plaintext, with channel).
ctx User pointer passed to the callback.
.endtable
Returns an opaque SecLinkT handle, or NULL on failure.
.h2 secLinkClose
Close the link, free all resources, and close the COM port.
.code
void secLinkClose(SecLinkT *link);
.endcode
.h2 secLinkHandshake
Perform Diffie-Hellman key exchange. Blocks until both sides complete. The RNG must be seeded before calling this.
.code
int secLinkHandshake(SecLinkT *link);
.endcode
Returns SECLINK_SUCCESS or SECLINK_ERR_HANDSHAKE.
.h2 secLinkSend
Send data on a channel, optionally encrypted.
.code
int secLinkSend(SecLinkT *link, const uint8_t *data,
int len, uint8_t channel, bool encrypt, bool block);
.endcode
.table
Parameter Description
--------- -----------
link Connection handle from secLinkOpen.
data Plaintext payload (1..SECLINK_MAX_PAYLOAD bytes).
len Payload length.
channel Logical channel number (0..SECLINK_MAX_CHANNEL).
encrypt If true, encrypt before sending (requires completed handshake).
block If true, block until the transmit window has space.
.endtable
Returns SECLINK_SUCCESS or a negative error code.
.h2 secLinkSendBuf
Send an arbitrarily large buffer by splitting into SECLINK_MAX_PAYLOAD chunks. Always blocks until all data is sent.
.code
int secLinkSendBuf(SecLinkT *link, const uint8_t *data,
int len, uint8_t channel, bool encrypt);
.endcode
Returns SECLINK_SUCCESS or the first error encountered.
.h2 secLinkPoll
Poll for incoming data. Decrypts encrypted packets and delivers plaintext to the receive callback.
.code
int secLinkPoll(SecLinkT *link);
.endcode
Returns the number of packets delivered, or a negative error code.
.h2 secLinkIsReady
Check whether the handshake is complete and the link is ready for encrypted data.
.code
bool secLinkIsReady(SecLinkT *link);
.endcode
.h2 secLinkGetPending
Get the number of unacknowledged packets in the transmit window.
.code
int secLinkGetPending(SecLinkT *link);
.endcode