.section Libraries
.topic lib.serial
.title Serial Stack
.toc 0 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_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
