blob: 6f038ce61e17797e0680a3da6284196e1301d3a5 [file] [log] [blame]
/*
* Copyright (c) 2023 Basalte bv
*
* SPDX-License-Identifier: Apache-2.0
*/
/** @file
* @brief CoAP Service API
*
* An API for applications to respond to CoAP requests
*/
#ifndef ZEPHYR_INCLUDE_NET_COAP_SERVICE_H_
#define ZEPHYR_INCLUDE_NET_COAP_SERVICE_H_
#include <zephyr/net/coap.h>
#include <zephyr/sys/iterable_sections.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief CoAP Service API
* @defgroup coap_service CoAP service API
* @ingroup networking
* @{
*/
/**
* @name CoAP Service configuration flags
* @anchor COAP_SERVICE_FLAGS
* @{
*/
/** Start the service on boot. */
#define COAP_SERVICE_AUTOSTART BIT(0)
/** @} */
/** @cond INTERNAL_HIDDEN */
struct coap_service_data {
int sock_fd;
struct coap_observer observers[CONFIG_COAP_SERVICE_OBSERVERS];
struct coap_pending pending[CONFIG_COAP_SERVICE_PENDING_MESSAGES];
};
struct coap_service {
const char *name;
const char *host;
uint16_t *port;
uint8_t flags;
struct coap_resource *res_begin;
struct coap_resource *res_end;
struct coap_service_data *data;
};
#define __z_coap_service_define(_name, _host, _port, _flags, _res_begin, _res_end) \
static struct coap_service_data coap_service_data_##_name = { \
.sock_fd = -1, \
}; \
const STRUCT_SECTION_ITERABLE(coap_service, _name) = { \
.name = STRINGIFY(_name), \
.host = _host, \
.port = (uint16_t *)(_port), \
.flags = _flags, \
.res_begin = (_res_begin), \
.res_end = (_res_end), \
.data = &coap_service_data_##_name, \
}
/** @endcond */
/**
* @brief Define a static CoAP resource owned by the service named @p _service .
*
* @note The handlers registered with the resource can return a CoAP response code to reply with
* an acknowledge without any payload, nothing is sent if the return value is 0 or negative.
* As seen in the example.
*
* @code{.c}
* static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(DT_ALIAS(led0), gpios);
*
* static int led_put(struct coap_resource *resource, struct coap_packet *request,
* struct sockaddr *addr, socklen_t addr_len)
* {
* const uint8_t *payload;
* uint16_t payload_len;
*
* payload = coap_packet_get_payload(request, &payload_len);
* if (payload_len != 1) {
* return COAP_RESPONSE_CODE_BAD_REQUEST;
* }
*
* if (gpio_pin_set_dt(&led, payload[0]) < 0) {
* return COAP_RESPONSE_CODE_INTERNAL_ERROR;
* }
*
* return COAP_RESPONSE_CODE_CHANGED;
* }
*
* COAP_RESOURCE_DEFINE(my_resource, my_service, {
* .put = led_put,
* });
* @endcode
*
* @param _name Name of the resource.
* @param _service Name of the associated service.
*/
#define COAP_RESOURCE_DEFINE(_name, _service, ...) \
STRUCT_SECTION_ITERABLE_ALTERNATE(coap_resource_##_service, coap_resource, _name) \
= __VA_ARGS__
/**
* @brief Define a CoAP service with static resources.
*
* @note The @p _host parameter can be `NULL`. If not, it is used to specify an IP address either in
* IPv4 or IPv6 format a fully-qualified hostname or a virtual host, otherwise the any address is
* used.
*
* @note The @p _port parameter must be non-`NULL`. It points to a location that specifies the port
* number to use for the service. If the specified port number is zero, then an ephemeral port
* number will be used and the actual port number assigned will be written back to memory. For
* ephemeral port numbers, the memory pointed to by @p _port must be writeable.
*
* @param _name Name of the service.
* @param _host IP address or hostname associated with the service.
* @param[inout] _port Pointer to port associated with the service.
* @param _flags Configuration flags @see @ref COAP_SERVICE_FLAGS.
*/
#define COAP_SERVICE_DEFINE(_name, _host, _port, _flags) \
extern struct coap_resource _CONCAT(_coap_resource_##_name, _list_start)[]; \
extern struct coap_resource _CONCAT(_coap_resource_##_name, _list_end)[]; \
__z_coap_service_define(_name, _host, _port, _flags, \
&_CONCAT(_coap_resource_##_name, _list_start)[0], \
&_CONCAT(_coap_resource_##_name, _list_end)[0])
/**
* @brief Count the number of CoAP services.
*
* @param[out] _dst Pointer to location where result is written.
*/
#define COAP_SERVICE_COUNT(_dst) STRUCT_SECTION_COUNT(coap_service, _dst)
/**
* @brief Count CoAP service static resources.
*
* @param _service Pointer to a service.
*/
#define COAP_SERVICE_RESOURCE_COUNT(_service) ((_service)->res_end - (_service)->res_begin)
/**
* @brief Check if service has the specified resource.
*
* @param _service Pointer to a service.
* @param _resource Pointer to a resource.
*/
#define COAP_SERVICE_HAS_RESOURCE(_service, _resource) \
((_service)->res_begin <= _resource && _resource < (_service)->res_end)
/**
* @brief Iterate over all CoAP services.
*
* @param _it Name of iterator (of type @ref coap_service)
*/
#define COAP_SERVICE_FOREACH(_it) STRUCT_SECTION_FOREACH(coap_service, _it)
/**
* @brief Iterate over static CoAP resources associated with a given @p _service.
*
* @note This macro requires that @p _service is defined with @ref COAP_SERVICE_DEFINE.
*
* @param _service Name of CoAP service
* @param _it Name of iterator (of type @ref coap_resource)
*/
#define COAP_RESOURCE_FOREACH(_service, _it) \
STRUCT_SECTION_FOREACH_ALTERNATE(coap_resource_##_service, coap_resource, _it)
/**
* @brief Iterate over all static resources associated with @p _service .
*
* @note This macro is suitable for a @p _service defined with @ref COAP_SERVICE_DEFINE.
*
* @param _service Pointer to COAP service
* @param _it Name of iterator (of type @ref coap_resource)
*/
#define COAP_SERVICE_FOREACH_RESOURCE(_service, _it) \
for (struct coap_resource *_it = (_service)->res_begin; ({ \
__ASSERT(_it <= (_service)->res_end, "unexpected list end location"); \
_it < (_service)->res_end; \
}); _it++)
/**
* @brief Start the provided @p service .
*
* @note This function is suitable for a @p service defined with @ref COAP_SERVICE_DEFINE.
*
* @param service Pointer to CoAP service
* @retval 0 in case of success.
* @retval -EALREADY in case of an already running service.
* @retval -ENOTSUP in case the server has no valid host and port configuration.
*/
int coap_service_start(const struct coap_service *service);
/**
* @brief Stop the provided @p service .
*
* @note This function is suitable for a @p service defined with @ref COAP_SERVICE_DEFINE.
*
* @param service Pointer to CoAP service
* @retval 0 in case of success.
* @retval -EALREADY in case the service isn't running.
*/
int coap_service_stop(const struct coap_service *service);
/**
* @brief Query the provided @p service running state.
*
* @note This function is suitable for a @p service defined with @ref COAP_SERVICE_DEFINE.
*
* @param service Pointer to CoAP service
* @retval 1 if the service is running
* @retval 0 if the service is stopped
* @retval negative in case of an error.
*/
int coap_service_is_running(const struct coap_service *service);
/**
* @brief Send a CoAP message from the provided @p service .
*
* @note This function is suitable for a @p service defined with @ref COAP_SERVICE_DEFINE.
*
* @param service Pointer to CoAP service
* @param cpkt CoAP Packet to send
* @param addr Peer address
* @param addr_len Peer address length
* @return 0 in case of success or negative in case of error.
*/
int coap_service_send(const struct coap_service *service, const struct coap_packet *cpkt,
const struct sockaddr *addr, socklen_t addr_len);
/**
* @brief Send a CoAP message from the provided @p resource .
*
* @note This function is suitable for a @p resource defined with @ref COAP_RESOURCE_DEFINE.
*
* @param resource Pointer to CoAP resource
* @param cpkt CoAP Packet to send
* @param addr Peer address
* @param addr_len Peer address length
* @return 0 in case of success or negative in case of error.
*/
int coap_resource_send(const struct coap_resource *resource, const struct coap_packet *cpkt,
const struct sockaddr *addr, socklen_t addr_len);
/**
* @brief Parse a CoAP observe request for the provided @p resource .
*
* @note This function is suitable for a @p resource defined with @ref COAP_RESOURCE_DEFINE.
*
* If the observe option value is equal to 0, an observer will be added, if the value is equal
* to 1, an existing observer will be removed.
*
* @param resource Pointer to CoAP resource
* @param request CoAP request to parse
* @param addr Peer address
* @return the observe option value in case of success or negative in case of error.
*/
int coap_resource_parse_observe(struct coap_resource *resource, const struct coap_packet *request,
const struct sockaddr *addr);
/**
* @brief Lookup an observer by address and remove it from the @p resource .
*
* @note This function is suitable for a @p resource defined with @ref COAP_RESOURCE_DEFINE.
*
* @param resource Pointer to CoAP resource
* @param addr Peer address
* @return 0 in case of success or negative in case of error.
*/
int coap_resource_remove_observer_by_addr(struct coap_resource *resource,
const struct sockaddr *addr);
/**
* @brief Lookup an observer by token and remove it from the @p resource .
*
* @note This function is suitable for a @p resource defined with @ref COAP_RESOURCE_DEFINE.
*
* @param resource Pointer to CoAP resource
* @param token Pointer to the token
* @param token_len Length of valid bytes in the token
* @return 0 in case of success or negative in case of error.
*/
int coap_resource_remove_observer_by_token(struct coap_resource *resource,
const uint8_t *token, uint8_t token_len);
/**
* @}
*/
#ifdef __cplusplus
}
#endif
#endif /* ZEPHYR_INCLUDE_NET_COAP_SERVICE_H_ */