ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
ptp.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 ptp.h
6 * @brief PTP / IEEE 1588-2008 (PTPv2) message codec + slave clock math (PROTOCORE_ENABLE_PTP).
7 *
8 * The Precision Time Protocol synchronizes clocks across a LAN to sub-microsecond accuracy by
9 * exchanging timestamped messages. This codec builds and parses the PTPv2 wire format - the 34-octet
10 * common header, the 10-octet (48-bit seconds + 32-bit nanoseconds) timestamp, the E2E Sync /
11 * Delay_Req / Follow_Up / Delay_Resp / Announce messages, and the P2P peer-delay messages (Pdelay_Req /
12 * Pdelay_Resp / Pdelay_Resp_Follow_Up, IEEE 1588-2008 ยง11.4) - and computes an ordinary-clock **slave**'s
13 * offset-from-master and mean-path-delay from the four transfer timestamps (t1..t4), plus the P2P
14 * meanLinkDelay. All multi-octet
15 * fields are big-endian (network order), per IEEE 1588-2008 clause 13. Pure and host-tested; the UDP
16 * transport (event port 319, general port 320, multicast 224.0.1.129) and the local timestamping are
17 * the application's - see the Ptp example for the ordinary-clock slave that drives this codec.
18 *
19 * @author Douglas Quigg (dstroy0)
20 * @date 2026
21 */
22
23#ifndef PROTOCORE_PTP_H
24#define PROTOCORE_PTP_H
25
26#include "protocore_config.h" // the entry point: protocore_types.h for the widths
27
28#if PROTOCORE_ENABLE_PTP
29
31
32// This module holds nothing between calls, so it carves no borrow and states none. An entry
33// takes one all the same, and never reads it, so every namespace in the tree is invoked the
34// same way.
35
36/** @brief PTPv2 messageType values (low nibble of octet 0). */
37enum protocore_ptp_msg_type
38{
39 PROTOCORE_PTP_SYNC = 0x0,
40 PROTOCORE_PTP_PDELAY_REQ = 0x2, ///< peer-delay request (P2P mechanism)
41 PROTOCORE_PTP_PDELAY_RESP = 0x3, ///< peer-delay response (P2P mechanism)
42 PROTOCORE_PTP_DELAY_REQ = 0x1,
43 PROTOCORE_PTP_FOLLOW_UP = 0x8,
44 PROTOCORE_PTP_DELAY_RESP = 0x9,
45 PROTOCORE_PTP_PDELAY_RESP_FOLLOW_UP = 0xA, ///< peer-delay response follow-up (two-step P2P)
46 PROTOCORE_PTP_ANNOUNCE = 0xB
47};
48
49#define PROTOCORE_PTP_HEADER_LEN 34 ///< common header length
50#define PROTOCORE_PTP_TS_LEN 10 ///< on-wire timestamp length (6-octet seconds + 4-octet nanoseconds)
51#define PROTOCORE_PTP_PDELAY_REQ_LEN 54 ///< header, originTimestamp, and the reserved field after it
52#define PROTOCORE_PTP_EVENT_PORT 319 ///< UDP port for event messages (Sync, Delay_Req)
53#define PROTOCORE_PTP_GENERAL_PORT 320 ///< UDP port for general messages (Follow_Up, Delay_Resp, Announce)
54
55/** @brief A PTP timestamp: 48-bit seconds + 32-bit nanoseconds. */
56typedef struct
57{
58 uint64_t seconds; ///< seconds (only the low 48 bits are on the wire)
59 uint32_t nanoseconds; ///< nanoseconds within the second (0 .. 999999999)
60} protocore_ptp_timestamp;
61
62/** @brief The 34-octet PTPv2 common header. */
63typedef struct
64{
65 uint8_t message_type; ///< protocore_ptp_msg_type (octet 0 low nibble)
66 uint8_t transport_specific; ///< octet 0 high nibble (majorSdoId)
67 uint8_t version; ///< PTP version (octet 1 low nibble); 2 for PTPv2
68 uint16_t message_length; ///< total message length in octets
69 uint8_t domain; ///< domainNumber
70 uint16_t flags; ///< flagField
71 int64_t correction; ///< correctionField (nanoseconds scaled by 2^16)
72 uint8_t clock_identity[8]; ///< sourcePortIdentity clockIdentity
73 uint16_t port_number; ///< sourcePortIdentity portNumber
74 uint16_t sequence_id; ///< sequenceId
75 uint8_t control; ///< controlField
76 int8_t log_interval; ///< logMessageInterval
77} protocore_ptp_header;
78
79/** @brief Parsed Delay_Resp body. */
80typedef struct
81{
82 protocore_ptp_timestamp receive; ///< receiveTimestamp (t4, when the master got our Delay_Req)
83 uint8_t req_clock_id[8]; ///< requestingPortIdentity clockIdentity (echoes our clock id)
84 uint16_t req_port; ///< requestingPortIdentity portNumber
85} protocore_ptp_delay_resp;
86
87/** @brief Parsed Pdelay_Resp / Pdelay_Resp_Follow_Up body (P2P peer-delay mechanism). */
88typedef struct
89{
90 protocore_ptp_timestamp
91 timestamp; ///< Pdelay_Resp: requestReceiptTimestamp (t2); Follow_Up: responseOriginTimestamp (t3)
92 uint8_t req_clock_id[8]; ///< requestingPortIdentity clockIdentity (echoes the Pdelay_Req sender)
93 uint16_t req_port; ///< requestingPortIdentity portNumber
94} protocore_ptp_pdelay_resp;
95
96/** @brief Parsed Announce body (the master's quality, for best-master selection / display). */
97typedef struct
98{
99 protocore_ptp_timestamp origin; ///< originTimestamp
100 int16_t utc_offset; ///< currentUtcOffset (TAI - UTC, seconds)
101 uint8_t gm_priority1; ///< grandmasterPriority1
102 uint8_t gm_clock_class; ///< grandmasterClockQuality.clockClass
103 uint8_t gm_clock_accuracy; ///< grandmasterClockQuality.clockAccuracy
104 uint16_t gm_variance; ///< grandmasterClockQuality.offsetScaledLogVariance
105 uint8_t gm_priority2; ///< grandmasterPriority2
106 uint8_t gm_identity[8]; ///< grandmasterIdentity
107 uint16_t steps_removed; ///< stepsRemoved
108 uint8_t time_source; ///< timeSource
109} protocore_ptp_announce;
110
111/** @brief Slave sync result: offset from master and mean path delay, in nanoseconds. */
112typedef struct
113{
114 int64_t offset_ns; ///< offsetFromMaster (local - master); subtract to correct the local clock
115 int64_t delay_ns; ///< meanPathDelay
116} protocore_ptp_sync;
117
118/** @brief What ts_write takes: p, ts. */
119typedef struct
120{
121 uint8_t *p;
122 const protocore_ptp_timestamp *ts;
123} PtpTsWriteArgs;
124
125/** @brief What ts_read takes: p, ts. */
126typedef struct
127{
128 const uint8_t *p;
129 protocore_ptp_timestamp *ts;
130} PtpTsReadArgs;
131
132/** @brief What ts_to_ns takes: ts. */
133typedef struct
134{
135 const protocore_ptp_timestamp *ts;
136} PtpTsToNsArgs;
137
138/** @brief What ts_from_ns takes: ns, ts. */
139typedef struct
140{
141 int64_t ns;
142 protocore_ptp_timestamp *ts;
143} PtpTsFromNsArgs;
144
145/** @brief What build_header takes: buf, cap, h, body_len. */
146typedef struct
147{
148 uint8_t *buf;
149 size_t cap;
150 const protocore_ptp_header *h;
151 uint16_t body_len;
152} PtpBuildHeaderArgs;
153
154/** @brief What parse_header takes: s, len, h. */
155typedef struct
156{
157 const uint8_t *s;
158 size_t len;
159 protocore_ptp_header *h;
160} PtpParseHeaderArgs;
161
162/** @brief What build_sync takes: buf, cap, h, origin. */
163typedef struct
164{
165 uint8_t *buf;
166 size_t cap;
167 const protocore_ptp_header *h;
168 const protocore_ptp_timestamp *origin;
169} PtpBuildSyncArgs;
170
171/** @brief What build_delay_req takes: buf, cap, h, origin. */
172typedef struct
173{
174 uint8_t *buf;
175 size_t cap;
176 const protocore_ptp_header *h;
177 const protocore_ptp_timestamp *origin;
178} PtpBuildDelayReqArgs;
179
180/** @brief What build_follow_up takes: buf, cap, h, precise. */
181typedef struct
182{
183 uint8_t *buf;
184 size_t cap;
185 const protocore_ptp_header *h;
186 const protocore_ptp_timestamp *precise;
187} PtpBuildFollowUpArgs;
188
189/** @brief What build_delay_resp takes: buf, cap, h, recv, ... */
190typedef struct
191{
192 uint8_t *buf;
193 size_t cap;
194 const protocore_ptp_header *h;
195 const protocore_ptp_timestamp *recv;
196 const uint8_t *req_clock_id;
197 uint16_t req_port;
198} PtpBuildDelayRespArgs;
199
200/** @brief What build_announce takes: buf, cap, h, a. */
201typedef struct
202{
203 uint8_t *buf;
204 size_t cap;
205 const protocore_ptp_header *h;
206 const protocore_ptp_announce *a;
207} PtpBuildAnnounceArgs;
208
209/** @brief What build_pdelay_req takes: buf, cap, h, origin. */
210typedef struct
211{
212 uint8_t *buf;
213 size_t cap;
214 const protocore_ptp_header *h;
215 const protocore_ptp_timestamp *origin;
216} PtpBuildPdelayReqArgs;
217
218/** @brief What build_pdelay_resp takes: buf, cap, h, recv, ... */
219typedef struct
220{
221 uint8_t *buf;
222 size_t cap;
223 const protocore_ptp_header *h;
224 const protocore_ptp_timestamp *recv;
225 const uint8_t *req_clock_id;
226 uint16_t req_port;
227} PtpBuildPdelayRespArgs;
228
229/** @brief What build_pdelay_resp_follow_up takes: buf, cap, h, ... */
230typedef struct
231{
232 uint8_t *buf;
233 size_t cap;
234 const protocore_ptp_header *h;
235 const protocore_ptp_timestamp *origin;
236 const uint8_t *req_clock_id;
237 uint16_t req_port;
238} PtpBuildPdelayRespFollowUpArgs;
239
240/** @brief What parse_timestamp_msg takes: s, len, h, ts. */
241typedef struct
242{
243 const uint8_t *s;
244 size_t len;
245 protocore_ptp_header *h;
246 protocore_ptp_timestamp *ts;
247} PtpParseTimestampMsgArgs;
248
249/** @brief What parse_delay_resp takes: s, len, h, out. */
250typedef struct
251{
252 const uint8_t *s;
253 size_t len;
254 protocore_ptp_header *h;
255 protocore_ptp_delay_resp *out;
256} PtpParseDelayRespArgs;
257
258/** @brief What parse_announce takes: s, len, h, out. */
259typedef struct
260{
261 const uint8_t *s;
262 size_t len;
263 protocore_ptp_header *h;
264 protocore_ptp_announce *out;
265} PtpParseAnnounceArgs;
266
267/** @brief What parse_pdelay_req takes: s, len, h, ts. */
268typedef struct
269{
270 const uint8_t *s;
271 size_t len;
272 protocore_ptp_header *h;
273 protocore_ptp_timestamp *ts;
274} PtpParsePdelayReqArgs;
275
276/** @brief What parse_pdelay_resp takes: s, len, h, out. */
277typedef struct
278{
279 const uint8_t *s;
280 size_t len;
281 protocore_ptp_header *h;
282 protocore_ptp_pdelay_resp *out;
283} PtpParsePdelayRespArgs;
284
285/** @brief What parse_pdelay_resp_follow_up takes: s, len, h, out. */
286typedef struct
287{
288 const uint8_t *s;
289 size_t len;
290 protocore_ptp_header *h;
291 protocore_ptp_pdelay_resp *out;
292} PtpParsePdelayRespFollowUpArgs;
293
294/** @brief What compute takes: t1, t2, t3, t4, out. */
295typedef struct
296{
297 int64_t t1;
298 int64_t t2;
299 int64_t t3;
300 int64_t t4;
301 protocore_ptp_sync *out;
302} PtpComputeArgs;
303
304/** @brief What compute_link_delay takes: t1, t2, t3, t4. */
305typedef struct
306{
307 int64_t t1;
308 int64_t t2;
309 int64_t t3;
310 int64_t t4;
311} PtpComputeLinkDelayArgs;
312
313/**
314 * @brief PTP / IEEE 1588-2008 (PTPv2) message codec + slave clock math (PROTOCORE_ENABLE_PTP).
315 *
316 * A caller sets the members a call takes, invokes it through ::Ptp with the bytes it runs
317 * out of, and reads the outcome off the same handle.
318 *
319 * Ptp.ts_write_args.p = ...;
320 * Ptp.ts_write_args.ts = ...;
321 * Ptp.ts_write(work);
322 *
323 * @var PtpNs::ts_write_args what ts_write takes: p, ts
324 * @var PtpNs::ts_read_args what ts_read takes: p, ts
325 * @var PtpNs::ts_to_ns_args what ts_to_ns takes: ts
326 * @var PtpNs::ts_from_ns_args what ts_from_ns takes: ns, ts
327 * @var PtpNs::build_header_args what build_header takes: buf, cap, h, body_len
328 * @var PtpNs::parse_header_args what parse_header takes: s, len, h
329 * @var PtpNs::build_sync_args what build_sync takes: buf, cap, h, origin
330 * @var PtpNs::build_delay_req_args what build_delay_req takes: buf, cap, h, origin
331 * @var PtpNs::build_follow_up_args what build_follow_up takes: buf, cap, h, precise
332 * @var PtpNs::build_delay_resp_args what build_delay_resp takes: buf, cap, h, recv,
333 * @var PtpNs::build_announce_args what build_announce takes: buf, cap, h, a
334 * @var PtpNs::build_pdelay_req_args what build_pdelay_req takes: buf, cap, h, origin
335 * @var PtpNs::build_pdelay_resp_args what build_pdelay_resp takes: buf, cap, h, recv,
336 * @var PtpNs::build_pdelay_resp_follow_up_args what build_pdelay_resp_follow_up takes: buf, cap, h,
337 * @var PtpNs::parse_timestamp_msg_args what parse_timestamp_msg takes: s, len, h, ts
338 * @var PtpNs::parse_delay_resp_args what parse_delay_resp takes: s, len, h, out
339 * @var PtpNs::parse_announce_args what parse_announce takes: s, len, h, out
340 * @var PtpNs::parse_pdelay_req_args what parse_pdelay_req takes: s, len, h, ts
341 * @var PtpNs::parse_pdelay_resp_args what parse_pdelay_resp takes: s, len, h, out
342 * @var PtpNs::parse_pdelay_resp_follow_up_args what parse_pdelay_resp_follow_up takes: s, len, h, out
343 * @var PtpNs::compute_args what compute takes: t1, t2, t3, t4, out
344 * @var PtpNs::compute_link_delay_args what compute_link_delay takes: t1, t2, t3, t4
345 * @var PtpNs::ok a call's true/false outcome
346 * @var PtpNs::value the value a call reports
347 * @var PtpNs::n PROTOCORE_PTP_HEADER_LEN or 0 on overflow / bad args
348 * @var PtpNs::ts_write write ts to p as the 10-octet on-wire form (big-endian)
349 * @var PtpNs::ts_read read a 10-octet on-wire timestamp from p into ts
350 * @var PtpNs::ts_to_ns convert ts to signed nanoseconds since its epoch (fits current ...
351 * @var PtpNs::ts_from_ns convert signed-nanoseconds ns to a timestamp
352 * @var PtpNs::build_header build the 34-octet common header into buf, stamping messageLength = ...
353 * @var PtpNs::parse_header parse the common header from s (len octets). Returns false if too ...
354 * @var PtpNs::build_sync build a Sync (origin is the originTimestamp; 0 for a two-step Sync)
355 * @var PtpNs::build_delay_req build a Delay_Req (origin is the originTimestamp; usually 0)
356 * @var PtpNs::build_follow_up build a Follow_Up carrying the precise Sync egress time precise (t1)
357 * @var PtpNs::build_delay_resp build a Delay_Resp carrying t4 recv and the requester's port ...
358 * @var PtpNs::build_announce build an Announce from a - master mode: advertise this clock's ...
359 * @var PtpNs::build_pdelay_req build a Pdelay_Req (origin is the originTimestamp, usually 0; the ...
360 * @var PtpNs::build_pdelay_resp build a Pdelay_Resp carrying t2 recv (requestReceiptTimestamp) + ...
361 * @var PtpNs::build_pdelay_resp_follow_up build a Pdelay_Resp_Follow_Up carrying t3 origin ...
362 * @var PtpNs::parse_timestamp_msg parse a Sync / Delay_Req / Follow_Up message: fills h and its ...
363 * @var PtpNs::parse_delay_resp parse a Delay_Resp into h + out. Returns false on a short / ...
364 * @var PtpNs::parse_announce parse an Announce into h + out. Returns false on a short / ...
365 * @var PtpNs::parse_pdelay_req parse a Pdelay_Req into h + its originTimestamp ts. False on a ...
366 * @var PtpNs::parse_pdelay_resp parse a Pdelay_Resp into h + out (timestamp is t2). False on a ...
367 * @var PtpNs::parse_pdelay_resp_follow_up parse a Pdelay_Resp_Follow_Up into h + out (timestamp is t3). False ...
368 * @var PtpNs::compute compute offsetFromMaster and meanPathDelay from the four PTP ...
369 * @var PtpNs::compute_link_delay compute the meanLinkDelay for the P2P mechanism (IEEE 1588-2008 ...
370 *
371 * @c work is bytes the CALLER holds. This module reads none of them: it carries nothing
372 * between calls, so there is no state to keep and nothing to wipe. The parameter is there so
373 * a caller drives every namespace the same way.
374 */
375typedef struct
376{
377 PtpTsWriteArgs ts_write_args;
378 PtpTsReadArgs ts_read_args;
379 PtpTsToNsArgs ts_to_ns_args;
380 PtpTsFromNsArgs ts_from_ns_args;
381 PtpBuildHeaderArgs build_header_args;
382 PtpParseHeaderArgs parse_header_args;
383 PtpBuildSyncArgs build_sync_args;
384 PtpBuildDelayReqArgs build_delay_req_args;
385 PtpBuildFollowUpArgs build_follow_up_args;
386 PtpBuildDelayRespArgs build_delay_resp_args;
387 PtpBuildAnnounceArgs build_announce_args;
388 PtpBuildPdelayReqArgs build_pdelay_req_args;
389 PtpBuildPdelayRespArgs build_pdelay_resp_args;
390 PtpBuildPdelayRespFollowUpArgs build_pdelay_resp_follow_up_args;
391 PtpParseTimestampMsgArgs parse_timestamp_msg_args;
392 PtpParseDelayRespArgs parse_delay_resp_args;
393 PtpParseAnnounceArgs parse_announce_args;
394 PtpParsePdelayReqArgs parse_pdelay_req_args;
395 PtpParsePdelayRespArgs parse_pdelay_resp_args;
396 PtpParsePdelayRespFollowUpArgs parse_pdelay_resp_follow_up_args;
397 PtpComputeArgs compute_args;
398 PtpComputeLinkDelayArgs compute_link_delay_args;
399 proto_bool ok;
400 int64_t value;
401 size_t n;
402} PtpVars;
403
404/** @brief The operands and the outcome. */
405extern PtpVars PtpV;
406
407/** @brief The entries. */
408typedef struct
409{
410 void (*const ts_write)(uint8_t *work);
411 void (*const ts_read)(uint8_t *work);
412 void (*const ts_to_ns)(uint8_t *work);
413 void (*const ts_from_ns)(uint8_t *work);
414 void (*const build_header)(uint8_t *work);
415 void (*const parse_header)(uint8_t *work);
416 void (*const build_sync)(uint8_t *work);
417 void (*const build_delay_req)(uint8_t *work);
418 void (*const build_follow_up)(uint8_t *work);
419 void (*const build_delay_resp)(uint8_t *work);
420 void (*const build_announce)(uint8_t *work);
421 void (*const build_pdelay_req)(uint8_t *work);
422 void (*const build_pdelay_resp)(uint8_t *work);
423 void (*const build_pdelay_resp_follow_up)(uint8_t *work);
424 void (*const parse_timestamp_msg)(uint8_t *work);
425 void (*const parse_delay_resp)(uint8_t *work);
426 void (*const parse_announce)(uint8_t *work);
427 void (*const parse_pdelay_req)(uint8_t *work);
428 void (*const parse_pdelay_resp)(uint8_t *work);
429 void (*const parse_pdelay_resp_follow_up)(uint8_t *work);
430 void (*const compute)(uint8_t *work);
431 void (*const compute_link_delay)(uint8_t *work);
432} PtpNs;
433
434// What the table binds, defined once in the .c and taking one parameter each: everything
435// else an entry needs is an operand in PtpV or a region of the borrow at a fixed offset.
436void protocore_ptp_ts_write(uint8_t *work);
437void protocore_ptp_ts_read(uint8_t *work);
438void protocore_ptp_ts_to_ns(uint8_t *work);
439void protocore_ptp_ts_from_ns(uint8_t *work);
440void protocore_ptp_build_header(uint8_t *work);
441void protocore_ptp_parse_header(uint8_t *work);
442void protocore_ptp_build_sync(uint8_t *work);
443void protocore_ptp_build_delay_req(uint8_t *work);
444void protocore_ptp_build_follow_up(uint8_t *work);
445void protocore_ptp_build_delay_resp(uint8_t *work);
446void protocore_ptp_build_announce(uint8_t *work);
447void protocore_ptp_build_pdelay_req(uint8_t *work);
448void protocore_ptp_build_pdelay_resp(uint8_t *work);
449void protocore_ptp_build_pdelay_resp_follow_up(uint8_t *work);
450void protocore_ptp_parse_timestamp_msg(uint8_t *work);
451void protocore_ptp_parse_delay_resp(uint8_t *work);
452void protocore_ptp_parse_announce(uint8_t *work);
453void protocore_ptp_parse_pdelay_req(uint8_t *work);
454void protocore_ptp_parse_pdelay_resp(uint8_t *work);
455void protocore_ptp_parse_pdelay_resp_follow_up(uint8_t *work);
456void protocore_ptp_compute(uint8_t *work);
457void protocore_ptp_compute_link_delay(uint8_t *work);
458
459// `static const`, initialised HERE rather than `extern` against a definition in the .c: a
460// const object whose initializer every translation unit can see is a COMPILE-TIME FACT, so
461// `Ptp.ts_write(work)` resolves to a named function and becomes a DIRECT call. An extern table
462// leaves the call indirect and the symbol live at every level, -O2 -flto included.
463static const PtpNs Ptp __attribute__((unused)) = {
464 .ts_write = protocore_ptp_ts_write,
465 .ts_read = protocore_ptp_ts_read,
466 .ts_to_ns = protocore_ptp_ts_to_ns,
467 .ts_from_ns = protocore_ptp_ts_from_ns,
468 .build_header = protocore_ptp_build_header,
469 .parse_header = protocore_ptp_parse_header,
470 .build_sync = protocore_ptp_build_sync,
471 .build_delay_req = protocore_ptp_build_delay_req,
472 .build_follow_up = protocore_ptp_build_follow_up,
473 .build_delay_resp = protocore_ptp_build_delay_resp,
474 .build_announce = protocore_ptp_build_announce,
475 .build_pdelay_req = protocore_ptp_build_pdelay_req,
476 .build_pdelay_resp = protocore_ptp_build_pdelay_resp,
477 .build_pdelay_resp_follow_up = protocore_ptp_build_pdelay_resp_follow_up,
478 .parse_timestamp_msg = protocore_ptp_parse_timestamp_msg,
479 .parse_delay_resp = protocore_ptp_parse_delay_resp,
480 .parse_announce = protocore_ptp_parse_announce,
481 .parse_pdelay_req = protocore_ptp_parse_pdelay_req,
482 .parse_pdelay_resp = protocore_ptp_parse_pdelay_resp,
483 .parse_pdelay_resp_follow_up = protocore_ptp_parse_pdelay_resp_follow_up,
484 .compute = protocore_ptp_compute,
485 .compute_link_delay = protocore_ptp_compute_link_delay,
486};
487
489
490#endif // PROTOCORE_ENABLE_PTP
491
492#endif // PROTOCORE_PTP_H
#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