ProtoCore v0.0.1
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
vxi11.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 vxi11.h
6 * @brief VXI-11 (TCP/IP Instrument Protocol) codec over ONC RPC / XDR (PC_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 * PC_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"
30
31#if PC_ENABLE_VXI11
32
33#include <stddef.h>
34#include <stdint.h>
35
36// ── ONC RPC / portmapper / VXI-11 program constants ─────────────────────────────────────────────
37#define PC_VXI11_CORE_PROG 0x0607AFu ///< DEVICE_CORE RPC program number
38#define PC_VXI11_CORE_VERS 1 ///< DEVICE_CORE version
39#define PC_RPC_PMAP_PROG 100000u ///< portmapper / rpcbind v2 program
40#define PC_RPC_PMAP_VERS 2
41#define PC_RPC_PMAP_PORT 111 ///< the well-known portmapper TCP port
42#define PC_RPC_PMAP_GETPORT 3 ///< PMAPPROC_GETPORT
43#define PC_RPC_PROTO_TCP 6 ///< IPPROTO_TCP (for the GETPORT mapping)
44#define PC_RPC_AUTH_NONE 0 ///< the AUTH_NONE auth flavor
45#define PC_RPC_MSG_ACCEPTED 0 ///< reply_stat: the call was accepted
46#define PC_RPC_ACCEPT_SUCCESS 0 ///< accept_stat: the procedure ran
47
48/** @brief VXI-11 DEVICE_CORE procedure numbers (procs 21 and 24 are intentionally unused). */
49enum class Vxi11Proc : uint32_t
50{
51 CREATE_LINK = 10,
52 DEVICE_WRITE = 11,
53 DEVICE_READ = 12,
54 DEVICE_READSTB = 13,
55 DEVICE_TRIGGER = 14,
56 DEVICE_CLEAR = 15,
57 DEVICE_REMOTE = 16,
58 DEVICE_LOCAL = 17,
59 DEVICE_LOCK = 18,
60 DEVICE_UNLOCK = 19,
61 DEVICE_ENABLE_SRQ = 20,
62 DEVICE_DOCMD = 22,
63 DESTROY_LINK = 23,
64 CREATE_INTR_CHAN = 25,
65 DESTROY_INTR_CHAN = 26,
66};
67
68// Device_Flags bits:
69#define PC_VXI11_FLAG_WAITLOCK 0x01 ///< block up to lock_timeout on lock contention
70#define PC_VXI11_FLAG_END 0x08 ///< (write) assert END/EOI with the last byte
71#define PC_VXI11_FLAG_TERMCHRSET 0x80 ///< (read) termChar is an active read terminator
72// device_read `reason` return bits (may combine):
73#define PC_VXI11_REASON_REQCNT 0x01 ///< requestSize bytes were transferred
74#define PC_VXI11_REASON_CHR 0x02 ///< the termChar was seen
75#define PC_VXI11_REASON_END 0x04 ///< an END/EOI indicator was received
76// Device_ErrorCode (common values):
77#define PC_VXI11_ERR_NONE 0
78#define PC_VXI11_ERR_SYNTAX 1
79#define PC_VXI11_ERR_NOT_ACCESSIBLE 3
80#define PC_VXI11_ERR_INVALID_LINK 4
81#define PC_VXI11_ERR_PARAMETER 5
82#define PC_VXI11_ERR_NO_LOCK 12
83#define PC_VXI11_ERR_IO_TIMEOUT 15
84#define PC_VXI11_ERR_IO_ERROR 17
85#define PC_VXI11_ERR_ABORT 23
86
87/** @brief A short human-readable string for a Device_ErrorCode (static; never null). */
88const char *pc_vxi11_error_str(int32_t error);
89
90// ── reusable ONC RPC framing ────────────────────────────────────────────────────────────────────
91
92/**
93 * @brief Write the 4-byte TCP record-marking header for a single-fragment message: the high bit is
94 * the last-fragment flag (always set here), the low 31 bits are @p payload_len.
95 * @return 4, or 0 if @p cap < 4 or @p payload_len exceeds 31 bits.
96 */
97size_t pc_rpc_record_mark(uint8_t *buf, size_t cap, uint32_t payload_len);
98
99/**
100 * @brief Parse a 4-byte record-marking header.
101 * @param last receives the last-fragment flag.
102 * @param frag_len receives the fragment payload byte length (excludes the 4 RM bytes).
103 * @return true if @p len >= 4; false otherwise.
104 */
105bool pc_rpc_parse_record_mark(const uint8_t *buf, size_t len, bool *last, uint32_t *frag_len);
106
107/**
108 * @brief Parse an accepted ONC-RPC reply header (the bytes AFTER the record mark): xid, REPLY,
109 * MSG_ACCEPTED, verf, accept_stat.
110 * @param xid receives the transaction id (echoes the call).
111 * @param accept_stat receives the accept_stat (0 = SUCCESS; the caller checks before reading results).
112 * @param result_off receives the offset (into @p rpc) where the procedure results begin.
113 * @return true on a well-formed accepted reply; false if truncated, not a REPLY, or MSG_DENIED.
114 */
115bool pc_rpc_parse_reply(const uint8_t *rpc, size_t len, uint32_t *xid, uint32_t *accept_stat, 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 pc_vxi11_build_getport(uint8_t *buf, size_t cap, uint32_t xid, uint32_t prog, uint32_t vers, uint32_t proto);
124
125/**
126 * @brief Parse a GETPORT reply (bytes after the record mark). @p port is 0 if not registered.
127 * @return true on a well-formed successful reply; false otherwise.
128 */
129bool pc_vxi11_parse_getport_resp(const uint8_t *rpc, size_t len, uint32_t *port);
130
131// ── VXI-11 DEVICE_CORE ──────────────────────────────────────────────────────────────────────────
132
133/**
134 * @brief Build a create_link call: open a link to @p device (e.g. "inst0"). @p client_id is
135 * caller-chosen; @p lock_device requests an exclusive lock.
136 * @return total bytes written (incl. record mark), or 0 on overflow / bad input.
137 */
138size_t pc_vxi11_build_create_link(uint8_t *buf, size_t cap, uint32_t xid, int32_t client_id, bool lock_device,
139 uint32_t lock_timeout, const char *device);
140
141/** @brief A decoded Create_LinkResp. */
142struct Vxi11CreateLinkResp
143{
144 int32_t error; ///< Device_ErrorCode (0 = no error)
145 int32_t lid; ///< the link id, for subsequent calls
146 uint32_t abort_port; ///< the DEVICE_ASYNC abort-channel port
147 uint32_t max_recv_size; ///< the largest data block the device accepts in one device_write
148};
149bool pc_vxi11_parse_create_link_resp(const uint8_t *rpc, size_t len, Vxi11CreateLinkResp *out);
150
151/**
152 * @brief Build a device_write call: write @p data (e.g. a SCPI command) to link @p lid. Set
153 * @ref PC_VXI11_FLAG_END in @p flags to assert END with the last byte.
154 * @return total bytes written (incl. record mark), or 0 on overflow / bad input.
155 */
156size_t pc_vxi11_build_device_write(uint8_t *buf, size_t cap, uint32_t xid, int32_t lid, uint32_t io_timeout,
157 uint32_t lock_timeout, uint32_t flags, const uint8_t *data, size_t data_len);
158
159/** @brief A decoded Device_WriteResp. */
160struct Vxi11WriteResp
161{
162 int32_t error;
163 uint32_t size; ///< number of bytes written
164};
165bool pc_vxi11_parse_write_resp(const uint8_t *rpc, size_t len, Vxi11WriteResp *out);
166
167/**
168 * @brief Build a device_read call: read up to @p request_size bytes from link @p lid. Set
169 * @ref PC_VXI11_FLAG_TERMCHRSET in @p flags to stop at @p term_char.
170 * @return total bytes written (incl. record mark), or 0 on overflow.
171 */
172size_t pc_vxi11_build_device_read(uint8_t *buf, size_t cap, uint32_t xid, int32_t lid, uint32_t request_size,
173 uint32_t io_timeout, uint32_t lock_timeout, uint32_t flags, uint8_t term_char);
174
175/** @brief A decoded Device_ReadResp. @ref data points INTO @p rpc. */
176struct Vxi11ReadResp
177{
178 int32_t error;
179 int32_t reason; ///< OR of PC_VXI11_REASON_* (why the read ended)
180 const uint8_t *data;
181 size_t data_len;
182};
183bool pc_vxi11_parse_read_resp(const uint8_t *rpc, size_t len, Vxi11ReadResp *out);
184
185/**
186 * @brief Build a device_readstb call: read the IEEE 488.2 status byte from link @p lid.
187 * @return total bytes written (incl. record mark), or 0 on overflow.
188 */
189size_t pc_vxi11_build_device_readstb(uint8_t *buf, size_t cap, uint32_t xid, int32_t lid, uint32_t flags,
190 uint32_t lock_timeout, uint32_t io_timeout);
191
192/** @brief A decoded Device_ReadStbResp. */
193struct Vxi11ReadStbResp
194{
195 int32_t error;
196 uint8_t stb; ///< the status byte
197};
198bool pc_vxi11_parse_readstb_resp(const uint8_t *rpc, size_t len, Vxi11ReadStbResp *out);
199
200/**
201 * @brief Build a device_clear call: clear link @p lid's device (the protocol-level Selected Device Clear -
202 * it empties the instrument's input / output buffers). Carries the Device_GenericParms (lid + flags +
203 * the lock / io timeouts), as device_readstb does. @return total bytes written, or 0 on overflow.
204 */
205size_t pc_vxi11_build_device_clear(uint8_t *buf, size_t cap, uint32_t xid, int32_t lid, uint32_t flags,
206 uint32_t lock_timeout, uint32_t io_timeout);
207
208/**
209 * @brief Build a device_trigger call: assert a trigger on link @p lid (the protocol-level Group Execute
210 * Trigger, i.e. IEEE 488.2 `*TRG`). Same Device_GenericParms as device_clear. @return bytes, or 0.
211 */
212size_t pc_vxi11_build_device_trigger(uint8_t *buf, size_t cap, uint32_t xid, int32_t lid, uint32_t flags,
213 uint32_t lock_timeout, uint32_t io_timeout);
214
215/**
216 * @brief Build a destroy_link call: close link @p lid.
217 * @return total bytes written (incl. record mark), or 0 on overflow.
218 */
219size_t pc_vxi11_build_destroy_link(uint8_t *buf, size_t cap, uint32_t xid, int32_t lid);
220
221/**
222 * @brief Parse a bare Device_Error result (destroy_link / device_trigger / device_clear / ...).
223 * @param error receives the Device_ErrorCode.
224 * @return true on a well-formed successful reply; false otherwise.
225 */
226bool pc_vxi11_parse_error_resp(const uint8_t *rpc, size_t len, int32_t *error);
227
228#endif // PC_ENABLE_VXI11
229
230#endif // PROTOCORE_VXI11_H
User-facing configuration for ProtoCore.