.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_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