2021-10-06 15:47:06 +02:00
|
|
|
/**
|
|
|
|
* @file lwm2m_gateway.c
|
|
|
|
* @brief
|
|
|
|
*
|
|
|
|
* Copyright (c) 2021 Laird Connectivity
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Gateway
|
|
|
|
* https://github.com/OpenMobileAlliance/lwm2m-registry/blob/prod/25.xml
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define LOG_MODULE_NAME net_lwm2m_gateway
|
|
|
|
#define LOG_LEVEL CONFIG_LWM2M_LOG_LEVEL
|
|
|
|
|
2022-05-06 11:12:04 +02:00
|
|
|
#include <zephyr/logging/log.h>
|
2021-10-06 15:47:06 +02:00
|
|
|
LOG_MODULE_REGISTER(LOG_MODULE_NAME);
|
|
|
|
|
|
|
|
#include <string.h>
|
2022-05-06 11:12:04 +02:00
|
|
|
#include <zephyr/init.h>
|
2021-10-06 15:47:06 +02:00
|
|
|
|
|
|
|
#include "lwm2m_object.h"
|
|
|
|
#include "lwm2m_engine.h"
|
|
|
|
#include "lwm2m_resource_ids.h"
|
|
|
|
#include "lwm2m_obj_gateway.h"
|
|
|
|
|
|
|
|
#define GATEWAY_VERSION_MAJOR 2
|
|
|
|
#define GATEWAY_VERSION_MINOR 0
|
|
|
|
#define GATEWAY_MAX_ID 4
|
|
|
|
|
|
|
|
#define MAX_INSTANCE_COUNT CONFIG_LWM2M_GATEWAY_MAX_INSTANCES
|
|
|
|
|
|
|
|
BUILD_ASSERT(CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE >= CONFIG_LWM2M_GATEWAY_PREFIX_MAX_STR_SIZE,
|
|
|
|
"Gateway prefix requires validation");
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Calculate resource instances as follows:
|
|
|
|
* start with GATEWAY_MAX_ID
|
|
|
|
* subtract EXEC resources (1)
|
|
|
|
*/
|
|
|
|
#define RESOURCE_INSTANCE_COUNT (GATEWAY_MAX_ID - 1)
|
|
|
|
|
|
|
|
struct lwm2m_gw_obj {
|
|
|
|
char device_id[CONFIG_LWM2M_GATEWAY_DEVICE_ID_MAX_STR_SIZE];
|
|
|
|
char prefix[CONFIG_LWM2M_GATEWAY_PREFIX_MAX_STR_SIZE];
|
|
|
|
char iot_device_objects[CONFIG_LWM2M_GATEWAY_IOT_DEVICE_OBJECTS_MAX_STR_SIZE];
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct lwm2m_gw_obj device_table[MAX_INSTANCE_COUNT];
|
|
|
|
static struct lwm2m_engine_obj lwm2m_gw;
|
|
|
|
static struct lwm2m_engine_obj_field fields[] = {
|
|
|
|
OBJ_FIELD_DATA(LWM2M_GATEWAY_DEVICE_RID, R, STRING),
|
|
|
|
OBJ_FIELD_DATA(LWM2M_GATEWAY_PREFIX_RID, RW, STRING),
|
|
|
|
OBJ_FIELD_DATA(LWM2M_GATEWAY_IOT_DEVICE_OBJECTS_RID, R, STRING),
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct lwm2m_engine_obj_inst inst[MAX_INSTANCE_COUNT];
|
|
|
|
static struct lwm2m_engine_res res[MAX_INSTANCE_COUNT][GATEWAY_MAX_ID];
|
|
|
|
static struct lwm2m_engine_res_inst res_inst[MAX_INSTANCE_COUNT][RESOURCE_INSTANCE_COUNT];
|
2024-01-08 13:42:28 +01:00
|
|
|
lwm2m_engine_gateway_msg_cb gateway_msg_cb[MAX_INSTANCE_COUNT];
|
2021-10-06 15:47:06 +02:00
|
|
|
|
2024-05-10 12:35:11 +02:00
|
|
|
static int prefix_validation_cb(uint16_t obj_inst_id, uint16_t res_id,
|
|
|
|
uint16_t res_inst_id, uint8_t *data,
|
|
|
|
uint16_t data_len, bool last_block,
|
|
|
|
size_t total_size, size_t offset)
|
2021-10-06 15:47:06 +02:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int length;
|
|
|
|
bool unique = true;
|
|
|
|
|
|
|
|
/* Prefix can't be empty because it is used to reference device */
|
|
|
|
if (data_len == 0) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Prefix of each gateway must be unique */
|
|
|
|
for (i = 0; i < MAX_INSTANCE_COUNT; i++) {
|
|
|
|
length = strlen(device_table[i].prefix);
|
|
|
|
if (length == data_len) {
|
|
|
|
if (strncmp(device_table[i].prefix, data, length) == 0) {
|
|
|
|
if (inst[i].obj_inst_id != obj_inst_id) {
|
|
|
|
unique = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return unique ? 0 : -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct lwm2m_engine_obj_inst *lwm2m_gw_create(uint16_t obj_inst_id)
|
|
|
|
{
|
|
|
|
int index, i = 0, j = 0;
|
|
|
|
|
|
|
|
/* Check that there is no other instance with this ID */
|
|
|
|
for (index = 0; index < MAX_INSTANCE_COUNT; index++) {
|
|
|
|
if (inst[index].obj && inst[index].obj_inst_id == obj_inst_id) {
|
|
|
|
LOG_ERR("Can not create instance - "
|
|
|
|
"already existing: %u",
|
|
|
|
obj_inst_id);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (index = 0; index < MAX_INSTANCE_COUNT; index++) {
|
|
|
|
if (!inst[index].obj) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (index >= MAX_INSTANCE_COUNT) {
|
|
|
|
LOG_ERR("Can not create instance - no more room: %u", obj_inst_id);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set default values */
|
|
|
|
strncpy(device_table[index].device_id, CONFIG_LWM2M_GATEWAY_DEFAULT_DEVICE_ID,
|
|
|
|
CONFIG_LWM2M_GATEWAY_DEVICE_ID_MAX_STR_SIZE);
|
|
|
|
snprintk(device_table[index].prefix, CONFIG_LWM2M_GATEWAY_PREFIX_MAX_STR_SIZE,
|
|
|
|
CONFIG_LWM2M_GATEWAY_DEFAULT_DEVICE_PREFIX "%u", index);
|
|
|
|
strncpy(device_table[index].iot_device_objects,
|
|
|
|
CONFIG_LWM2M_GATEWAY_DEFAULT_IOT_DEVICE_OBJECTS,
|
|
|
|
CONFIG_LWM2M_GATEWAY_IOT_DEVICE_OBJECTS_MAX_STR_SIZE);
|
|
|
|
|
|
|
|
(void)memset(res[index], 0, sizeof(res[index][0]) * ARRAY_SIZE(res[index]));
|
|
|
|
init_res_instance(res_inst[index], ARRAY_SIZE(res_inst[index]));
|
|
|
|
|
|
|
|
/* initialize instance resource data */
|
2023-10-17 12:47:47 +02:00
|
|
|
INIT_OBJ_RES_DATA_LEN(LWM2M_GATEWAY_DEVICE_RID, res[index], i, res_inst[index], j,
|
|
|
|
device_table[index].device_id,
|
|
|
|
CONFIG_LWM2M_GATEWAY_DEVICE_ID_MAX_STR_SIZE,
|
|
|
|
strlen(device_table[index].device_id) + 1);
|
|
|
|
INIT_OBJ_RES_LEN(LWM2M_GATEWAY_PREFIX_RID, res[index], i, res_inst[index], j, 1, false,
|
|
|
|
true, device_table[index].prefix, CONFIG_LWM2M_GATEWAY_PREFIX_MAX_STR_SIZE,
|
|
|
|
strlen(device_table[index].prefix) + 1, NULL, NULL, prefix_validation_cb,
|
|
|
|
NULL, NULL);
|
|
|
|
INIT_OBJ_RES_DATA_LEN(LWM2M_GATEWAY_IOT_DEVICE_OBJECTS_RID, res[index], i, res_inst[index],
|
|
|
|
j, device_table[index].iot_device_objects,
|
|
|
|
sizeof(device_table[index].iot_device_objects),
|
|
|
|
strlen(device_table[index].iot_device_objects) + 1);
|
2021-10-06 15:47:06 +02:00
|
|
|
|
|
|
|
inst[index].resources = res[index];
|
|
|
|
inst[index].resource_count = i;
|
|
|
|
LOG_DBG("Created LWM2M gateway instance: %d", obj_inst_id);
|
|
|
|
return &inst[index];
|
|
|
|
}
|
|
|
|
|
2024-01-08 13:42:28 +01:00
|
|
|
int lwm2m_gw_handle_req(struct lwm2m_message *msg)
|
|
|
|
{
|
|
|
|
struct coap_option options[4];
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = coap_find_options(msg->in.in_cpkt, COAP_OPTION_URI_PATH, options,
|
|
|
|
ARRAY_SIZE(options));
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int index = 0; index < MAX_INSTANCE_COUNT; index++) {
|
|
|
|
/* Skip uninitialized objects */
|
|
|
|
if (!inst[index].obj) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
char *prefix = device_table[index].prefix;
|
|
|
|
size_t prefix_len = strlen(prefix);
|
|
|
|
|
|
|
|
if (prefix_len != options[0].len) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (strncmp(options[0].value, prefix, prefix_len) != 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (gateway_msg_cb[index] == NULL) {
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
/* Delete prefix from path*/
|
|
|
|
ret = coap_options_to_path(&options[1], ret - 1, &msg->path);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
return gateway_msg_cb[index](msg);
|
|
|
|
}
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
int lwm2m_register_gw_callback(uint16_t obj_inst_id, lwm2m_engine_gateway_msg_cb cb)
|
|
|
|
{
|
|
|
|
for (int index = 0; index < MAX_INSTANCE_COUNT; index++) {
|
|
|
|
if (inst[index].obj_inst_id == obj_inst_id) {
|
|
|
|
gateway_msg_cb[index] = cb;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
init: remove the need for a dummy device pointer in SYS_INIT functions
The init infrastructure, found in `init.h`, is currently used by:
- `SYS_INIT`: to call functions before `main`
- `DEVICE_*`: to initialize devices
They are all sorted according to an initialization level + a priority.
`SYS_INIT` calls are really orthogonal to devices, however, the required
function signature requires a `const struct device *dev` as a first
argument. The only reason for that is because the same init machinery is
used by devices, so we have something like:
```c
struct init_entry {
int (*init)(const struct device *dev);
/* only set by DEVICE_*, otherwise NULL */
const struct device *dev;
}
```
As a result, we end up with such weird/ugly pattern:
```c
static int my_init(const struct device *dev)
{
/* always NULL! add ARG_UNUSED to avoid compiler warning */
ARG_UNUSED(dev);
...
}
```
This is really a result of poor internals isolation. This patch proposes
a to make init entries more flexible so that they can accept sytem
initialization calls like this:
```c
static int my_init(void)
{
...
}
```
This is achieved using a union:
```c
union init_function {
/* for SYS_INIT, used when init_entry.dev == NULL */
int (*sys)(void);
/* for DEVICE*, used when init_entry.dev != NULL */
int (*dev)(const struct device *dev);
};
struct init_entry {
/* stores init function (either for SYS_INIT or DEVICE*)
union init_function init_fn;
/* stores device pointer for DEVICE*, NULL for SYS_INIT. Allows
* to know which union entry to call.
*/
const struct device *dev;
}
```
This solution **does not increase ROM usage**, and allows to offer clean
public APIs for both SYS_INIT and DEVICE*. Note that however, init
machinery keeps a coupling with devices.
**NOTE**: This is a breaking change! All `SYS_INIT` functions will need
to be converted to the new signature. See the script offered in the
following commit.
Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
init: convert SYS_INIT functions to the new signature
Conversion scripted using scripts/utils/migrate_sys_init.py.
Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
manifest: update projects for SYS_INIT changes
Update modules with updated SYS_INIT calls:
- hal_ti
- lvgl
- sof
- TraceRecorderSource
Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
tests: devicetree: devices: adjust test
Adjust test according to the recently introduced SYS_INIT
infrastructure.
Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
tests: kernel: threads: adjust SYS_INIT call
Adjust to the new signature: int (*init_fn)(void);
Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
2022-10-19 09:33:44 +02:00
|
|
|
static int lwm2m_gw_init(void)
|
2021-10-06 15:47:06 +02:00
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
/* initialize the LwM2M Gateway field data */
|
|
|
|
lwm2m_gw.obj_id = LWM2M_OBJECT_GATEWAY_ID;
|
|
|
|
lwm2m_gw.version_major = GATEWAY_VERSION_MAJOR;
|
|
|
|
lwm2m_gw.version_minor = GATEWAY_VERSION_MINOR;
|
2024-01-19 15:14:37 +01:00
|
|
|
lwm2m_gw.is_core = false;
|
2021-10-06 15:47:06 +02:00
|
|
|
lwm2m_gw.fields = fields;
|
|
|
|
lwm2m_gw.field_count = ARRAY_SIZE(fields);
|
|
|
|
lwm2m_gw.max_instance_count = MAX_INSTANCE_COUNT;
|
|
|
|
lwm2m_gw.create_cb = lwm2m_gw_create;
|
|
|
|
lwm2m_register_obj(&lwm2m_gw);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2024-01-19 15:14:37 +01:00
|
|
|
LWM2M_OBJ_INIT(lwm2m_gw_init);
|