20bool is_reserved(uint8_t b)
22 return b == 0x7E || b == 0x7D || b == 0x11 || b == 0x13;
26bool put_stuffed(uint8_t *out, uint16_t *p, uint16_t cap, uint8_t b)
34 out[(*p)++] = ThreadHdlc::HDLC_ESCAPE;
35 out[(*p)++] = (uint8_t)(b ^ 0x20);
49uint8_t pc_spinel_pack_uint(uint32_t value, uint8_t *out, uint8_t cap)
62 uint8_t
byte = (uint8_t)(value & 0x7F);
73int pc_spinel_unpack_uint(
const uint8_t *raw, uint8_t len, uint32_t *value)
81 for (uint8_t n = 0; n < len; n++)
84 v |= (uint32_t)(b & 0x7F) << shift;
102uint16_t pc_spinel_command_build(uint8_t header, uint32_t cmd, uint32_t prop,
const uint8_t *value, uint16_t value_len,
103 uint8_t *out, uint16_t cap)
105 if (!out || cap < 1 || (value ==
nullptr && value_len > 0))
111 uint8_t n = pc_spinel_pack_uint(cmd, out + p, (uint8_t)(cap - p));
117 n = pc_spinel_pack_uint(prop, out + p, (uint8_t)(cap > p ? cap - p : 0));
123 if ((uint32_t)p + value_len > cap)
127 for (uint16_t i = 0; i < value_len; i++)
129 out[p + i] = value[i];
131 return (uint16_t)(p + value_len);
134int pc_spinel_command_parse(
const uint8_t *payload, uint16_t len, uint8_t *header, uint32_t *cmd, uint32_t *prop,
135 const uint8_t **value, uint16_t *value_len)
137 if (!payload || len < 1)
142 uint8_t h = payload[p++];
145 int n = pc_spinel_unpack_uint(payload + p, (uint8_t)((len - p) > 255 ? 255 : (len - p)), &c);
151 n = pc_spinel_unpack_uint(payload + p, (uint8_t)((len - p) > 255 ? 255 : (len - p)), &pr);
171 *value = payload + p;
175 *value_len = (uint16_t)(len - p);
182void pc_spinel_reader_init(SpinelReader *r,
const uint8_t *value, uint16_t len)
189 r->len = value ? len : 0;
191 r->err = (value ==
nullptr && len > 0);
197const uint8_t *take(SpinelReader *r, uint16_t n)
199 if (!r || r->err || (uint32_t)r->off + n > r->len)
207 const uint8_t *at = r->buf + r->off;
208 r->off = (uint16_t)(r->off + n);
213bool pc_spinel_get_bool(SpinelReader *r,
bool *out)
215 const uint8_t *b = take(r, 1);
227bool pc_spinel_get_u8(SpinelReader *r, uint8_t *out)
229 const uint8_t *b = take(r, 1);
241bool pc_spinel_get_i8(SpinelReader *r, int8_t *out)
243 const uint8_t *b = take(r, 1);
255bool pc_spinel_get_u16(SpinelReader *r, uint16_t *out)
257 const uint8_t *b = take(r, 2);
264 *out = (uint16_t)(b[0] | (b[1] << 8));
269bool pc_spinel_get_i16(SpinelReader *r, int16_t *out)
272 if (!pc_spinel_get_u16(r, &v))
283bool pc_spinel_get_u32(SpinelReader *r, uint32_t *out)
285 const uint8_t *b = take(r, 4);
292 *out = (uint32_t)b[0] | ((uint32_t)b[1] << 8) | ((uint32_t)b[2] << 16) | ((uint32_t)b[3] << 24);
297bool pc_spinel_get_i32(SpinelReader *r, int32_t *out)
300 if (!pc_spinel_get_u32(r, &v))
311bool pc_spinel_get_uint(SpinelReader *r, uint32_t *out)
318 int n = pc_spinel_unpack_uint(r->buf + r->off, (uint8_t)((r->len - r->off) > 255 ? 255 : (r->len - r->off)), &v);
324 r->off = (uint16_t)(r->off + n);
332bool pc_spinel_get_eui64(SpinelReader *r,
const uint8_t **out8)
334 const uint8_t *b = take(r, 8);
346bool pc_spinel_get_ipv6(SpinelReader *r,
const uint8_t **out16)
348 const uint8_t *b = take(r, 16);
360bool pc_spinel_get_utf8(SpinelReader *r,
const char **out, uint16_t *out_len)
367 while (i < r->len && r->buf[i] != 0)
378 *out = (
const char *)(r->buf + r->off);
382 *out_len = (uint16_t)(i - r->off);
384 r->off = (uint16_t)(i + 1);
388bool pc_spinel_get_data(SpinelReader *r,
const uint8_t **out, uint16_t *out_len)
396 *out = r->buf + r->off;
400 *out_len = (uint16_t)(r->len - r->off);
406bool pc_spinel_get_data_wlen(SpinelReader *r,
const uint8_t **out, uint16_t *out_len)
409 if (!pc_spinel_get_u16(r, &n))
413 const uint8_t *b = take(r, n);
429bool pc_spinel_reader_ok(
const SpinelReader *r)
434void pc_spinel_writer_init(SpinelWriter *w, uint8_t *out, uint16_t cap)
441 w->cap = out ? cap : 0;
443 w->err = (out ==
nullptr && cap > 0);
449uint8_t *room(SpinelWriter *w, uint16_t n)
451 if (!w || w->err || (uint32_t)w->off + n > w->cap)
459 uint8_t *at = w->buf + w->off;
460 w->off = (uint16_t)(w->off + n);
465bool pc_spinel_put_bool(SpinelWriter *w,
bool v)
467 uint8_t *b = room(w, 1);
476bool pc_spinel_put_u8(SpinelWriter *w, uint8_t v)
478 uint8_t *b = room(w, 1);
487bool pc_spinel_put_i8(SpinelWriter *w, int8_t v)
489 return pc_spinel_put_u8(w, (uint8_t)v);
492bool pc_spinel_put_u16(SpinelWriter *w, uint16_t v)
494 uint8_t *b = room(w, 2);
499 b[0] = (uint8_t)(v & 0xFF);
500 b[1] = (uint8_t)(v >> 8);
504bool pc_spinel_put_i16(SpinelWriter *w, int16_t v)
506 return pc_spinel_put_u16(w, (uint16_t)v);
509bool pc_spinel_put_u32(SpinelWriter *w, uint32_t v)
511 uint8_t *b = room(w, 4);
516 b[0] = (uint8_t)(v & 0xFF);
517 b[1] = (uint8_t)((v >> 8) & 0xFF);
518 b[2] = (uint8_t)((v >> 16) & 0xFF);
519 b[3] = (uint8_t)((v >> 24) & 0xFF);
523bool pc_spinel_put_i32(SpinelWriter *w, int32_t v)
525 return pc_spinel_put_u32(w, (uint32_t)v);
528bool pc_spinel_put_uint(SpinelWriter *w, uint32_t v)
535 uint8_t n = pc_spinel_pack_uint(v, tmp,
sizeof(tmp));
544 uint8_t *b = room(w, n);
549 for (uint8_t i = 0; i < n; i++)
556bool pc_spinel_put_eui64(SpinelWriter *w,
const uint8_t *v8)
566 uint8_t *b = room(w, 8);
571 for (uint8_t i = 0; i < 8; i++)
578bool pc_spinel_put_ipv6(SpinelWriter *w,
const uint8_t *v16)
588 uint8_t *b = room(w, 16);
593 for (uint8_t i = 0; i < 16; i++)
600bool pc_spinel_put_utf8(SpinelWriter *w,
const char *s)
615 uint8_t *b = room(w, (uint16_t)(n + 1));
620 for (uint16_t i = 0; i <= n; i++)
622 b[i] = (uint8_t)s[i];
627bool pc_spinel_put_data(SpinelWriter *w,
const uint8_t *d, uint16_t n)
629 if (d ==
nullptr && n > 0)
637 uint8_t *b = room(w, n);
642 for (uint16_t i = 0; i < n; i++)
649bool pc_spinel_put_data_wlen(SpinelWriter *w,
const uint8_t *d, uint16_t n)
651 if (!pc_spinel_put_u16(w, n))
655 return pc_spinel_put_data(w, d, n);
658uint16_t pc_spinel_writer_len(
const SpinelWriter *w)
671const SpinelPropInfo k_props[] = {
672 {SpinelProp::SPINEL_PROP_LAST_STATUS,
"LAST_STATUS",
'i'},
673 {SpinelProp::SPINEL_PROP_PROTOCOL_VERSION,
"PROTOCOL_VERSION",
'i'},
674 {SpinelProp::SPINEL_PROP_NCP_VERSION,
"NCP_VERSION",
'U'},
675 {SpinelProp::SPINEL_PROP_INTERFACE_TYPE,
"INTERFACE_TYPE",
'i'},
676 {SpinelProp::SPINEL_PROP_VENDOR_ID,
"VENDOR_ID",
'i'},
677 {SpinelProp::SPINEL_PROP_CAPS,
"CAPS",
'i'},
678 {SpinelProp::SPINEL_PROP_INTERFACE_COUNT,
"INTERFACE_COUNT",
'C'},
679 {SpinelProp::SPINEL_PROP_HWADDR,
"HWADDR",
'E'},
680 {SpinelProp::SPINEL_PROP_LOCK,
"LOCK",
'b'},
681 {SpinelProp::SPINEL_PROP_PHY_ENABLED,
"PHY_ENABLED",
'b'},
682 {SpinelProp::SPINEL_PROP_PHY_CHAN,
"PHY_CHAN",
'C'},
683 {SpinelProp::SPINEL_PROP_PHY_CHAN_SUPPORTED,
"PHY_CHAN_SUPPORTED",
'C'},
684 {SpinelProp::SPINEL_PROP_PHY_FREQ,
"PHY_FREQ",
'L'},
685 {SpinelProp::SPINEL_PROP_PHY_TX_POWER,
"PHY_TX_POWER",
'c'},
686 {SpinelProp::SPINEL_PROP_PHY_RSSI,
"PHY_RSSI",
'c'},
687 {SpinelProp::SPINEL_PROP_MAC_SCAN_STATE,
"MAC_SCAN_STATE",
'C'},
688 {SpinelProp::SPINEL_PROP_MAC_SCAN_MASK,
"MAC_SCAN_MASK",
'C'},
689 {SpinelProp::SPINEL_PROP_MAC_SCAN_PERIOD,
"MAC_SCAN_PERIOD",
'S'},
690 {SpinelProp::SPINEL_PROP_MAC_15_4_LADDR,
"MAC_15_4_LADDR",
'E'},
691 {SpinelProp::SPINEL_PROP_MAC_15_4_SADDR,
"MAC_15_4_SADDR",
'S'},
692 {SpinelProp::SPINEL_PROP_MAC_15_4_PANID,
"MAC_15_4_PANID",
'S'},
693 {SpinelProp::SPINEL_PROP_NET_SAVED,
"NET_SAVED",
'b'},
694 {SpinelProp::SPINEL_PROP_NET_IF_UP,
"NET_IF_UP",
'b'},
695 {SpinelProp::SPINEL_PROP_NET_STACK_UP,
"NET_STACK_UP",
'b'},
696 {SpinelProp::SPINEL_PROP_NET_ROLE,
"NET_ROLE",
'C'},
697 {SpinelProp::SPINEL_PROP_NET_NETWORK_NAME,
"NET_NETWORK_NAME",
'U'},
698 {SpinelProp::SPINEL_PROP_NET_XPANID,
"NET_XPANID",
'D'},
699 {SpinelProp::SPINEL_PROP_NET_NETWORK_KEY,
"NET_NETWORK_KEY",
'D'},
700 {SpinelProp::SPINEL_PROP_IPV6_LL_ADDR,
"IPV6_LL_ADDR",
'6'},
701 {SpinelProp::SPINEL_PROP_IPV6_ML_ADDR,
"IPV6_ML_ADDR",
'6'},
702 {SpinelProp::SPINEL_PROP_STREAM_DEBUG,
"STREAM_DEBUG",
'U'},
703 {SpinelProp::SPINEL_PROP_STREAM_RAW,
"STREAM_RAW",
'd'},
704 {SpinelProp::SPINEL_PROP_STREAM_NET,
"STREAM_NET",
'd'},
712const StatusName k_status[] = {
713 {SpinelStatus::SPINEL_STATUS_OK,
"OK"},
714 {SpinelStatus::SPINEL_STATUS_FAILURE,
"FAILURE"},
715 {SpinelStatus::SPINEL_STATUS_UNIMPLEMENTED,
"UNIMPLEMENTED"},
716 {SpinelStatus::SPINEL_STATUS_INVALID_ARGUMENT,
"INVALID_ARGUMENT"},
717 {SpinelStatus::SPINEL_STATUS_INVALID_STATE,
"INVALID_STATE"},
718 {SpinelStatus::SPINEL_STATUS_INVALID_COMMAND,
"INVALID_COMMAND"},
719 {SpinelStatus::SPINEL_STATUS_INVALID_INTERFACE,
"INVALID_INTERFACE"},
720 {SpinelStatus::SPINEL_STATUS_INTERNAL_ERROR,
"INTERNAL_ERROR"},
721 {SpinelStatus::SPINEL_STATUS_SECURITY_ERROR,
"SECURITY_ERROR"},
722 {SpinelStatus::SPINEL_STATUS_PARSE_ERROR,
"PARSE_ERROR"},
723 {SpinelStatus::SPINEL_STATUS_IN_PROGRESS,
"IN_PROGRESS"},
724 {SpinelStatus::SPINEL_STATUS_NOMEM,
"NOMEM"},
725 {SpinelStatus::SPINEL_STATUS_BUSY,
"BUSY"},
726 {SpinelStatus::SPINEL_STATUS_PROP_NOT_FOUND,
"PROP_NOT_FOUND"},
727 {SpinelStatus::SPINEL_STATUS_DROPPED,
"DROPPED"},
728 {SpinelStatus::SPINEL_STATUS_EMPTY,
"EMPTY"},
732const SpinelPropInfo *pc_spinel_prop_lookup(uint32_t
id)
734 for (uint16_t i = 0; i <
sizeof(k_props) /
sizeof(k_props[0]); i++)
736 if (k_props[i].
id ==
id)
744const char *pc_spinel_prop_name(uint32_t
id)
746 const SpinelPropInfo *e = pc_spinel_prop_lookup(
id);
747 return e ? e->name :
"UNKNOWN";
750const char *pc_spinel_status_name(uint32_t status)
752 for (uint16_t i = 0; i <
sizeof(k_status) /
sizeof(k_status[0]); i++)
754 if (k_status[i].code == status)
756 return k_status[i].name;
759 if (status >= SpinelStatus::SPINEL_STATUS_RESET_POWER_ON &&
760 status <= SpinelStatus::SPINEL_STATUS_RESET_POWER_ON + 7)
767uint16_t pc_spinel_fcs(
const uint8_t *buf, uint16_t len)
775uint16_t pc_spinel_frame_encode(
const uint8_t *payload, uint16_t len, uint8_t *out, uint16_t cap)
781 uint16_t fcs = pc_spinel_fcs(payload, len);
783 for (uint16_t i = 0; i < len; i++)
785 if (!put_stuffed(out, &p, cap, payload[i]))
790 if (!put_stuffed(out, &p, cap, (uint8_t)(fcs & 0xFF)) ||
791 !put_stuffed(out, &p, cap, (uint8_t)(fcs >> 8)))
799 out[p++] = ThreadHdlc::HDLC_FLAG;
803int pc_spinel_frame_decode(
const uint8_t *raw, uint16_t len, uint8_t *payload, uint16_t pay_cap, uint16_t *pay_len)
810 while (flag < len && raw[flag] != ThreadHdlc::HDLC_FLAG)
822 for (uint16_t i = 0; i < flag; i++)
825 if (b == ThreadHdlc::HDLC_ESCAPE)
831 b = (uint8_t)(raw[i] ^ 0x20);
843 uint16_t plen = (uint16_t)(n - 2);
844 uint16_t fcs = pc_spinel_fcs(un, plen);
845 if ((uint16_t)(un[plen] | (un[plen + 1] << 8)) != fcs)
853 for (uint16_t i = 0; i < plen; i++)
861 return (
int)(flag + 1);
Parameterized CRC engine - one source of truth for every cyclic redundancy check.
constexpr pc_crc_params PC_CRC16_X25
CRC-16/X-25 (HDLC FCS). check = 0x906E. Used by services/radio/thread, mbplus, nema_ts2.
uint32_t pc_crc(const pc_crc_params *p, const uint8_t *data, size_t len)
One-shot CRC of len octets at data.
#define PC_THREAD_MAX_DATA
Max spinel payload bytes carried in one HDLC-lite frame.
Thread spinel / HDLC-lite framing codec (PC_ENABLE_THREAD) - OpenThread RCP.