diff --git a/include/net/lwm2m.h b/include/net/lwm2m.h index 8aef192306e..2cc32f886d9 100644 --- a/include/net/lwm2m.h +++ b/include/net/lwm2m.h @@ -80,6 +80,13 @@ struct lwm2m_ctx { struct k_delayed_work retransmit_work; struct sys_mutex send_lock; + /** A pointer to currently processed request, for internal LwM2M engine + * use. The underlying type is ``struct lwm2m_message``, but since it's + * declared in a private header and not exposed to the application, + * it's stored as a void pointer. + */ + void *processed_req; + #if defined(CONFIG_LWM2M_DTLS_SUPPORT) /** TLS tag is set by client as a reference used when the * LwM2M engine calls tls_credential_(add|delete) @@ -831,6 +838,19 @@ int lwm2m_engine_delete_res_inst(char *pathstr); */ int lwm2m_engine_start(struct lwm2m_ctx *client_ctx); +/** + * @brief Acknowledge the currently processed request with an empty ACK. + * + * LwM2M engine by default sends piggybacked responses for requests. + * This function allows to send an empty ACK for a request earlier (from the + * application callback). The LwM2M engine will then send the actual response + * as a separate CON message after all callbacks are executed. + * + * @param[in] client_ctx LwM2M context + * + */ +void lwm2m_acknowledge(struct lwm2m_ctx *client_ctx); + /** * @brief LwM2M RD client events * diff --git a/subsys/net/lib/lwm2m/lwm2m_engine.c b/subsys/net/lib/lwm2m/lwm2m_engine.c index 74133d7d7ad..2b9f6efb2f0 100644 --- a/subsys/net/lib/lwm2m/lwm2m_engine.c +++ b/subsys/net/lib/lwm2m/lwm2m_engine.c @@ -1076,6 +1076,27 @@ cleanup: return ret; } +void lwm2m_acknowledge(struct lwm2m_ctx *client_ctx) +{ + struct lwm2m_message *request; + + if (client_ctx == NULL || client_ctx->processed_req == NULL) { + return; + } + + request = (struct lwm2m_message *)client_ctx->processed_req; + + if (request->acknowledged) { + return; + } + + if (lwm2m_send_empty_ack(client_ctx, request->mid) < 0) { + return; + } + + request->acknowledged = true; +} + uint16_t lwm2m_get_rd_data(uint8_t *client_data, uint16_t size) { struct lwm2m_engine_obj *obj; @@ -3766,6 +3787,42 @@ error: return 0; } +static int lwm2m_response_promote_to_con(struct lwm2m_message *msg) +{ + int ret; + + msg->type = COAP_TYPE_CON; + msg->mid = coap_next_id(); + + /* Since the response CoAP packet is already generated at this point, + * tweak the specific fields manually: + * - CoAP message type (byte 0, bits 2 and 3) + * - CoAP message id (bytes 2 and 3) + */ + msg->cpkt.data[0] &= ~(0x3 << 4); + msg->cpkt.data[0] |= (msg->type & 0x3) << 4; + msg->cpkt.data[2] = msg->mid >> 8; + msg->cpkt.data[3] = (uint8_t) msg->mid; + + /* Add the packet to the pending list. */ + msg->pending = coap_pending_next_unused( + msg->ctx->pendings, + CONFIG_LWM2M_ENGINE_MAX_PENDING); + if (!msg->pending) { + LOG_ERR("Unable to find a free pending to track " + "retransmissions."); + return -ENOMEM; + } + + ret = coap_pending_init(msg->pending, &msg->cpkt, &msg->ctx->remote_addr); + if (ret < 0) { + LOG_ERR("Unable to initialize a pending " + "retransmission (err:%d).", ret); + } + + return ret; +} + static void lwm2m_udp_receive(struct lwm2m_ctx *client_ctx, uint8_t *buf, uint16_t buf_len, struct sockaddr *from_addr, @@ -3867,12 +3924,26 @@ static void lwm2m_udp_receive(struct lwm2m_ctx *client_ctx, /* skip token generation by default */ msg->tkl = 0; + client_ctx->processed_req = msg; + /* process the response to this request */ r = udp_request_handler(&response, msg); if (r < 0) { return; } + if (msg->acknowledged) { + r = lwm2m_response_promote_to_con(msg); + if (r < 0) { + LOG_ERR("Failed to promote reponse to CON: %d", + r); + lwm2m_reset_message(msg, true); + return; + } + } + + client_ctx->processed_req = NULL; + r = lwm2m_send_message(msg); if (r < 0) { LOG_ERR("Err sending response: %d", r);