net: lwm2m: refactor engine to use lwm2m_message structure

Sending an lwm2m message is too difficult.  It requires pending / reply
and other structures to be configured and set by various portions of
the library.  There is also no way to know if a pending message ever
encounters a timeout.

Let's fix this by simplifying the internal LwM2M engine APIs for
handling lwm2m messages:

1. A user calls lwm2m_get_message(lwm2m_ctx) which returns the first
   available lwm2m message from an array of messages
   (total # of messages is set via CONFIG_LWM2M_ENGINE_MAX_MESSAGES).
2. Next the user sets all of the fields in the message that are
   required (type, code message id, token, etc)
3. Then the user calls lwm2m_init_message(msg).  This initializes the
   underlying zoap_packet, pending and reply structures.
4. Once initialized, the user creates their payload in msg->zpkt.
5. When the user is ready to send, the call lwm2m_send_message(msg).
6. And if for some reason an error occurs at any point, they can free
   up the entire set of structures with: lwm2m_release_message(msg).

Included in the refactoring is a timeout_cb field which can be set in
the LwM2M messages.  If a pending structure ever expires the engine
will call the timeout_cb passing in the msg structure before it's
automatically released.

Signed-off-by: Michael Scott <michael.scott@linaro.org>
This commit is contained in:
Michael Scott 2017-09-01 01:11:43 -07:00 committed by Jukka Rissanen
parent 42f3eccb38
commit 897dbffe7c
5 changed files with 412 additions and 351 deletions

View file

@ -29,6 +29,12 @@ config LWM2M_ENGINE_STACK_SIZE
Set the stack size for the LWM2M library engine (used for handling
OBSERVE and NOTIFY events)
config LWM2M_ENGINE_MAX_MESSAGES
int "LWM2M engine max. message object"
default 10
help
Set the maximum message objects for the LWM2M library client
config LWM2M_ENGINE_MAX_PENDING
int "LWM2M engine max. pending objects"
default 5

View file

@ -111,6 +111,9 @@ static struct lwm2m_engine_obj *get_engine_obj(int obj_id);
static struct lwm2m_engine_obj_inst *get_engine_obj_inst(int obj_id,
int obj_inst_id);
/* Shared set of in-flight LwM2M messages */
static struct lwm2m_message messages[CONFIG_LWM2M_ENGINE_MAX_MESSAGES];
/* for debugging: to print IP addresses */
char *lwm2m_sprint_ip_addr(const struct sockaddr *addr)
{
@ -189,7 +192,7 @@ int lwm2m_notify_observer_path(struct lwm2m_obj_path *path)
path->res_id);
}
static int engine_add_observer(struct net_app_ctx *app_ctx,
static int engine_add_observer(struct lwm2m_message *msg,
const u8_t *token, u8_t tkl,
struct lwm2m_obj_path *path,
u16_t format)
@ -199,6 +202,11 @@ static int engine_add_observer(struct net_app_ctx *app_ctx,
struct sockaddr *addr;
int i;
if (!msg || !msg->ctx) {
SYS_LOG_ERR("valid lwm2m message is required");
return -EINVAL;
}
if (!token || (tkl == 0 || tkl > MAX_TOKEN_LEN)) {
SYS_LOG_ERR("token(%p) and token length(%u) must be valid.",
token, tkl);
@ -206,7 +214,7 @@ static int engine_add_observer(struct net_app_ctx *app_ctx,
}
/* remote addr */
addr = &app_ctx->default_ctx->remote;
addr = &msg->ctx->net_app_ctx.default_ctx->remote;
/* check if object exists */
if (!get_engine_obj(path->obj_id)) {
@ -243,7 +251,7 @@ static int engine_add_observer(struct net_app_ctx *app_ctx,
/* make sure this observer doesn't exist already */
SYS_SLIST_FOR_EACH_CONTAINER(&engine_observer_list, obs, node) {
if (&obs->ctx->net_app_ctx == app_ctx &&
if (obs->ctx == msg->ctx &&
memcmp(&obs->path, path, sizeof(*path)) == 0) {
/* quietly update the token information */
memcpy(obs->token, token, tkl);
@ -272,8 +280,7 @@ static int engine_add_observer(struct net_app_ctx *app_ctx,
/* copy the values and add it to the list */
observe_node_data[i].used = true;
observe_node_data[i].ctx = CONTAINER_OF(app_ctx,
struct lwm2m_ctx, net_app_ctx);
observe_node_data[i].ctx = msg->ctx;
memcpy(&observe_node_data[i].path, path, sizeof(*path));
memcpy(observe_node_data[i].token, token, tkl);
observe_node_data[i].tkl = tkl;
@ -588,104 +595,192 @@ static void zoap_options_to_path(struct zoap_option *opt, int options_count,
}
}
int lwm2m_init_message(struct net_app_ctx *app_ctx, struct zoap_packet *zpkt,
struct net_pkt **pkt, u8_t type, u8_t code, u16_t mid,
const u8_t *token, u8_t tkl)
struct lwm2m_message *find_msg_from_pending(struct zoap_pending *pending)
{
size_t i;
if (!pending) {
return NULL;
}
for (i = 0; i < CONFIG_LWM2M_ENGINE_MAX_MESSAGES; i++) {
if (messages[i].ctx && messages[i].pending == pending) {
return &messages[i];
}
}
return NULL;
}
struct lwm2m_message *lwm2m_get_message(struct lwm2m_ctx *client_ctx)
{
size_t i;
for (i = 0; i < CONFIG_LWM2M_ENGINE_MAX_MESSAGES; i++) {
if (!messages[i].ctx) {
messages[i].ctx = client_ctx;
return &messages[i];
}
}
return NULL;
}
void lwm2m_release_message(struct lwm2m_message *msg)
{
if (!msg) {
return;
}
if (msg->pending) {
zoap_pending_clear(msg->pending);
}
if (msg->reply) {
/* make sure we want to clear the reply */
zoap_reply_clear(msg->reply);
}
memset(msg, 0, sizeof(*msg));
}
int lwm2m_init_message(struct lwm2m_message *msg)
{
struct net_pkt *pkt;
struct net_app_ctx *app_ctx;
struct net_buf *frag;
int r;
*pkt = net_app_get_net_pkt(app_ctx, AF_UNSPEC, BUF_ALLOC_TIMEOUT);
if (!*pkt) {
if (!msg || !msg->ctx) {
SYS_LOG_ERR("LwM2M message is invalid.");
return -EINVAL;
}
app_ctx = &msg->ctx->net_app_ctx;
pkt = net_app_get_net_pkt(app_ctx, AF_UNSPEC, BUF_ALLOC_TIMEOUT);
if (!pkt) {
SYS_LOG_ERR("Unable to get TX packet, not enough memory.");
return -ENOMEM;
}
frag = net_app_get_net_buf(app_ctx, *pkt,
BUF_ALLOC_TIMEOUT);
frag = net_app_get_net_buf(app_ctx, pkt, BUF_ALLOC_TIMEOUT);
if (!frag) {
SYS_LOG_ERR("Unable to get DATA buffer, not enough memory.");
net_pkt_unref(*pkt);
*pkt = NULL;
return -ENOMEM;
r = -ENOMEM;
goto cleanup;
}
r = zoap_packet_init(zpkt, *pkt);
r = zoap_packet_init(&msg->zpkt, pkt);
if (r < 0) {
SYS_LOG_ERR("zoap packet init error (err:%d)", r);
return r;
goto cleanup;
}
/* FIXME: Could be that zoap_packet_init() sets some defaults */
zoap_header_set_version(zpkt, 1);
zoap_header_set_type(zpkt, type);
zoap_header_set_code(zpkt, code);
zoap_header_set_version(&msg->zpkt, 1);
zoap_header_set_type(&msg->zpkt, msg->type);
zoap_header_set_code(&msg->zpkt, msg->code);
if (mid > 0) {
zoap_header_set_id(zpkt, mid);
if (msg->mid > 0) {
zoap_header_set_id(&msg->zpkt, msg->mid);
} else {
zoap_header_set_id(zpkt, zoap_next_id());
zoap_header_set_id(&msg->zpkt, zoap_next_id());
}
/*
* tkl == 0 is for a new TOKEN
* tkl == LWM2M_MSG_TOKEN_LEN_SKIP means dont set
*/
if (tkl == 0) {
zoap_header_set_token(zpkt, zoap_next_token(),
if (msg->tkl == 0) {
zoap_header_set_token(&msg->zpkt, zoap_next_token(),
MAX_TOKEN_LEN);
} else if (token && tkl != LWM2M_MSG_TOKEN_LEN_SKIP) {
zoap_header_set_token(zpkt, token, tkl);
} else if (msg->token && msg->tkl != LWM2M_MSG_TOKEN_LEN_SKIP) {
zoap_header_set_token(&msg->zpkt, msg->token, msg->tkl);
}
/* only TYPE_CON messages need pending tracking / reply handling */
if (msg->type != ZOAP_TYPE_CON) {
return 0;
}
msg->pending = zoap_pending_next_unused(
msg->ctx->pendings,
CONFIG_LWM2M_ENGINE_MAX_PENDING);
if (!msg->pending) {
SYS_LOG_ERR("Unable to find a free pending to track "
"retransmissions.");
r = -ENOMEM;
goto cleanup;
}
r = zoap_pending_init(msg->pending, &msg->zpkt,
&app_ctx->default_ctx->remote);
if (r < 0) {
SYS_LOG_ERR("Unable to initialize a pending "
"retransmission (err:%d).", r);
goto cleanup;
}
/* clear out pkt to avoid double unref */
pkt = NULL;
if (msg->reply_cb) {
msg->reply = zoap_reply_next_unused(
msg->ctx->replies,
CONFIG_LWM2M_ENGINE_MAX_REPLIES);
if (!msg->reply) {
SYS_LOG_ERR("No resources for "
"waiting for replies.");
r = -ENOMEM;
goto cleanup;
}
zoap_reply_init(msg->reply, &msg->zpkt);
msg->reply->reply = msg->reply_cb;
}
return 0;
}
struct zoap_pending *lwm2m_init_message_pending(struct lwm2m_ctx *client_ctx,
struct zoap_packet *zpkt)
{
struct zoap_pending *pending = NULL;
int ret;
pending = zoap_pending_next_unused(client_ctx->pendings,
CONFIG_LWM2M_ENGINE_MAX_PENDING);
if (!pending) {
SYS_LOG_ERR("Unable to find a free pending to track "
"retransmissions.");
return NULL;
}
ret = zoap_pending_init(pending, zpkt,
&client_ctx->net_app_ctx.default_ctx->remote);
if (ret < 0) {
SYS_LOG_ERR("Unable to initialize a pending "
"retransmission (err:%d).", ret);
pending->pkt = NULL;
return NULL;
}
return pending;
}
void lwm2m_init_message_cleanup(struct net_pkt *pkt,
struct zoap_pending *pending,
struct zoap_reply *reply)
{
if (pending) {
zoap_pending_clear(pending);
/* don't unref attached pkt twice */
if (!pending->pkt) {
pkt = NULL;
}
}
if (reply) {
zoap_reply_clear(reply);
}
cleanup:
lwm2m_release_message(msg);
if (pkt) {
net_pkt_unref(pkt);
}
return r;
}
int lwm2m_send_message(struct lwm2m_message *msg)
{
int ret;
if (!msg || !msg->ctx) {
SYS_LOG_ERR("LwM2M message is invalid.");
return -EINVAL;
}
msg->send_attempts++;
ret = net_app_send_pkt(&msg->ctx->net_app_ctx, msg->zpkt.pkt,
&msg->ctx->net_app_ctx.default_ctx->remote,
NET_SOCKADDR_MAX_SIZE, K_NO_WAIT, NULL);
if (ret < 0) {
return ret;
}
if (msg->type == ZOAP_TYPE_CON) {
if (msg->send_attempts > 1) {
return 0;
}
zoap_pending_cycle(msg->pending);
k_delayed_work_submit(&msg->ctx->retransmit_work,
msg->pending->timeout);
} else {
/* if we're not expecting an ACK, free up the msg data */
lwm2m_release_message(msg);
}
return 0;
}
u16_t lwm2m_get_rd_data(u8_t *client_data, u16_t size)
@ -2015,9 +2110,8 @@ static int get_observe_option(const struct zoap_packet *zpkt)
return zoap_option_value_to_int(&option);
}
static int handle_request(struct net_app_ctx *app_ctx,
struct zoap_packet *request,
struct zoap_packet *response)
static int handle_request(struct zoap_packet *request,
struct lwm2m_message *msg)
{
int r;
u8_t code;
@ -2040,9 +2134,9 @@ static int handle_request(struct net_app_ctx *app_ctx,
context.path = &path;
engine_clear_context(&context);
/* set ZoAP request / response */
/* set ZoAP request / message */
in.in_zpkt = request;
out.out_zpkt = response;
out.out_zpkt = &msg->zpkt;
/* set default reader/writer */
in.reader = &plain_text_reader;
@ -2159,8 +2253,7 @@ static int handle_request(struct net_app_ctx *app_ctx,
r);
}
r = engine_add_observer(app_ctx,
token, tkl, &path,
r = engine_add_observer(msg, token, tkl, &path,
accept);
if (r < 0) {
SYS_LOG_ERR("add OBSERVE error: %d", r);
@ -2248,25 +2341,16 @@ static int handle_request(struct net_app_ctx *app_ctx,
return r;
}
int lwm2m_udp_sendto(struct net_app_ctx *app_ctx, struct net_pkt *pkt)
{
return net_app_send_pkt(app_ctx, pkt, &app_ctx->default_ctx->remote,
NET_SOCKADDR_MAX_SIZE, K_NO_WAIT, NULL);
}
void lwm2m_udp_receive(struct lwm2m_ctx *client_ctx, struct net_pkt *pkt,
bool handle_separate_response,
int (*udp_request_handler)(struct net_app_ctx *app_ctx,
struct zoap_packet *,
struct zoap_packet *))
udp_request_handler_cb_t udp_request_handler)
{
struct lwm2m_message *msg = NULL;
struct net_udp_hdr hdr, *udp_hdr;
struct zoap_pending *pending;
struct zoap_reply *reply;
struct zoap_packet response;
struct sockaddr from_addr;
struct zoap_packet response2;
struct net_pkt *pkt2;
int header_len, r;
const u8_t *token;
u8_t tkl;
@ -2312,8 +2396,17 @@ void lwm2m_udp_receive(struct lwm2m_ctx *client_ctx, struct net_pkt *pkt,
token = zoap_header_get_token(&response, &tkl);
pending = zoap_pending_received(&response, client_ctx->pendings,
CONFIG_LWM2M_ENGINE_MAX_PENDING);
/*
* Clear pending pointer because zoap_pending_received() calls
* zoap_pending_clear, and later when we call lwm2m_release_message()
* it will try and call zoap_pending_clear() again if msg->pending
* is != NULL.
*/
if (pending) {
/* TODO: If necessary cancel retransmissions */
msg = find_msg_from_pending(pending);
if (msg) {
msg->pending = NULL;
}
}
SYS_LOG_DBG("checking for reply from [%s]",
@ -2321,47 +2414,7 @@ void lwm2m_udp_receive(struct lwm2m_ctx *client_ctx, struct net_pkt *pkt,
reply = zoap_response_received(&response, &from_addr,
client_ctx->replies,
CONFIG_LWM2M_ENGINE_MAX_REPLIES);
if (!reply) {
/*
* If no normal response handler is found, then this is
* a new request coming from the server. Let's look
* at registered objects to find a handler.
*/
if (udp_request_handler &&
zoap_header_get_type(&response) == ZOAP_TYPE_CON) {
/* Create a response packet if we reach this point */
r = lwm2m_init_message(&client_ctx->net_app_ctx,
&response2, &pkt2,
ZOAP_TYPE_ACK,
zoap_header_get_code(&response),
zoap_header_get_id(&response),
NULL, LWM2M_MSG_TOKEN_LEN_SKIP);
if (r < 0) {
if (pkt2) {
net_pkt_unref(pkt2);
}
goto cleanup;
}
/*
* The "response" here is actually a new request
*/
r = udp_request_handler(&client_ctx->net_app_ctx,
&response, &response2);
if (r < 0) {
SYS_LOG_ERR("Request handler error: %d", r);
} else {
r = lwm2m_udp_sendto(&client_ctx->net_app_ctx,
pkt2);
if (r < 0) {
SYS_LOG_ERR("Err sending response: %d",
r);
}
}
} else {
SYS_LOG_ERR("No handler for response");
}
} else {
if (reply) {
/*
* Separate response is composed of 2 messages, empty ACK with
* no token and an additional message with a matching token id
@ -2375,12 +2428,61 @@ void lwm2m_udp_receive(struct lwm2m_ctx *client_ctx, struct net_pkt *pkt,
if (handle_separate_response && !tkl &&
zoap_header_get_type(&response) == ZOAP_TYPE_ACK) {
SYS_LOG_DBG("separated response, not removing reply");
} else {
SYS_LOG_DBG("reply %p handled and removed", reply);
zoap_reply_clear(reply);
goto cleanup;
}
}
if (reply || pending) {
/* free up msg resources */
if (msg) {
lwm2m_release_message(msg);
}
SYS_LOG_DBG("reply %p handled and removed", reply);
goto cleanup;
}
/*
* If no normal response handler is found, then this is
* a new request coming from the server. Let's look
* at registered objects to find a handler.
*/
if (udp_request_handler &&
zoap_header_get_type(&response) == ZOAP_TYPE_CON) {
msg = lwm2m_get_message(client_ctx);
if (!msg) {
SYS_LOG_ERR("Unable to get a lwm2m message!");
goto cleanup;
}
/* Create a response message if we reach this point */
msg->type = ZOAP_TYPE_ACK;
msg->code = zoap_header_get_code(&response);
msg->mid = zoap_header_get_id(&response);
/* skip token generation by default */
msg->tkl = LWM2M_MSG_TOKEN_LEN_SKIP;
r = lwm2m_init_message(msg);
if (r < 0) {
goto cleanup;
}
/* process the response to this request */
r = udp_request_handler(&response, msg);
if (r < 0) {
SYS_LOG_ERR("Request handler error: %d", r);
} else {
r = lwm2m_send_message(msg);
if (r < 0) {
SYS_LOG_ERR("Err sending response: %d",
r);
lwm2m_release_message(msg);
}
}
} else {
SYS_LOG_ERR("No handler for response");
}
cleanup:
if (pkt) {
net_pkt_unref(pkt);
@ -2400,6 +2502,7 @@ static void udp_receive(struct net_app_ctx *app_ctx, struct net_pkt *pkt,
static void retransmit_request(struct k_work *work)
{
struct lwm2m_ctx *client_ctx;
struct lwm2m_message *msg;
struct zoap_pending *pending;
int r;
@ -2410,17 +2513,28 @@ static void retransmit_request(struct k_work *work)
return;
}
r = lwm2m_udp_sendto(&client_ctx->net_app_ctx,
pending->pkt);
if (r < 0) {
msg = find_msg_from_pending(pending);
if (!msg) {
SYS_LOG_ERR("pending has no valid LwM2M message!");
return;
}
if (!zoap_pending_cycle(pending)) {
zoap_pending_clear(pending);
/* pending request has expired */
if (msg->message_timeout_cb) {
msg->message_timeout_cb(msg);
}
lwm2m_release_message(msg);
return;
}
r = lwm2m_send_message(msg);
if (r < 0) {
SYS_LOG_ERR("Error sending lwm2m message: %d", r);
/* don't error here, retry until timeout */
}
k_delayed_work_submit(&client_ctx->retransmit_work, pending->timeout);
}
@ -2458,10 +2572,7 @@ static int notify_message_reply_cb(const struct zoap_packet *response,
static int generate_notify_message(struct observe_node *obs,
bool manual_trigger)
{
struct net_pkt *pkt = NULL;
struct zoap_pending *pending = NULL;
struct zoap_reply *reply = NULL;
struct zoap_packet request;
struct lwm2m_message *msg;
struct lwm2m_engine_obj_inst *obj_inst;
struct lwm2m_output_context out;
struct lwm2m_engine_context context;
@ -2481,7 +2592,6 @@ static int generate_notify_message(struct observe_node *obs,
memcpy(&path, &obs->path, sizeof(struct lwm2m_obj_path));
context.path = &path;
context.operation = LWM2M_OP_READ;
out.out_zpkt = &request;
SYS_LOG_DBG("[%s] NOTIFY MSG START: %u/%u/%u(%u) token:'%s' [%s] %lld",
manual_trigger ? "MANUAL" : "AUTO",
@ -2503,11 +2613,23 @@ static int generate_notify_message(struct observe_node *obs,
return -EINVAL;
}
ret = lwm2m_init_message(&obs->ctx->net_app_ctx, out.out_zpkt, &pkt,
ZOAP_TYPE_CON, ZOAP_RESPONSE_CODE_CONTENT,
0, obs->token, obs->tkl);
msg = lwm2m_get_message(obs->ctx);
if (!msg) {
SYS_LOG_ERR("Unable to get a lwm2m message!");
return -ENOMEM;
}
out.out_zpkt = &msg->zpkt;
msg->type = ZOAP_TYPE_CON;
msg->code = ZOAP_RESPONSE_CODE_CONTENT;
msg->mid = 0;
msg->token = obs->token;
msg->tkl = obs->tkl;
msg->reply_cb = notify_message_reply_cb;
ret = lwm2m_init_message(msg);
if (ret) {
goto cleanup;
return ret;
}
/* each notification should increment the obs counter */
@ -2542,36 +2664,17 @@ static int generate_notify_message(struct observe_node *obs,
goto cleanup;
}
pending = lwm2m_init_message_pending(obs->ctx, out.out_zpkt);
if (!pending) {
ret = -ENOMEM;
goto cleanup;
}
reply = zoap_reply_next_unused(obs->ctx->replies,
CONFIG_LWM2M_ENGINE_MAX_REPLIES);
if (!reply) {
SYS_LOG_ERR("No resources for waiting for replies.");
ret = -ENOMEM;
goto cleanup;
}
zoap_reply_init(reply, &request);
reply->reply = notify_message_reply_cb;
ret = lwm2m_udp_sendto(&obs->ctx->net_app_ctx, pkt);
ret = lwm2m_send_message(msg);
if (ret < 0) {
SYS_LOG_ERR("Error sending LWM2M packet (err:%d).", ret);
goto cleanup;
}
SYS_LOG_DBG("NOTIFY MSG: SENT");
zoap_pending_cycle(pending);
k_delayed_work_submit(&obs->ctx->retransmit_work, pending->timeout);
return ret;
SYS_LOG_DBG("NOTIFY MSG: SENT");
return 0;
cleanup:
lwm2m_init_message_cleanup(pkt, pending, reply);
lwm2m_release_message(msg);
return ret;
}

View file

@ -35,6 +35,40 @@
/* Use this value to skip token generation */
#define LWM2M_MSG_TOKEN_LEN_SKIP 0xFF
struct lwm2m_message;
/* Establish a message timeout callback */
typedef void (*lwm2m_message_timeout_cb_t)(struct lwm2m_message *msg);
/* Internal LwM2M message structure to track in-flight messages. */
struct lwm2m_message {
/** LwM2M context related to this message */
struct lwm2m_ctx *ctx;
/** ZoAP packet data related to this message */
struct zoap_packet zpkt;
/** Message transmission handling for TYPE_CON */
struct zoap_pending *pending;
struct zoap_reply *reply;
/** Message configuration */
const u8_t *token;
zoap_reply_t reply_cb;
lwm2m_message_timeout_cb_t message_timeout_cb;
u16_t mid;
u8_t type;
u8_t code;
u8_t tkl;
/** Counter for message re-send / abort handling */
u8_t send_attempts;
};
/* Establish a request handler callback type */
typedef int (*udp_request_handler_cb_t)(struct zoap_packet *request,
struct lwm2m_message *msg);
char *lwm2m_sprint_ip_addr(const struct sockaddr *addr);
int lwm2m_notify_observer(u16_t obj_id, u16_t obj_inst_id, u16_t res_id);
@ -51,14 +85,12 @@ int lwm2m_get_or_create_engine_obj(struct lwm2m_engine_context *context,
struct lwm2m_engine_obj_inst **obj_inst,
u8_t *created);
int lwm2m_init_message(struct net_app_ctx *app_ctx, struct zoap_packet *zpkt,
struct net_pkt **pkt, u8_t type, u8_t code, u16_t mid,
const u8_t *token, u8_t tkl);
struct zoap_pending *lwm2m_init_message_pending(struct lwm2m_ctx *client_ctx,
struct zoap_packet *zpkt);
void lwm2m_init_message_cleanup(struct net_pkt *pkt,
struct zoap_pending *pending,
struct zoap_reply *reply);
/* LwM2M message functions */
struct lwm2m_message *find_msg_from_pending(struct zoap_pending *pending);
struct lwm2m_message *lwm2m_get_message(struct lwm2m_ctx *client_ctx);
void lwm2m_release_message(struct lwm2m_message *msg);
int lwm2m_init_message(struct lwm2m_message *msg);
int lwm2m_send_message(struct lwm2m_message *msg);
u16_t lwm2m_get_rd_data(u8_t *client_data, u16_t size);
@ -67,11 +99,8 @@ int lwm2m_write_handler(struct lwm2m_engine_obj_inst *obj_inst,
struct lwm2m_engine_obj_field *obj_field,
struct lwm2m_engine_context *context);
int lwm2m_udp_sendto(struct net_app_ctx *app_ctx, struct net_pkt *pkt);
void lwm2m_udp_receive(struct lwm2m_ctx *client_ctx, struct net_pkt *pkt,
bool handle_separate_response,
int (*udp_request_handler)(struct net_app_ctx *app_ctx,
struct zoap_packet *request,
struct zoap_packet *response));
udp_request_handler_cb_t udp_request_handler);
#endif /* LWM2M_ENGINE_H */

View file

@ -46,97 +46,64 @@ firmware_udp_receive(struct net_app_ctx *app_ctx, struct net_pkt *pkt,
lwm2m_udp_receive(&firmware_ctx, pkt, true, NULL);
}
static void retransmit_request(struct k_work *work)
static void do_transmit_timeout_cb(struct lwm2m_message *msg)
{
struct zoap_pending *pending;
int r;
pending = zoap_pending_next_to_expire(firmware_ctx.pendings,
CONFIG_LWM2M_ENGINE_MAX_PENDING);
if (!pending) {
return;
}
r = lwm2m_udp_sendto(&firmware_ctx.net_app_ctx, pending->pkt);
if (r < 0) {
return;
}
if (!zoap_pending_cycle(pending)) {
zoap_pending_clear(pending);
return;
}
k_delayed_work_submit(&firmware_ctx.retransmit_work, pending->timeout);
/* TODO: Handle timeout */
}
static int transfer_request(struct zoap_block_context *ctx,
const u8_t *token, u8_t tkl,
zoap_reply_t reply_cb)
{
struct zoap_packet request;
struct net_pkt *pkt = NULL;
struct zoap_pending *pending = NULL;
struct zoap_reply *reply = NULL;
struct lwm2m_message *msg;
int ret;
ret = lwm2m_init_message(&firmware_ctx.net_app_ctx, &request, &pkt,
ZOAP_TYPE_CON, ZOAP_METHOD_GET,
0, token, tkl);
msg = lwm2m_get_message(&firmware_ctx);
if (!msg) {
SYS_LOG_ERR("Unable to get a lwm2m message!");
return -ENOMEM;
}
msg->type = ZOAP_TYPE_CON;
msg->code = ZOAP_METHOD_GET;
msg->mid = 0;
msg->token = token;
msg->tkl = tkl;
msg->reply_cb = reply_cb;
msg->message_timeout_cb = do_transmit_timeout_cb;
ret = lwm2m_init_message(msg);
if (ret) {
goto cleanup;
}
/* hard code URI path here -- should be pulled from package_uri */
ret = zoap_add_option(&request, ZOAP_OPTION_URI_PATH,
ret = zoap_add_option(&msg->zpkt, ZOAP_OPTION_URI_PATH,
"large-create", sizeof("large-create") - 1);
ret = zoap_add_option(&request, ZOAP_OPTION_URI_PATH,
ret = zoap_add_option(&msg->zpkt, ZOAP_OPTION_URI_PATH,
"1", sizeof("1") - 1);
if (ret < 0) {
SYS_LOG_ERR("Error adding URI_QUERY 'large'");
goto cleanup;
}
ret = zoap_add_block2_option(&request, ctx);
ret = zoap_add_block2_option(&msg->zpkt, ctx);
if (ret) {
SYS_LOG_ERR("Unable to add block2 option.");
goto cleanup;
}
pending = lwm2m_init_message_pending(&firmware_ctx, &request);
if (!pending) {
ret = -ENOMEM;
goto cleanup;
}
/* set the reply handler */
if (reply_cb) {
reply = zoap_reply_next_unused(firmware_ctx.replies,
CONFIG_LWM2M_ENGINE_MAX_REPLIES);
if (!reply) {
SYS_LOG_ERR("No resources for waiting for replies.");
ret = -ENOMEM;
goto cleanup;
}
zoap_reply_init(reply, &request);
reply->reply = reply_cb;
}
/* send request */
ret = lwm2m_udp_sendto(&firmware_ctx.net_app_ctx, pkt);
ret = lwm2m_send_message(msg);
if (ret < 0) {
SYS_LOG_ERR("Error sending LWM2M packet (err:%d).",
ret);
SYS_LOG_ERR("Error sending LWM2M packet (err:%d).", ret);
goto cleanup;
}
zoap_pending_cycle(pending);
k_delayed_work_submit(&firmware_ctx.retransmit_work, pending->timeout);
return 0;
cleanup:
lwm2m_init_message_cleanup(pkt, pending, reply);
lwm2m_release_message(msg);
return ret;
}
@ -299,8 +266,6 @@ int lwm2m_firmware_start_transfer(char *package_uri)
firmware_ctx.net_init_timeout = NETWORK_INIT_TIMEOUT;
firmware_ctx.net_timeout = NETWORK_CONNECT_TIMEOUT;
k_work_init(&firmware_work, firmware_transfer);
k_delayed_work_init(&firmware_ctx.retransmit_work,
retransmit_request);
/* start file transfer work */
strncpy(firmware_uri, package_uri, PACKAGE_URI_LEN - 1);

View file

@ -398,72 +398,57 @@ static int sm_do_init(int index)
static int sm_do_bootstrap(int index)
{
struct zoap_packet request;
struct net_pkt *pkt = NULL;
struct zoap_pending *pending = NULL;
struct zoap_reply *reply = NULL;
struct lwm2m_message *msg;
struct net_app_ctx *app_ctx = NULL;
int ret = 0;
int ret;
if (clients[index].use_bootstrap &&
clients[index].bootstrapped == 0 &&
clients[index].has_bs_server_info) {
app_ctx = &clients[index].ctx->net_app_ctx;
ret = lwm2m_init_message(app_ctx,
&request, &pkt, ZOAP_TYPE_CON,
ZOAP_METHOD_POST, 0, NULL, 0);
msg = lwm2m_get_message(clients[index].ctx);
if (!msg) {
SYS_LOG_ERR("Unable to get a lwm2m message!");
return -ENOMEM;
}
msg->type = ZOAP_TYPE_CON;
msg->code = ZOAP_METHOD_POST;
msg->mid = 0;
msg->reply_cb = do_bootstrap_reply_cb;
ret = lwm2m_init_message(msg);
if (ret) {
goto cleanup;
}
zoap_add_option(&request, ZOAP_OPTION_URI_PATH,
zoap_add_option(&msg->zpkt, ZOAP_OPTION_URI_PATH,
"bs", strlen("bs"));
snprintf(query_buffer, sizeof(query_buffer) - 1,
"ep=%s", clients[index].ep_name);
zoap_add_option(&request, ZOAP_OPTION_URI_QUERY,
zoap_add_option(&msg->zpkt, ZOAP_OPTION_URI_QUERY,
query_buffer, strlen(query_buffer));
pending = lwm2m_init_message_pending(clients[index].ctx,
&request);
if (!pending) {
ret = -ENOMEM;
goto cleanup;
}
reply = zoap_reply_next_unused(clients[index].ctx->replies,
CONFIG_LWM2M_ENGINE_MAX_REPLIES);
if (!reply) {
SYS_LOG_ERR("No resources for waiting for replies.");
ret = -ENOMEM;
goto cleanup;
}
zoap_reply_init(reply, &request);
reply->reply = do_bootstrap_reply_cb;
/* log the bootstrap attempt */
SYS_LOG_DBG("Register ID with bootstrap server [%s] as '%s'",
lwm2m_sprint_ip_addr(
&app_ctx->default_ctx->remote),
query_buffer);
ret = lwm2m_udp_sendto(app_ctx, pkt);
ret = lwm2m_send_message(msg);
if (ret < 0) {
SYS_LOG_ERR("Error sending LWM2M packet (err:%d).",
ret);
goto cleanup;
}
zoap_pending_cycle(pending);
k_delayed_work_submit(&clients[index].ctx->retransmit_work,
pending->timeout);
set_sm_state(index, ENGINE_BOOTSTRAP_SENT);
}
return ret;
return 0;
cleanup:
lwm2m_init_message_cleanup(pkt, pending, reply);
lwm2m_release_message(msg);
return ret;
}
@ -512,54 +497,62 @@ static int sm_bootstrap_done(int index)
}
static int sm_send_registration(int index, bool send_obj_support_data,
zoap_reply_t reply_cb)
zoap_reply_t reply_cb,
lwm2m_message_timeout_cb_t timeout_cb)
{
struct net_app_ctx *app_ctx = NULL;
struct zoap_packet request;
struct net_pkt *pkt = NULL;
struct zoap_pending *pending = NULL;
struct zoap_reply *reply = NULL;
struct lwm2m_message *msg;
u8_t *payload;
u16_t client_data_len, len;
int ret = 0;
int ret;
app_ctx = &clients[index].ctx->net_app_ctx;
msg = lwm2m_get_message(clients[index].ctx);
if (!msg) {
SYS_LOG_ERR("Unable to get a lwm2m message!");
return -ENOMEM;
}
/* remember the last reg time */
clients[index].last_update = k_uptime_get();
ret = lwm2m_init_message(app_ctx,
&request, &pkt, ZOAP_TYPE_CON,
ZOAP_METHOD_POST, 0, NULL, 0);
msg->type = ZOAP_TYPE_CON;
msg->code = ZOAP_METHOD_POST;
msg->mid = 0;
msg->reply_cb = reply_cb;
msg->message_timeout_cb = timeout_cb;
ret = lwm2m_init_message(msg);
if (ret) {
goto cleanup;
}
zoap_add_option(&request, ZOAP_OPTION_URI_PATH,
zoap_add_option(&msg->zpkt, ZOAP_OPTION_URI_PATH,
LWM2M_RD_CLIENT_URI,
strlen(LWM2M_RD_CLIENT_URI));
if (!sm_is_registered(index)) {
/* include client endpoint in URI QUERY on 1st registration */
zoap_add_option_int(&request, ZOAP_OPTION_CONTENT_FORMAT,
zoap_add_option_int(&msg->zpkt, ZOAP_OPTION_CONTENT_FORMAT,
LWM2M_FORMAT_APP_LINK_FORMAT);
snprintf(query_buffer, sizeof(query_buffer) - 1,
"lwm2m=%s", LWM2M_PROTOCOL_VERSION);
zoap_add_option(&request, ZOAP_OPTION_URI_QUERY,
zoap_add_option(&msg->zpkt, ZOAP_OPTION_URI_QUERY,
query_buffer, strlen(query_buffer));
snprintf(query_buffer, sizeof(query_buffer) - 1,
"ep=%s", clients[index].ep_name);
zoap_add_option(&request, ZOAP_OPTION_URI_QUERY,
zoap_add_option(&msg->zpkt, ZOAP_OPTION_URI_QUERY,
query_buffer, strlen(query_buffer));
} else {
/* include server endpoint in URI PATH otherwise */
zoap_add_option(&request, ZOAP_OPTION_URI_PATH,
zoap_add_option(&msg->zpkt, ZOAP_OPTION_URI_PATH,
clients[index].server_ep,
strlen(clients[index].server_ep));
}
snprintf(query_buffer, sizeof(query_buffer) - 1,
"lt=%d", clients[index].lifetime);
zoap_add_option(&request, ZOAP_OPTION_URI_QUERY,
zoap_add_option(&msg->zpkt, ZOAP_OPTION_URI_QUERY,
query_buffer, strlen(query_buffer));
/* TODO: add supported binding query string */
@ -567,54 +560,34 @@ static int sm_send_registration(int index, bool send_obj_support_data,
/* generate the rd data */
client_data_len = lwm2m_get_rd_data(client_data,
sizeof(client_data));
payload = zoap_packet_get_payload(&request, &len);
payload = zoap_packet_get_payload(&msg->zpkt, &len);
if (!payload) {
ret = -EINVAL;
goto cleanup;
}
memcpy(payload, client_data, client_data_len);
ret = zoap_packet_set_used(&request, client_data_len);
ret = zoap_packet_set_used(&msg->zpkt, client_data_len);
if (ret) {
goto cleanup;
}
}
pending = lwm2m_init_message_pending(clients[index].ctx, &request);
if (!pending) {
ret = -ENOMEM;
goto cleanup;
}
reply = zoap_reply_next_unused(clients[index].ctx->replies,
CONFIG_LWM2M_ENGINE_MAX_REPLIES);
if (!reply) {
SYS_LOG_ERR("No resources for waiting for replies.");
ret = -ENOMEM;
goto cleanup;
}
zoap_reply_init(reply, &request);
reply->reply = reply_cb;
/* log the registration attempt */
SYS_LOG_DBG("registration sent [%s]",
lwm2m_sprint_ip_addr(&app_ctx->default_ctx->remote));
ret = lwm2m_udp_sendto(app_ctx, pkt);
ret = lwm2m_send_message(msg);
if (ret < 0) {
SYS_LOG_ERR("Error sending LWM2M packet (err:%d).",
ret);
goto cleanup;
}
zoap_pending_cycle(pending);
k_delayed_work_submit(&clients[index].ctx->retransmit_work,
pending->timeout);
return ret;
/* log the registration attempt */
SYS_LOG_DBG("registration sent [%s]",
lwm2m_sprint_ip_addr(&app_ctx->default_ctx->remote));
return 0;
cleanup:
lwm2m_init_message_cleanup(pkt, pending, reply);
lwm2m_release_message(msg);
return ret;
}
@ -626,7 +599,7 @@ static int sm_do_registration(int index)
!sm_is_registered(index) &&
clients[index].has_registration_info) {
ret = sm_send_registration(index, true,
do_registration_reply_cb);
do_registration_reply_cb, NULL);
if (!ret) {
set_sm_state(index, ENGINE_REGISTRATION_SENT);
} else {
@ -650,7 +623,7 @@ static int sm_registration_done(int index)
forced_update = clients[index].trigger_update;
clients[index].trigger_update = 0;
ret = sm_send_registration(index, forced_update,
do_update_reply_cb);
do_update_reply_cb, NULL);
if (!ret) {
set_sm_state(index, ENGINE_UPDATE_SENT);
} else {
@ -664,59 +637,44 @@ static int sm_registration_done(int index)
static int sm_do_deregister(int index)
{
struct net_app_ctx *app_ctx = NULL;
struct zoap_packet request;
struct net_pkt *pkt = NULL;
struct zoap_pending *pending = NULL;
struct zoap_reply *reply = NULL;
struct lwm2m_message *msg;
int ret;
app_ctx = &clients[index].ctx->net_app_ctx;
msg = lwm2m_get_message(clients[index].ctx);
if (!msg) {
SYS_LOG_ERR("Unable to get a lwm2m message!");
return -ENOMEM;
}
ret = lwm2m_init_message(app_ctx,
&request, &pkt, ZOAP_TYPE_CON,
ZOAP_METHOD_DELETE, 0, NULL, 0);
msg->type = ZOAP_TYPE_CON;
msg->code = ZOAP_METHOD_DELETE;
msg->mid = 0;
msg->reply_cb = do_deregister_reply_cb;
ret = lwm2m_init_message(msg);
if (ret) {
goto cleanup;
}
zoap_add_option(&request, ZOAP_OPTION_URI_PATH,
zoap_add_option(&msg->zpkt, ZOAP_OPTION_URI_PATH,
clients[index].server_ep,
strlen(clients[index].server_ep));
pending = lwm2m_init_message_pending(clients[index].ctx, &request);
if (!pending) {
ret = -ENOMEM;
goto cleanup;
}
reply = zoap_reply_next_unused(clients[index].ctx->replies,
CONFIG_LWM2M_ENGINE_MAX_REPLIES);
if (!reply) {
SYS_LOG_ERR("No resources for waiting for replies.");
ret = -ENOMEM;
goto cleanup;
}
zoap_reply_init(reply, &request);
reply->reply = do_deregister_reply_cb;
SYS_LOG_INF("Deregister from '%s'", clients[index].server_ep);
ret = lwm2m_udp_sendto(app_ctx, pkt);
ret = lwm2m_send_message(msg);
if (ret < 0) {
SYS_LOG_ERR("Error sending LWM2M packet (err:%d).",
ret);
goto cleanup;
}
zoap_pending_cycle(pending);
k_delayed_work_submit(&clients[index].ctx->retransmit_work,
pending->timeout);
set_sm_state(index, ENGINE_DEREGISTER_SENT);
return ret;
return 0;
cleanup:
lwm2m_init_message_cleanup(pkt, pending, reply);
lwm2m_release_message(msg);
return ret;
}