ProtoCore v0.0.2
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
gpib.cpp
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 gpib.cpp
6 * @brief GPIB-over-LAN (Prologix-style) `++` command codec (pure, host-tested).
7 */
8
10#include "shared_primitives/strbuf.h" // pc_sb frame builder
11
12#if PC_ENABLE_GPIB
13
14#include <string.h>
15
16size_t pc_gpib_command(char *buf, size_t cap, const char *cmd)
17{
18 if (!buf || cap == 0 || !cmd)
19 {
20 return 0;
21 }
22 pc_sb sb_cmd = {buf, cap, 0, true};
23 pc_sb_lit(&sb_cmd, "++");
24 pc_sb_put(&sb_cmd, cmd);
25 pc_sb_lit(&sb_cmd, "\n");
26 return pc_sb_finish(&sb_cmd);
27}
28
29size_t pc_gpib_addr(char *buf, size_t cap, uint8_t pad, int sad)
30{
31 if (!buf || cap == 0 || pad > 30)
32 {
33 return 0;
34 }
35 pc_sb sb_addr = {buf, cap, 0, true};
36 pc_sb_lit(&sb_addr, "++addr ");
37 pc_sb_u32(&sb_addr, pad);
38 if (sad >= 0)
39 {
40 pc_sb_ch(&sb_addr, ' ');
41 pc_sb_i64(&sb_addr, sad);
42 }
43 pc_sb_lit(&sb_addr, "\n");
44 return pc_sb_finish(&sb_addr);
45}
46
47size_t pc_gpib_read(char *buf, size_t cap, GpibRead mode, uint8_t ch)
48{
49 if (!buf || cap == 0)
50 {
51 return 0;
52 }
53 pc_sb sb_read = {buf, cap, 0, true};
54 switch (mode)
55 {
56 case GpibRead::UNTIL_EOI:
57 pc_sb_lit(&sb_read, "++read eoi\n");
58 break;
59 case GpibRead::UNTIL_CHAR:
60 pc_sb_lit(&sb_read, "++read ");
61 pc_sb_u32(&sb_read, ch);
62 pc_sb_lit(&sb_read, "\n");
63 break;
64 case GpibRead::UNTIL_TIMEOUT:
65 default:
66 pc_sb_lit(&sb_read, "++read\n");
67 break;
68 }
69 return pc_sb_finish(&sb_read);
70}
71
72size_t pc_gpib_spoll(char *buf, size_t cap, int pad, int sad)
73{
74 if (!buf || cap == 0)
75 {
76 return 0;
77 }
78 pc_sb sb_spoll = {buf, cap, 0, true};
79 pc_sb_lit(&sb_spoll, "++spoll");
80 if (pad >= 0)
81 {
82 pc_sb_ch(&sb_spoll, ' ');
83 pc_sb_i64(&sb_spoll, pad);
84 if (sad >= 0)
85 {
86 pc_sb_ch(&sb_spoll, ' ');
87 pc_sb_i64(&sb_spoll, sad);
88 }
89 }
90 pc_sb_lit(&sb_spoll, "\n");
91 return pc_sb_finish(&sb_spoll);
92}
93
94size_t pc_gpib_eos(char *buf, size_t cap, GpibEos eos)
95{
96 if (!buf || cap == 0)
97 {
98 return 0;
99 }
100 pc_sb sb_eos = {buf, cap, 0, true};
101 pc_sb_lit(&sb_eos, "++eos ");
102 pc_sb_i64(&sb_eos, (int64_t)eos); // the wire field IS the enumerator's decimal value
103 pc_sb_lit(&sb_eos, "\n");
104 return pc_sb_finish(&sb_eos);
105}
106
107size_t pc_gpib_build_data(uint8_t *buf, size_t cap, const uint8_t *src, size_t len)
108{
109 if (!buf || cap == 0 || (len && !src))
110 {
111 return 0;
112 }
113 size_t o = 0;
114 for (size_t i = 0; i < len; i++)
115 {
116 uint8_t c = src[i];
117 bool esc = (c == 13 || c == 10 || c == 27 || c == 43); // CR / LF / ESC / '+'
118 size_t need = esc ? 2 : 1;
119 if (o + need + 1 > cap) // + 1 reserves room for the trailing '\n'
120 {
121 return 0;
122 }
123 if (esc)
124 {
125 buf[o++] = 27; // leading ESC
126 }
127 buf[o++] = c;
128 }
129 buf[o++] = '\n'; // the unescaped line terminator
130 return o;
131}
132
133bool pc_gpib_is_command(const char *line, size_t len)
134{
135 return line && len >= 2 && line[0] == '+' && line[1] == '+';
136}
137
138// Trim leading and trailing spaces / CR / LF from [s, s+len); updates s and len.
139static void trim(const char **s, size_t *len)
140{
141 const char *p = *s;
142 size_t n = *len;
143 while (n && (p[n - 1] == '\r' || p[n - 1] == '\n' || p[n - 1] == ' '))
144 {
145 n--;
146 }
147 while (n && (*p == ' '))
148 {
149 p++;
150 n--;
151 }
152 *s = p;
153 *len = n;
154}
155
156bool pc_gpib_parse_decimal(const char *s, size_t len, uint32_t *out)
157{
158 if (!s)
159 {
160 return false;
161 }
162 trim(&s, &len);
163 if (len == 0)
164 {
165 return false;
166 }
167 uint32_t v = 0;
168 for (size_t i = 0; i < len; i++)
169 {
170 if (s[i] < '0' || s[i] > '9')
171 {
172 return false;
173 }
174 v = v * 10 + (uint32_t)(s[i] - '0');
175 }
176 if (out)
177 {
178 *out = v;
179 }
180 return true;
181}
182
183bool pc_gpib_parse_addr(const char *s, size_t len, uint8_t *pad, int *sad)
184{
185 if (!s)
186 {
187 return false;
188 }
189 trim(&s, &len);
190 if (len == 0)
191 {
192 return false;
193 }
194 // primary
195 size_t i = 0;
196 uint32_t p = 0;
197 bool any = false;
198 while (i < len && s[i] >= '0' && s[i] <= '9')
199 {
200 p = p * 10 + (uint32_t)(s[i] - '0');
201 i++;
202 any = true;
203 }
204 if (!any || p > 30)
205 {
206 return false;
207 }
208 // optional secondary after spaces
209 int sad_val = -1;
210 while (i < len && s[i] == ' ')
211 {
212 i++;
213 }
214 if (i < len)
215 {
216 uint32_t sa = 0;
217 bool sany = false;
218 while (i < len && s[i] >= '0' && s[i] <= '9')
219 {
220 sa = sa * 10 + (uint32_t)(s[i] - '0');
221 i++;
222 sany = true;
223 }
224 if (!sany || i != len || sa < 96 || sa > 126)
225 {
226 return false;
227 }
228 sad_val = (int)sa;
229 }
230 if (i != len) // GCOVR_EXCL_LINE unreachable: the space skip above stops at i==len or a non-space, and
231 {
232 return false; // GCOVR_EXCL_LINE the secondary block only falls through when i==len - so i==len here
233 }
234 if (pad)
235 {
236 *pad = (uint8_t)p;
237 }
238 if (sad)
239 {
240 *sad = sad_val;
241 }
242 return true;
243}
244
245bool pc_gpib_parse_version(const char *s, size_t len, const char **ver, size_t *ver_len)
246{
247 if (!s)
248 {
249 return false;
250 }
251 static const char key[] = "version ";
252 const size_t klen = sizeof(key) - 1;
253 if (len < klen)
254 {
255 return false;
256 }
257 for (size_t i = 0; i + klen <= len; i++)
258 {
259 if (memcmp(s + i, key, klen) == 0)
260 {
261 const char *v = s + i + klen;
262 size_t vlen = len - i - klen;
263 trim(&v, &vlen);
264 if (vlen == 0)
265 {
266 return false;
267 }
268 if (ver)
269 {
270 *ver = v;
271 }
272 if (ver_len)
273 {
274 *ver_len = vlen;
275 }
276 return true;
277 }
278 }
279 return false;
280}
281
282#endif // PC_ENABLE_GPIB
GPIB-over-LAN (Prologix-style) controller command codec (PC_ENABLE_GPIB) - a zero-heap codec for the ...
Bounded no-heap string builder that fails closed on overflow (one shared copy).
size_t pc_sb_finish(pc_sb *b)
NUL-terminate and return the built length, or 0 if the build overflowed.
Definition strbuf.h:648
void pc_sb_ch(pc_sb *b, char c)
Append a single character.
Definition strbuf.h:186
void pc_sb_lit(pc_sb *b, const char(&s)[N])
Append a string literal. The array parameter deduces N, so the length is a constant.
Definition strbuf.h:76
void pc_sb_i64(pc_sb *b, int64_t v)
Append v as signed decimal (64-bit), with a leading '-' when negative.
Definition strbuf.h:315
void pc_sb_put(pc_sb *b, const char *s)
Append NUL-terminated s; leaves the buffer untouched and clears ok if it would not fit.
Definition strbuf.h:60
void pc_sb_u32(pc_sb *b, uint32_t v)
Append v as decimal (no leading zeros; "0" for zero).
Definition strbuf.h:303
Bump-append target; ok latches false once an append would overflow cap.
Definition strbuf.h:30