File syn_coap.c¶
File List > net > syn_coap.c
Go to the documentation of this file
#if __has_include("syn_config.h")
#include "syn_config.h"
#endif
#if !defined(SYN_USE_COAP) || SYN_USE_COAP
#include "../port/syn_port_system.h"
#include "../util/syn_assert.h"
#include "../util/syn_pack.h"
#include "syn_coap.h"
#include <string.h>
size_t syn_coap_serialize(const SYN_CoapMsg *msg, const SYN_CoapOption *options,
size_t option_count, uint8_t *buf, size_t max_buf_len)
{
SYN_ASSERT(msg != NULL);
SYN_ASSERT(buf != NULL);
if (max_buf_len < (size_t)(4 + msg->token_len)) {
return 0;
}
/* Copy and sort options in ascending order of option numbers (CoAP spec requirement) */
SYN_CoapOption sorted[16];
size_t count = option_count > 16 ? 16 : option_count;
if (count > 0) {
memcpy(sorted, options, count * sizeof(SYN_CoapOption));
}
for (size_t i = 0; i < count; i++) {
for (size_t j = i + 1; j < count; j++) {
if (sorted[i].num > sorted[j].num) {
SYN_CoapOption tmp = sorted[i];
sorted[i] = sorted[j];
sorted[j] = tmp;
}
}
}
/* Encode 4-byte header */
buf[0] = (uint8_t)((COAP_VERSION << 6) | ((msg->type & 0x03) << 4) | (msg->token_len & 0x0F));
buf[1] = msg->code;
syn_poke_u16(msg->msg_id, buf, 2);
size_t pos = 4;
for (size_t i = 0; i < msg->token_len; i++) {
buf[pos++] = msg->token[i];
}
/* Encode options */
uint16_t prev_num = 0;
for (size_t i = 0; i < count; i++) {
uint16_t delta = sorted[i].num - prev_num;
prev_num = sorted[i].num;
uint8_t delta_val = 0;
uint8_t delta_ext_len = 0;
uint16_t delta_ext = 0;
if (delta < 13) {
delta_val = (uint8_t)delta;
} else if (delta < 269) {
delta_val = 13;
delta_ext_len = 1;
delta_ext = delta - 13;
} else {
delta_val = 14;
delta_ext_len = 2;
delta_ext = delta - 269;
}
uint8_t len_val = 0;
uint8_t len_ext_len = 0;
uint16_t len_ext = 0;
size_t opt_len = sorted[i].len;
if (opt_len < 13) {
len_val = (uint8_t)opt_len;
} else if (opt_len < 269) {
len_val = 13;
len_ext_len = 1;
len_ext = (uint16_t)(opt_len - 13);
} else {
len_val = 14;
len_ext_len = 2;
len_ext = (uint16_t)(opt_len - 269);
}
size_t opt_header_len = (size_t)(1 + delta_ext_len + len_ext_len);
if (pos + opt_header_len + opt_len > max_buf_len) {
return 0;
}
buf[pos++] = (uint8_t)((delta_val << 4) | len_val);
if (delta_ext_len == 1) {
buf[pos++] = (uint8_t)delta_ext;
} else if (delta_ext_len == 2) {
buf[pos++] = (uint8_t)(delta_ext >> 8);
buf[pos++] = (uint8_t)(delta_ext & 0xFF);
}
if (len_ext_len == 1) {
buf[pos++] = (uint8_t)len_ext;
} else if (len_ext_len == 2) {
buf[pos++] = (uint8_t)(len_ext >> 8);
buf[pos++] = (uint8_t)(len_ext & 0xFF);
}
memcpy(buf + pos, sorted[i].val, opt_len);
pos += opt_len;
}
/* Encode payload */
if (msg->payload_len > 0) {
if (pos + 1 + msg->payload_len > max_buf_len) {
return 0;
}
buf[pos++] = 0xFF; /* Payload marker */
memcpy(buf + pos, msg->payload, msg->payload_len);
pos += msg->payload_len;
}
return pos;
}
SYN_Status syn_coap_parse(SYN_CoapMsg *msg, SYN_CoapOption *options, size_t max_options,
size_t *option_count, const uint8_t *buf, size_t buf_len)
{
SYN_ASSERT(msg != NULL);
SYN_ASSERT(buf != NULL);
SYN_ASSERT(option_count != NULL);
if (buf_len < 4) {
return SYN_ERROR;
}
uint8_t ver = (buf[0] >> 6) & 0x03;
if (ver != COAP_VERSION) {
return SYN_ERROR;
}
msg->type = (buf[0] >> 4) & 0x03;
msg->token_len = buf[0] & 0x0F;
msg->code = buf[1];
msg->msg_id = syn_peek_u16(buf, 2);
if (msg->token_len > 8) {
return SYN_ERROR;
}
if (buf_len < (size_t)(4 + msg->token_len)) {
return SYN_ERROR;
}
memcpy(msg->token, buf + 4, msg->token_len);
size_t pos = 4 + msg->token_len;
size_t opt_idx = 0;
uint16_t prev_num = 0;
msg->payload = NULL;
msg->payload_len = 0;
while (pos < buf_len) {
if (buf[pos] == 0xFF) {
pos++;
msg->payload = buf + pos;
msg->payload_len = buf_len - pos;
break;
}
uint8_t delta_val = (buf[pos] >> 4) & 0x0F;
uint8_t len_val = buf[pos] & 0x0F;
pos++;
uint16_t delta = 0;
if (delta_val < 13) {
delta = delta_val;
} else if (delta_val == 13) {
if (pos >= buf_len)
return SYN_ERROR;
delta = 13 + buf[pos++];
} else if (delta_val == 14) {
if (pos + 1 >= buf_len)
return SYN_ERROR;
delta = (uint16_t)(269 + (((uint16_t)buf[pos] << 8) | buf[pos + 1]));
pos += 2;
} else {
return SYN_ERROR;
}
size_t len = 0;
if (len_val < 13) {
len = len_val;
} else if (len_val == 13) {
if (pos >= buf_len)
return SYN_ERROR;
len = 13 + buf[pos++];
} else if (len_val == 14) {
if (pos + 1 >= buf_len)
return SYN_ERROR;
len = (size_t)(269 + (((uint16_t)buf[pos] << 8) | buf[pos + 1]));
pos += 2;
} else {
return SYN_ERROR;
}
if (pos + len > buf_len) {
return SYN_ERROR;
}
uint16_t num = prev_num + delta;
prev_num = num;
if (opt_idx < max_options) {
options[opt_idx].num = num;
options[opt_idx].val = buf + pos;
options[opt_idx].len = len;
opt_idx++;
}
pos += len;
}
*option_count = opt_idx;
return SYN_OK;
}
void syn_coap_request_init(SYN_CoapRequest *r, const SYN_SockAddr *server_addr,
const SYN_CoapMsg *msg, uint32_t timeout_ms, uint8_t retries)
{
SYN_ASSERT(r != NULL);
(void)memset(r, 0, sizeof(*r));
r->server_addr = *server_addr;
r->req_msg = msg;
r->start_ms = 0;
/* Initialize backoff: factor 2 for binary exponential.
* max_attempts = retries + 1 (original + retransmissions) */
syn_backoff_init(&r->backoff, timeout_ms, timeout_ms << retries, 2, retries + 1);
}
SYN_PT_Status syn_coap_request_task(SYN_PT *pt, SYN_Task *task)
{
SYN_CoapRequest *r = (SYN_CoapRequest *)task->user_data;
SYN_ASSERT(r != NULL);
uint32_t attempt_delay = 0;
PT_BEGIN(pt);
r->status = SYN_TIMEOUT;
r->sock = syn_port_udp_open(0);
if (r->sock == SYN_SOCKET_INVALID) {
r->status = SYN_ERROR;
PT_EXIT(pt);
}
/* Serialize once into struct-owned buffer (survives across yields) */
r->tx_len = syn_coap_serialize(r->req_msg, r->req_options, r->req_option_count, r->tx_buf,
sizeof(r->tx_buf));
if (r->tx_len == 0) {
syn_port_sock_close(r->sock);
r->sock = SYN_SOCKET_INVALID;
r->status = SYN_ERROR;
PT_EXIT(pt);
}
r->start_ms = syn_port_get_tick_ms();
syn_backoff_reset(&r->backoff);
while (!syn_backoff_exhausted(&r->backoff)) {
/* Send request */
if (syn_port_udp_sendto(r->sock, r->tx_buf, r->tx_len, &r->server_addr) != (int)r->tx_len) {
r->status = SYN_ERROR;
break;
}
/* Calculate delay for THIS attempt (including jitter) */
attempt_delay = syn_backoff_next_ms(&r->backoff);
r->start_ms = syn_port_get_tick_ms();
/* Wait for response */
while ((syn_port_get_tick_ms() - r->start_ms) < attempt_delay) {
SYN_SockAddr from;
int n = syn_port_udp_recvfrom(r->sock, r->resp_buf, sizeof(r->resp_buf), &from, 0);
if (n > 0) {
r->resp_len = (size_t)n;
SYN_Status st = syn_coap_parse(&r->resp_msg, r->resp_options, 8,
&r->resp_option_count, r->resp_buf, r->resp_len);
if (st == SYN_OK && r->resp_msg.token_len == r->req_msg->token_len &&
memcmp(r->resp_msg.token, r->req_msg->token, r->resp_msg.token_len) == 0) {
r->status = SYN_OK;
break;
}
}
PT_DEFER(pt, task);
}
if (r->status == SYN_OK)
break;
}
if (r->status == SYN_BUSY)
r->status = SYN_TIMEOUT;
syn_port_sock_close(r->sock);
r->sock = SYN_SOCKET_INVALID;
PT_END(pt);
}
#endif /* SYN_USE_COAP */