ProtoCore v0.0.1
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
hislip.h
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file hislip.h
6 * @brief HiSLIP (High-Speed LAN Instrument Protocol) message codec (PC_ENABLE_HISLIP) - a zero-heap
7 * codec for the IVI Foundation's modern LXI instrument transport (IVI-6.1, HiSLIP 2.0) on
8 * TCP port 4880, the successor to VXI-11 that carries SCPI at higher throughput.
9 *
10 * A HiSLIP session runs over TWO TCP connections to the same port 4880 - a synchronous channel
11 * (the ordered SCPI command/response stream: Data / DataEND, Trigger, device-clear) and an
12 * asynchronous channel (out-of-band control: lock, status/SRQ, remote-local, interrupt) - bound by
13 * a 16-bit SessionID negotiated in the handshake.
14 *
15 * Every message is a fixed 16-byte header optionally followed by a payload:
16 * @code
17 * "HS" (2) MessageType (1) ControlCode (1) MessageParameter (4, BE) PayloadLength (8, BE)
18 * @endcode
19 * This codec builds + parses that header (@ref pc_hislip_build_header / @ref pc_hislip_parse_header),
20 * the Initialize / AsyncInitialize handshake (the MessageParameter carries the protocol version +
21 * vendor id, then the negotiated version + SessionID), and the Data / DataEND messages that carry a
22 * SCPI payload keyed by a MessageID. Pairs with @c PC_ENABLE_SCPI (the payload). Pure codec,
23 * host-tested; the two TCP connections are the application's.
24 *
25 * Reference: IVI-6.1 "IVI High-Speed LAN Instrument Protocol (HiSLIP)" v2.0 (2020-04-23).
26 *
27 * @author Douglas Quigg (dstroy0)
28 * @date 2026
29 */
30
31#ifndef PROTOCORE_HISLIP_H
32#define PROTOCORE_HISLIP_H
33
34#include "protocore_config.h"
35
36#if PC_ENABLE_HISLIP
37
38#include <stddef.h>
39#include <stdint.h>
40
41/** @brief The IANA-assigned HiSLIP TCP port (both channels connect here). */
42#define PC_HISLIP_PORT 4880
43
44/** @brief The fixed header length: prologue(2) + type(1) + control(1) + parameter(4) + length(8). */
45#define PC_HISLIP_HEADER_LEN 16
46
47/** @brief Protocol version words (`<major><minor>`), encoded in the high 16 bits of the handshake
48 * MessageParameter. The client offers its max; the server returns min(client, server). */
49#define PC_HISLIP_VERSION_1_0 0x0100
50#define PC_HISLIP_VERSION_1_1 0x0101
51#define PC_HISLIP_VERSION_2_0 0x0200
52
53/** @brief The MessageID a client starts at; each subsequent Data/DataEND/Trigger increments by 2
54 * (unsigned 32-bit, wraps). A server response echoes the request's MessageID. */
55#define PC_HISLIP_MESSAGE_ID_INIT 0xFFFFFF00u
56
57// ControlCode bits carried by an InitializeResponse:
58#define PC_HISLIP_INITRESP_OVERLAP 0x01 ///< bit 0: prefer overlapped (vs synchronized) mode
59#define PC_HISLIP_INITRESP_ENC_MANDATORY 0x02 ///< bit 1: encryption mandatory (2.0)
60#define PC_HISLIP_INITRESP_ENC_INITIAL 0x04 ///< bit 2: initial encryption required (2.0)
61// ControlCode bit carried by a Data or DataEND message:
62#define PC_HISLIP_DATA_RMT_DELIVERED 0x01 ///< bit 0: message delivered following a Response Message Terminator
63
64/** @brief HiSLIP MessageType codes (IVI-6.1). Codes 0-24 are HiSLIP 1.x; 25-38 were added in 2.0. */
65enum class HislipMsg : uint8_t
66{
67 INITIALIZE = 0,
68 INITIALIZE_RESPONSE = 1,
69 FATAL_ERROR = 2,
70 ERROR = 3,
71 ASYNC_LOCK = 4,
72 ASYNC_LOCK_RESPONSE = 5,
73 DATA = 6,
74 DATA_END = 7,
75 DEVICE_CLEAR_COMPLETE = 8,
76 DEVICE_CLEAR_ACKNOWLEDGE = 9,
77 ASYNC_REMOTE_LOCAL_CONTROL = 10,
78 ASYNC_REMOTE_LOCAL_RESPONSE = 11,
79 TRIGGER = 12,
80 INTERRUPTED = 13,
81 ASYNC_INTERRUPTED = 14,
82 ASYNC_MAX_MSG_SIZE = 15,
83 ASYNC_MAX_MSG_SIZE_RESPONSE = 16,
84 ASYNC_INITIALIZE = 17,
85 ASYNC_INITIALIZE_RESPONSE = 18,
86 ASYNC_DEVICE_CLEAR = 19,
87 ASYNC_SERVICE_REQUEST = 20,
88 ASYNC_STATUS_QUERY = 21,
89 ASYNC_STATUS_RESPONSE = 22,
90 ASYNC_DEVICE_CLEAR_ACKNOWLEDGE = 23,
91 ASYNC_LOCK_INFO = 24,
92 ASYNC_LOCK_INFO_RESPONSE = 25,
93 GET_DESCRIPTORS = 26,
94 GET_DESCRIPTORS_RESPONSE = 27,
95 START_TLS = 28,
96 ASYNC_START_TLS = 29,
97 ASYNC_START_TLS_RESPONSE = 30,
98 END_TLS = 31,
99 ASYNC_END_TLS = 32,
100 ASYNC_END_TLS_RESPONSE = 33,
101 GET_SASL_MECHANISM_LIST = 34,
102 GET_SASL_MECHANISM_LIST_RESPONSE = 35,
103 AUTHENTICATION_START = 36,
104 AUTHENTICATION_EXCHANGE = 37,
105 AUTHENTICATION_RESULT = 38,
106};
107
108/** @brief A decoded HiSLIP header. */
109struct HislipHeader
110{
111 HislipMsg type;
112 uint8_t control; ///< ControlCode (message-specific flag; 0 when undefined)
113 uint32_t parameter; ///< MessageParameter (message-specific; 0 when undefined)
114 uint64_t payload_len; ///< byte length of the payload that follows the 16-byte header
115};
116
117/**
118 * @brief Build the 16-byte header into @p buf.
119 * @return 16 (@ref PC_HISLIP_HEADER_LEN), or 0 if @p cap < 16 or @p buf is null.
120 */
121size_t pc_hislip_build_header(uint8_t *buf, size_t cap, HislipMsg type, uint8_t control, uint32_t parameter,
122 uint64_t payload_len);
123
124/**
125 * @brief Parse a 16-byte header from the head of [buf, buf+len).
126 * @return true on a valid `"HS"` prologue with @p len >= 16; false otherwise.
127 * @note The message type is copied through even if beyond 38 (forward-compat); the caller decides.
128 */
129bool pc_hislip_parse_header(const uint8_t *buf, size_t len, HislipHeader *out);
130
131// ── handshake builders ─────────────────────────────────────────────────────────────────────────
132
133/**
134 * @brief Build an Initialize message (client -> server, sync channel): parameter = (version << 16)
135 * | vendor_id, payload = the sub-address string (e.g. "hislip0").
136 * @return total bytes written (16 + sub-address length), or 0 on overflow / bad input.
137 */
138size_t pc_hislip_build_initialize(uint8_t *buf, size_t cap, uint16_t protocol_version, uint16_t vendor_id,
139 const char *sub_address);
140
141/**
142 * @brief Build an InitializeResponse (server -> client): control (overlap / encryption bits),
143 * parameter = (negotiated version << 16) | session_id, no payload.
144 * @return 16, or 0 on overflow.
145 */
146size_t pc_hislip_build_initialize_response(uint8_t *buf, size_t cap, uint8_t control, uint16_t protocol_version,
147 uint16_t session_id);
148
149/**
150 * @brief Build an AsyncInitialize (client -> server, async channel): parameter = session_id, no payload.
151 * @return 16, or 0 on overflow.
152 */
153size_t pc_hislip_build_async_initialize(uint8_t *buf, size_t cap, uint16_t session_id);
154
155/**
156 * @brief Build an AsyncInitializeResponse (server -> client): parameter = server_vendor_id, no payload.
157 * @return 16, or 0 on overflow.
158 */
159size_t pc_hislip_build_async_initialize_response(uint8_t *buf, size_t cap, uint8_t control, uint16_t server_vendor_id);
160
161/**
162 * @brief Build a Data (@p is_end false) or DataEND (@p is_end true) message carrying @p payload
163 * keyed by @p message_id (parameter). @p control is usually 0 (set @ref
164 * PC_HISLIP_DATA_RMT_DELIVERED on a server response after a terminator).
165 * @return total bytes written (16 + payload_len), or 0 on overflow / bad input.
166 */
167size_t pc_hislip_build_data(uint8_t *buf, size_t cap, bool is_end, uint8_t control, uint32_t message_id,
168 const uint8_t *payload, size_t payload_len);
169
170/** @brief The next client MessageID (increments by 2, wraps) - see @ref PC_HISLIP_MESSAGE_ID_INIT. */
171uint32_t pc_hislip_next_message_id(uint32_t id);
172
173// ── handshake parsers ──────────────────────────────────────────────────────────────────────────
174
175/** @brief A decoded Initialize message. @ref sub_address points INTO the source buffer. */
176struct HislipInitialize
177{
178 uint16_t protocol_version;
179 uint16_t vendor_id;
180 const char *sub_address;
181 size_t sub_address_len;
182};
183
184/**
185 * @brief Parse a full Initialize message (header + payload) from [buf, buf+len).
186 * @return true on a complete, well-formed Initialize; false otherwise.
187 */
188bool pc_hislip_parse_initialize(const uint8_t *buf, size_t len, HislipInitialize *out);
189
190/** @brief A decoded InitializeResponse message. */
191struct HislipInitializeResponse
192{
193 uint16_t protocol_version;
194 uint16_t session_id;
195 bool overlap; ///< ControlCode bit 0 (prefer overlapped)
196 bool encryption_mandatory; ///< ControlCode bit 1 (2.0)
197};
198
199/**
200 * @brief Parse an InitializeResponse header from [buf, buf+len).
201 * @return true on a well-formed InitializeResponse; false otherwise.
202 */
203bool pc_hislip_parse_initialize_response(const uint8_t *buf, size_t len, HislipInitializeResponse *out);
204
205#endif // PC_ENABLE_HISLIP
206
207#endif // PROTOCORE_HISLIP_H
User-facing configuration for ProtoCore.