mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
Merge pull request #13386 from kb2ma/gcoap/sock_async
net/gcoap: use sock_async and events
This commit is contained in:
commit
cba056d560
@ -855,8 +855,11 @@ endif
|
||||
|
||||
ifneq (,$(filter gcoap,$(USEMODULE)))
|
||||
USEMODULE += nanocoap
|
||||
USEMODULE += gnrc_sock_udp
|
||||
USEMODULE += gnrc_sock_async
|
||||
USEMODULE += sock_async_event
|
||||
USEMODULE += sock_util
|
||||
USEMODULE += event_callback
|
||||
USEMODULE += event_timeout
|
||||
endif
|
||||
|
||||
ifneq (,$(filter luid,$(USEMODULE)))
|
||||
|
@ -347,6 +347,8 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "event/callback.h"
|
||||
#include "event/timeout.h"
|
||||
#include "net/ipv6/addr.h"
|
||||
#include "net/sock/udp.h"
|
||||
#include "net/nanocoap.h"
|
||||
@ -362,13 +364,6 @@ extern "C" {
|
||||
* @ingroup config
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* @brief Size for module message queue
|
||||
*/
|
||||
#ifndef CONFIG_GCOAP_MSG_QUEUE_SIZE
|
||||
#define CONFIG_GCOAP_MSG_QUEUE_SIZE (4)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Server port; use RFC 7252 default if not defined
|
||||
*/
|
||||
@ -481,14 +476,6 @@ extern "C" {
|
||||
*/
|
||||
#define GCOAP_SEND_LIMIT_NON (-1)
|
||||
|
||||
/**
|
||||
* @ingroup net_gcoap_conf
|
||||
* @brief Time in usec that the event loop waits for an incoming CoAP message
|
||||
*/
|
||||
#ifndef CONFIG_GCOAP_RECV_TIMEOUT
|
||||
#define CONFIG_GCOAP_RECV_TIMEOUT (1 * US_PER_SEC)
|
||||
#endif
|
||||
|
||||
#ifdef DOXYGEN
|
||||
/**
|
||||
* @ingroup net_gcoap_conf
|
||||
@ -512,19 +499,6 @@ extern "C" {
|
||||
#define CONFIG_GCOAP_NON_TIMEOUT (5000000U)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Identifies waiting timed out for a response to a sent message
|
||||
*/
|
||||
#define GCOAP_MSG_TYPE_TIMEOUT (0x1501)
|
||||
|
||||
/**
|
||||
* @brief Identifies a request to interrupt listening for an incoming message
|
||||
* on a sock
|
||||
*
|
||||
* Allows the event loop to process IPC messages.
|
||||
*/
|
||||
#define GCOAP_MSG_TYPE_INTR (0x1502)
|
||||
|
||||
/**
|
||||
* @ingroup net_gcoap_conf
|
||||
* @brief Maximum number of Observe clients
|
||||
@ -692,8 +666,8 @@ struct gcoap_request_memo {
|
||||
sock_udp_ep_t remote_ep; /**< Remote endpoint */
|
||||
gcoap_resp_handler_t resp_handler; /**< Callback for the response */
|
||||
void *context; /**< ptr to user defined context data */
|
||||
xtimer_t response_timer; /**< Limits wait for response */
|
||||
msg_t timeout_msg; /**< For response timer */
|
||||
event_timeout_t resp_evt_tmout; /**< Limits wait for response */
|
||||
event_callback_t resp_tmout_cb; /**< Callback for response timeout */
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -27,6 +27,7 @@
|
||||
|
||||
#include "assert.h"
|
||||
#include "net/gcoap.h"
|
||||
#include "net/sock/async/event.h"
|
||||
#include "net/sock/util.h"
|
||||
#include "mutex.h"
|
||||
#include "random.h"
|
||||
@ -45,7 +46,7 @@
|
||||
|
||||
/* Internal functions */
|
||||
static void *_event_loop(void *arg);
|
||||
static void _listen(sock_udp_t *sock);
|
||||
static void _on_sock_evt(sock_udp_t *sock, sock_async_flags_t type);
|
||||
static ssize_t _well_known_core_handler(coap_pkt_t* pdu, uint8_t *buf, size_t len, void *ctx);
|
||||
static size_t _handle_req(coap_pkt_t *pdu, uint8_t *buf, size_t len,
|
||||
sock_udp_ep_t *remote);
|
||||
@ -98,19 +99,16 @@ static gcoap_state_t _coap_state = {
|
||||
|
||||
static kernel_pid_t _pid = KERNEL_PID_UNDEF;
|
||||
static char _msg_stack[GCOAP_STACK_SIZE];
|
||||
static msg_t _msg_queue[CONFIG_GCOAP_MSG_QUEUE_SIZE];
|
||||
static event_queue_t _queue;
|
||||
static uint8_t _listen_buf[CONFIG_GCOAP_PDU_BUF_SIZE];
|
||||
static sock_udp_t _sock;
|
||||
|
||||
|
||||
/* Event/Message loop for gcoap _pid thread. */
|
||||
/* Event loop for gcoap _pid thread. */
|
||||
static void *_event_loop(void *arg)
|
||||
{
|
||||
msg_t msg_rcvd;
|
||||
(void)arg;
|
||||
|
||||
msg_init_queue(_msg_queue, CONFIG_GCOAP_MSG_QUEUE_SIZE);
|
||||
|
||||
sock_udp_ep_t local;
|
||||
memset(&local, 0, sizeof(sock_udp_ep_t));
|
||||
local.family = AF_INET6;
|
||||
@ -123,150 +121,130 @@ static void *_event_loop(void *arg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
while(1) {
|
||||
res = msg_try_receive(&msg_rcvd);
|
||||
|
||||
if (res > 0) {
|
||||
switch (msg_rcvd.type) {
|
||||
case GCOAP_MSG_TYPE_TIMEOUT: {
|
||||
gcoap_request_memo_t *memo = (gcoap_request_memo_t *)msg_rcvd.content.ptr;
|
||||
|
||||
/* no retries remaining */
|
||||
if ((memo->send_limit == GCOAP_SEND_LIMIT_NON)
|
||||
|| (memo->send_limit == 0)) {
|
||||
_expire_request(memo);
|
||||
}
|
||||
/* reduce retries remaining, double timeout and resend */
|
||||
else {
|
||||
memo->send_limit--;
|
||||
#ifdef CONFIG_GCOAP_NO_RETRANS_BACKOFF
|
||||
unsigned i = 0;
|
||||
#else
|
||||
unsigned i = COAP_MAX_RETRANSMIT - memo->send_limit;
|
||||
#endif
|
||||
uint32_t timeout = ((uint32_t)COAP_ACK_TIMEOUT << i) * US_PER_SEC;
|
||||
#if COAP_RANDOM_FACTOR_1000 > 1000
|
||||
uint32_t end = ((uint32_t)TIMEOUT_RANGE_END << i) * US_PER_SEC;
|
||||
timeout = random_uint32_range(timeout, end);
|
||||
#endif
|
||||
|
||||
ssize_t bytes = sock_udp_send(&_sock, memo->msg.data.pdu_buf,
|
||||
memo->msg.data.pdu_len,
|
||||
&memo->remote_ep);
|
||||
if (bytes > 0) {
|
||||
xtimer_set_msg(&memo->response_timer, timeout,
|
||||
&memo->timeout_msg, _pid);
|
||||
}
|
||||
else {
|
||||
DEBUG("gcoap: sock resend failed: %d\n", (int)bytes);
|
||||
_expire_request(memo);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_listen(&_sock);
|
||||
}
|
||||
event_queue_init(&_queue);
|
||||
sock_udp_event_init(&_sock, &_queue, _on_sock_evt);
|
||||
event_loop(&_queue);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Listen for an incoming CoAP message. */
|
||||
static void _listen(sock_udp_t *sock)
|
||||
/* Handles sock events from the event queue. */
|
||||
static void _on_sock_evt(sock_udp_t *sock, sock_async_flags_t type)
|
||||
{
|
||||
coap_pkt_t pdu;
|
||||
sock_udp_ep_t remote;
|
||||
gcoap_request_memo_t *memo = NULL;
|
||||
uint8_t open_reqs = gcoap_op_state();
|
||||
|
||||
/* We expect a -EINTR response here when unlimited waiting (SOCK_NO_TIMEOUT)
|
||||
* is interrupted when sending a message in gcoap_req_send(). While a
|
||||
* request is outstanding, sock_udp_recv() is called here with limited
|
||||
* waiting so the request's timeout can be handled in a timely manner in
|
||||
* _event_loop(). */
|
||||
ssize_t res = sock_udp_recv(sock, _listen_buf, sizeof(_listen_buf),
|
||||
open_reqs > 0 ? CONFIG_GCOAP_RECV_TIMEOUT : SOCK_NO_TIMEOUT,
|
||||
&remote);
|
||||
if (res <= 0) {
|
||||
#if ENABLE_DEBUG
|
||||
if (res < 0 && res != -ETIMEDOUT) {
|
||||
DEBUG("gcoap: udp recv failure: %d\n", res);
|
||||
if (type & SOCK_ASYNC_MSG_RECV) {
|
||||
ssize_t res = sock_udp_recv(sock, _listen_buf, sizeof(_listen_buf),
|
||||
0, &remote);
|
||||
if (res <= 0) {
|
||||
DEBUG("gcoap: udp recv failure: %d\n", (int)res);
|
||||
return;
|
||||
}
|
||||
|
||||
res = coap_parse(&pdu, _listen_buf, res);
|
||||
if (res < 0) {
|
||||
DEBUG("gcoap: parse failure: %d\n", (int)res);
|
||||
/* If a response, can't clear memo, but it will timeout later. */
|
||||
return;
|
||||
}
|
||||
|
||||
if (pdu.hdr->code == COAP_CODE_EMPTY) {
|
||||
DEBUG("gcoap: empty messages not handled yet\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* validate class and type for incoming */
|
||||
switch (coap_get_code_class(&pdu)) {
|
||||
/* incoming request */
|
||||
case COAP_CLASS_REQ:
|
||||
if (coap_get_type(&pdu) == COAP_TYPE_NON
|
||||
|| coap_get_type(&pdu) == COAP_TYPE_CON) {
|
||||
size_t pdu_len = _handle_req(&pdu, _listen_buf, sizeof(_listen_buf),
|
||||
&remote);
|
||||
if (pdu_len > 0) {
|
||||
ssize_t bytes = sock_udp_send(sock, _listen_buf, pdu_len,
|
||||
&remote);
|
||||
if (bytes <= 0) {
|
||||
DEBUG("gcoap: send response failed: %d\n", (int)bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
DEBUG("gcoap: illegal request type: %u\n", coap_get_type(&pdu));
|
||||
}
|
||||
break;
|
||||
|
||||
/* incoming response */
|
||||
case COAP_CLASS_SUCCESS:
|
||||
case COAP_CLASS_CLIENT_FAILURE:
|
||||
case COAP_CLASS_SERVER_FAILURE:
|
||||
_find_req_memo(&memo, &pdu, &remote);
|
||||
if (memo) {
|
||||
switch (coap_get_type(&pdu)) {
|
||||
case COAP_TYPE_NON:
|
||||
case COAP_TYPE_ACK:
|
||||
if (memo->resp_evt_tmout.queue) {
|
||||
event_timeout_clear(&memo->resp_evt_tmout);
|
||||
}
|
||||
memo->state = GCOAP_MEMO_RESP;
|
||||
if (memo->resp_handler) {
|
||||
memo->resp_handler(memo, &pdu, &remote);
|
||||
}
|
||||
|
||||
if (memo->send_limit >= 0) { /* if confirmable */
|
||||
*memo->msg.data.pdu_buf = 0; /* clear resend PDU buffer */
|
||||
}
|
||||
memo->state = GCOAP_MEMO_UNUSED;
|
||||
break;
|
||||
case COAP_TYPE_CON:
|
||||
DEBUG("gcoap: separate CON response not handled yet\n");
|
||||
break;
|
||||
default:
|
||||
DEBUG("gcoap: illegal response type: %u\n", coap_get_type(&pdu));
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
DEBUG("gcoap: msg not found for ID: %u\n", coap_get_id(&pdu));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
DEBUG("gcoap: illegal code class: %u\n", coap_get_code_class(&pdu));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Handles response timeout for a request; resend confirmable if needed. */
|
||||
static void _on_resp_timeout(void *arg) {
|
||||
gcoap_request_memo_t *memo = (gcoap_request_memo_t *)arg;
|
||||
|
||||
/* no retries remaining */
|
||||
if ((memo->send_limit == GCOAP_SEND_LIMIT_NON) || (memo->send_limit == 0)) {
|
||||
_expire_request(memo);
|
||||
}
|
||||
/* reduce retries remaining, double timeout and resend */
|
||||
else {
|
||||
memo->send_limit--;
|
||||
#ifdef CONFIG_GCOAP_NO_RETRANS_BACKOFF
|
||||
unsigned i = 0;
|
||||
#else
|
||||
unsigned i = COAP_MAX_RETRANSMIT - memo->send_limit;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
uint32_t timeout = ((uint32_t)COAP_ACK_TIMEOUT << i) * US_PER_SEC;
|
||||
#if COAP_RANDOM_FACTOR_1000 > 1000
|
||||
uint32_t end = ((uint32_t)TIMEOUT_RANGE_END << i) * US_PER_SEC;
|
||||
timeout = random_uint32_range(timeout, end);
|
||||
#endif
|
||||
event_timeout_set(&memo->resp_evt_tmout, timeout);
|
||||
|
||||
res = coap_parse(&pdu, _listen_buf, res);
|
||||
if (res < 0) {
|
||||
DEBUG("gcoap: parse failure: %d\n", (int)res);
|
||||
/* If a response, can't clear memo, but it will timeout later. */
|
||||
return;
|
||||
}
|
||||
|
||||
if (pdu.hdr->code == COAP_CODE_EMPTY) {
|
||||
DEBUG("gcoap: empty messages not handled yet\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* validate class and type for incoming */
|
||||
switch (coap_get_code_class(&pdu)) {
|
||||
/* incoming request */
|
||||
case COAP_CLASS_REQ:
|
||||
if (coap_get_type(&pdu) == COAP_TYPE_NON
|
||||
|| coap_get_type(&pdu) == COAP_TYPE_CON) {
|
||||
size_t pdu_len = _handle_req(&pdu, _listen_buf, sizeof(_listen_buf),
|
||||
&remote);
|
||||
if (pdu_len > 0) {
|
||||
ssize_t bytes = sock_udp_send(sock, _listen_buf, pdu_len,
|
||||
&remote);
|
||||
if (bytes <= 0) {
|
||||
DEBUG("gcoap: send response failed: %d\n", (int)bytes);
|
||||
}
|
||||
}
|
||||
ssize_t bytes = sock_udp_send(&_sock, memo->msg.data.pdu_buf,
|
||||
memo->msg.data.pdu_len, &memo->remote_ep);
|
||||
if (bytes <= 0) {
|
||||
DEBUG("gcoap: sock resend failed: %d\n", (int)bytes);
|
||||
_expire_request(memo);
|
||||
}
|
||||
else {
|
||||
DEBUG("gcoap: illegal request type: %u\n", coap_get_type(&pdu));
|
||||
}
|
||||
break;
|
||||
|
||||
/* incoming response */
|
||||
case COAP_CLASS_SUCCESS:
|
||||
case COAP_CLASS_CLIENT_FAILURE:
|
||||
case COAP_CLASS_SERVER_FAILURE:
|
||||
_find_req_memo(&memo, &pdu, &remote);
|
||||
if (memo) {
|
||||
switch (coap_get_type(&pdu)) {
|
||||
case COAP_TYPE_NON:
|
||||
case COAP_TYPE_ACK:
|
||||
xtimer_remove(&memo->response_timer);
|
||||
memo->state = GCOAP_MEMO_RESP;
|
||||
if (memo->resp_handler) {
|
||||
memo->resp_handler(memo, &pdu, &remote);
|
||||
}
|
||||
|
||||
if (memo->send_limit >= 0) { /* if confirmable */
|
||||
*memo->msg.data.pdu_buf = 0; /* clear resend PDU buffer */
|
||||
}
|
||||
memo->state = GCOAP_MEMO_UNUSED;
|
||||
break;
|
||||
case COAP_TYPE_CON:
|
||||
DEBUG("gcoap: separate CON response not handled yet\n");
|
||||
break;
|
||||
default:
|
||||
DEBUG("gcoap: illegal response type: %u\n", coap_get_type(&pdu));
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
DEBUG("gcoap: msg not found for ID: %u\n", coap_get_id(&pdu));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
DEBUG("gcoap: illegal code class: %u\n", coap_get_code_class(&pdu));
|
||||
}
|
||||
}
|
||||
|
||||
@ -798,38 +776,28 @@ size_t gcoap_req_send(const uint8_t *buf, size_t len,
|
||||
}
|
||||
}
|
||||
|
||||
/* Memos complete; send msg and start timer */
|
||||
ssize_t res = sock_udp_send(&_sock, buf, len, remote);
|
||||
|
||||
/* timeout may be zero for non-confirmable */
|
||||
if ((memo != NULL) && (res > 0) && (timeout > 0)) {
|
||||
/* We assume gcoap_req_send() is called on some thread other than
|
||||
* gcoap's. First, put a message in the mbox for the sock udp object,
|
||||
* which will interrupt listening on the gcoap thread. (When there are
|
||||
* no outstanding requests, gcoap blocks indefinitely in _listen() at
|
||||
* sock_udp_recv().) While the message sent here is outstanding, the
|
||||
* sock_udp_recv() call will be set to a short timeout so the request
|
||||
* timer below, also on the gcoap thread, is processed in a timely
|
||||
* manner. */
|
||||
msg_t mbox_msg;
|
||||
mbox_msg.type = GCOAP_MSG_TYPE_INTR;
|
||||
mbox_msg.content.value = 0;
|
||||
if (mbox_try_put(&_sock.reg.mbox, &mbox_msg)) {
|
||||
/* start response wait timer on the gcoap thread */
|
||||
memo->timeout_msg.type = GCOAP_MSG_TYPE_TIMEOUT;
|
||||
memo->timeout_msg.content.ptr = (char *)memo;
|
||||
xtimer_set_msg(&memo->response_timer, timeout, &memo->timeout_msg, _pid);
|
||||
/* set response timeout; may be zero for non-confirmable */
|
||||
if (memo != NULL) {
|
||||
if (timeout > 0) {
|
||||
event_callback_init(&memo->resp_tmout_cb, _on_resp_timeout, memo);
|
||||
event_timeout_init(&memo->resp_evt_tmout, &_queue,
|
||||
&memo->resp_tmout_cb.super);
|
||||
event_timeout_set(&memo->resp_evt_tmout, timeout);
|
||||
}
|
||||
else {
|
||||
res = 0;
|
||||
DEBUG("gcoap: can't wake up mbox; no timeout for msg\n");
|
||||
memset(&memo->resp_evt_tmout, 0, sizeof(event_timeout_t));
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t res = sock_udp_send(&_sock, buf, len, remote);
|
||||
if (res <= 0) {
|
||||
if (memo != NULL) {
|
||||
if (msg_type == COAP_TYPE_CON) {
|
||||
*memo->msg.data.pdu_buf = 0; /* clear resend buffer */
|
||||
}
|
||||
if (timeout > 0) {
|
||||
event_timeout_clear(&memo->resp_evt_tmout);
|
||||
}
|
||||
memo->state = GCOAP_MEMO_UNUSED;
|
||||
}
|
||||
DEBUG("gcoap: sock send failed: %d\n", (int)res);
|
||||
|
Loading…
Reference in New Issue
Block a user