ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
vxi11.h
Go to the documentation of this file.
1// ProtoCore v1.0.16 - Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file vxi11.h
6 * @brief VXI-11 (TCP/IP Instrument Protocol) codec over ONC RPC / XDR (PROTOCORE_ENABLE_VXI11) - a
7 * zero-heap codec for the legacy LXI instrument transport that predates HiSLIP.
8 *
9 * VXI-11 rides on ONC RPC (Sun RPC, RFC 5531) with XDR (RFC 4506) over TCP. A session:
10 * portmap GETPORT(0x0607AF, 1, TCP) -> the DEVICE_CORE port ; connect there ;
11 * create_link("inst0") -> a link id ; device_write("*IDN?\n") ; device_read() -> the reply ;
12 * destroy_link().
13 *
14 * This codec provides the reusable ONC-RPC framing (the TCP record-marking header + the CALL /
15 * accepted-REPLY message headers with AUTH_NONE) over XDR (big-endian, 4-byte aligned,
16 * length-prefixed opaque/string - no sub-word types on the wire), plus the VXI-11 DEVICE_CORE
17 * procedure builders + response parsers and the portmapper GETPORT call. Pairs with @c
18 * PROTOCORE_ENABLE_SCPI (the payload). Pure codec, host-tested; the TCP connection is the application's.
19 *
20 * References: VXI-11 (VXIbus Consortium, 1995); RFC 5531 (ONC RPC), RFC 4506 (XDR), RFC 1833 (portmap).
21 *
22 * @author Douglas Quigg (dstroy0)
23 * @date 2026
24 */
25
26#ifndef PROTOCORE_VXI11_H
27#define PROTOCORE_VXI11_H
28
29#include "protocore_config.h" // the entry point: protocore_types.h for the widths
30
31#if PROTOCORE_ENABLE_VXI11
32
34
35// ── ONC RPC / portmapper / VXI-11 program constants ─────────────────────────────────────────────
36#define PROTOCORE_VXI11_CORE_PROG 0x0607AFu ///< DEVICE_CORE RPC program number
37#define PROTOCORE_VXI11_CORE_VERS 1 ///< DEVICE_CORE version
38#define PROTOCORE_RPC_PMAP_PROG 100000u ///< portmapper / rpcbind v2 program
39#define PROTOCORE_RPC_PMAP_VERS 2
40#define PROTOCORE_RPC_PMAP_PORT 111 ///< the well-known portmapper TCP port
41#define PROTOCORE_RPC_PMAP_GETPORT 3 ///< PMAPPROC_GETPORT
42#define PROTOCORE_RPC_PROTO_TCP 6 ///< IPPROTO_TCP (for the GETPORT mapping)
43#define PROTOCORE_RPC_AUTH_NONE 0 ///< the AUTH_NONE auth flavor
44#define PROTOCORE_RPC_MSG_ACCEPTED 0 ///< reply_stat: the call was accepted
45#define PROTOCORE_RPC_ACCEPT_SUCCESS 0 ///< accept_stat: the procedure ran
46
47/** @brief VXI-11 DEVICE_CORE procedure numbers (procs 21 and 24 are intentionally unused). */
48typedef enum PROTO_ENUM_PACKED
49{
50 VXI11_PROC_CREATE_LINK = 10,
51 VXI11_PROC_DEVICE_WRITE = 11,
52 VXI11_PROC_DEVICE_READ = 12,
53 VXI11_PROC_DEVICE_READSTB = 13,
54 VXI11_PROC_DEVICE_TRIGGER = 14,
55 VXI11_PROC_DEVICE_CLEAR = 15,
56 VXI11_PROC_DEVICE_REMOTE = 16,
57 VXI11_PROC_DEVICE_LOCAL = 17,
58 VXI11_PROC_DEVICE_LOCK = 18,
59 VXI11_PROC_DEVICE_UNLOCK = 19,
60 VXI11_PROC_DEVICE_ENABLE_SRQ = 20,
61 VXI11_PROC_DEVICE_DOCMD = 22,
62 VXI11_PROC_DESTROY_LINK = 23,
63 VXI11_PROC_CREATE_INTR_CHAN = 25,
64 VXI11_PROC_DESTROY_INTR_CHAN = 26,
65} Vxi11Proc;
66
67// Device_Flags bits:
68#define PROTOCORE_VXI11_FLAG_WAITLOCK 0x01 ///< block up to lock_timeout on lock contention
69#define PROTOCORE_VXI11_FLAG_END 0x08 ///< (write) assert END/EOI with the last byte
70#define PROTOCORE_VXI11_FLAG_TERMCHRSET 0x80 ///< (read) termChar is an active read terminator
71// device_read `reason` return bits (may combine):
72#define PROTOCORE_VXI11_REASON_REQCNT 0x01 ///< requestSize bytes were transferred
73#define PROTOCORE_VXI11_REASON_CHR 0x02 ///< the termChar was seen
74#define PROTOCORE_VXI11_REASON_END 0x04 ///< an END/EOI indicator was received
75// Device_ErrorCode (common values):
76#define PROTOCORE_VXI11_ERR_NONE 0
77#define PROTOCORE_VXI11_ERR_SYNTAX 1
78#define PROTOCORE_VXI11_ERR_NOT_ACCESSIBLE 3
79#define PROTOCORE_VXI11_ERR_INVALID_LINK 4
80#define PROTOCORE_VXI11_ERR_PARAMETER 5
81#define PROTOCORE_VXI11_ERR_NO_LOCK 12
82#define PROTOCORE_VXI11_ERR_IO_TIMEOUT 15
83#define PROTOCORE_VXI11_ERR_IO_ERROR 17
84#define PROTOCORE_VXI11_ERR_ABORT 23
85
86/** @brief A short human-readable string for a Device_ErrorCode (static; never null). */
87const char *protocore_vxi11_error_str(int32_t error);
88
89// ── reusable ONC RPC framing ────────────────────────────────────────────────────────────────────
90
91/**
92 * @brief Write the 4-byte TCP record-marking header for a single-fragment message: the high bit is
93 * the last-fragment flag (always set here), the low 31 bits are @p payload_len.
94 * @return 4, or 0 if @p cap < 4 or @p payload_len exceeds 31 bits.
95 */
96size_t protocore_rpc_record_mark(uint8_t *buf, size_t cap, uint32_t payload_len);
97
98/**
99 * @brief Parse a 4-byte record-marking header.
100 * @param last receives the last-fragment flag.
101 * @param frag_len receives the fragment payload byte length (excludes the 4 RM bytes).
102 * @return true if @p len >= 4; false otherwise.
103 */
104proto_bool protocore_rpc_parse_record_mark(const uint8_t *buf, size_t len, proto_bool *last, uint32_t *frag_len);
105
106/**
107 * @brief Parse an accepted ONC-RPC reply header (the bytes AFTER the record mark): xid, REPLY,
108 * MSG_ACCEPTED, verf, accept_stat.
109 * @param xid receives the transaction id (echoes the call).
110 * @param accept_stat receives the accept_stat (0 = SUCCESS; the caller checks before reading results).
111 * @param result_off receives the offset (into @p rpc) where the procedure results begin.
112 * @return true on a well-formed accepted reply; false if truncated, not a REPLY, or MSG_DENIED.
113 */
114proto_bool protocore_rpc_parse_reply(const uint8_t *rpc, size_t len, uint32_t *xid, uint32_t *accept_stat,
115 size_t *result_off);
116
117// ── portmapper ──────────────────────────────────────────────────────────────────────────────────
118
119/**
120 * @brief Build a PMAPPROC_GETPORT call (with record mark): maps (prog, vers, proto) to a TCP port.
121 * @return total bytes written (incl. the 4-byte record mark), or 0 on overflow.
122 */
123size_t protocore_vxi11_build_getport(uint8_t *buf, size_t cap, uint32_t xid, uint32_t prog, uint32_t vers,
124 uint32_t proto);
125
126/**
127 * @brief Parse a GETPORT reply (bytes after the record mark). @p port is 0 if not registered.
128 * @return true on a well-formed successful reply; false otherwise.
129 */
130proto_bool protocore_vxi11_parse_getport_resp(const uint8_t *rpc, size_t len, uint32_t *port);
131
132// ── VXI-11 DEVICE_CORE ──────────────────────────────────────────────────────────────────────────
133
134/**
135 * @brief Build a create_link call: open a link to @p device (e.g. "inst0"). @p client_id is
136 * caller-chosen; @p lock_device requests an exclusive lock.
137 * @return total bytes written (incl. record mark), or 0 on overflow / bad input.
138 */
139size_t protocore_vxi11_build_create_link(uint8_t *buf, size_t cap, uint32_t xid, int32_t client_id,
140 proto_bool lock_device, uint32_t lock_timeout, const char *device);
141
142/** @brief A decoded Create_LinkResp. */
143typedef struct
144{
145 int32_t error; ///< Device_ErrorCode (0 = no error)
146 int32_t lid; ///< the link id, for subsequent calls
147 uint32_t abort_port; ///< the DEVICE_ASYNC abort-channel port
148 uint32_t max_recv_size; ///< the largest data block the device accepts in one device_write
149} Vxi11CreateLinkResp;
150proto_bool protocore_vxi11_parse_create_link_resp(const uint8_t *rpc, size_t len, Vxi11CreateLinkResp *out);
151
152/**
153 * @brief Build a device_write call: write @p data (e.g. a SCPI command) to link @p lid. Set
154 * @ref PROTOCORE_VXI11_FLAG_END in @p flags to assert END with the last byte.
155 * @return total bytes written (incl. record mark), or 0 on overflow / bad input.
156 */
157size_t protocore_vxi11_build_device_write(uint8_t *buf, size_t cap, uint32_t xid, int32_t lid, uint32_t io_timeout,
158 uint32_t lock_timeout, uint32_t flags, const uint8_t *data, size_t data_len);
159
160/** @brief A decoded Device_WriteResp. */
161typedef struct
162{
163 int32_t error;
164 uint32_t size; ///< number of bytes written
165} Vxi11WriteResp;
166proto_bool protocore_vxi11_parse_write_resp(const uint8_t *rpc, size_t len, Vxi11WriteResp *out);
167
168/**
169 * @brief Build a device_read call: read up to @p request_size bytes from link @p lid. Set
170 * @ref PROTOCORE_VXI11_FLAG_TERMCHRSET in @p flags to stop at @p term_char.
171 * @return total bytes written (incl. record mark), or 0 on overflow.
172 */
173size_t protocore_vxi11_build_device_read(uint8_t *buf, size_t cap, uint32_t xid, int32_t lid, uint32_t request_size,
174 uint32_t io_timeout, uint32_t lock_timeout, uint32_t flags, uint8_t term_char);
175
176/** @brief A decoded Device_ReadResp. @ref data points INTO @p rpc. */
177typedef struct
178{
179 int32_t error;
180 int32_t reason; ///< OR of PROTOCORE_VXI11_REASON_* (why the read ended)
181 const uint8_t *data;
182 size_t data_len;
183} Vxi11ReadResp;
184proto_bool protocore_vxi11_parse_read_resp(const uint8_t *rpc, size_t len, Vxi11ReadResp *out);
185
186/**
187 * @brief Build a device_readstb call: read the IEEE 488.2 status byte from link @p lid.
188 * @return total bytes written (incl. record mark), or 0 on overflow.
189 */
190size_t protocore_vxi11_build_device_readstb(uint8_t *buf, size_t cap, uint32_t xid, int32_t lid, uint32_t flags,
191 uint32_t lock_timeout, uint32_t io_timeout);
192
193/** @brief A decoded Device_ReadStbResp. */
194typedef struct
195{
196 int32_t error;
197 uint8_t stb; ///< the status byte
198} Vxi11ReadStbResp;
199proto_bool protocore_vxi11_parse_readstb_resp(const uint8_t *rpc, size_t len, Vxi11ReadStbResp *out);
200
201/**
202 * @brief Build a device_clear call: clear link @p lid's device (the protocol-level Selected Device Clear -
203 * it empties the instrument's input / output buffers). Carries the Device_GenericParms (lid + flags +
204 * the lock / io timeouts), as device_readstb does. @return total bytes written, or 0 on overflow.
205 */
206size_t protocore_vxi11_build_device_clear(uint8_t *buf, size_t cap, uint32_t xid, int32_t lid, uint32_t flags,
207 uint32_t lock_timeout, uint32_t io_timeout);
208
209/**
210 * @brief Build a device_trigger call: assert a trigger on link @p lid (the protocol-level Group Execute
211 * Trigger, i.e. IEEE 488.2 `*TRG`). Same Device_GenericParms as device_clear. @return bytes, or 0.
212 */
213size_t protocore_vxi11_build_device_trigger(uint8_t *buf, size_t cap, uint32_t xid, int32_t lid, uint32_t flags,
214 uint32_t lock_timeout, uint32_t io_timeout);
215
216/**
217 * @brief Build a destroy_link call: close link @p lid.
218 * @return total bytes written (incl. record mark), or 0 on overflow.
219 */
220size_t protocore_vxi11_build_destroy_link(uint8_t *buf, size_t cap, uint32_t xid, int32_t lid);
221
222/**
223 * @brief Parse a bare Device_Error result (destroy_link / device_trigger / device_clear / ...).
224 * @param error receives the Device_ErrorCode.
225 * @return true on a well-formed successful reply; false otherwise.
226 */
227proto_bool protocore_vxi11_parse_error_resp(const uint8_t *rpc, size_t len, int32_t *error);
228
230
231#endif // PROTOCORE_ENABLE_VXI11
232
233#endif // PROTOCORE_VXI11_H
PROTO_ENUM_PACKED
Application protocol spoken on a listener port or connection slot.
#define PROTOCORE_BEGIN_DECLS
Give a header's declarations C linkage, so their symbol names carry no parameter types.
Definition types.h:96
_Bool proto_bool
The truth value.
Definition types.h:64
#define PROTOCORE_END_DECLS
Definition types.h:97