1 /**
2 * @file lwm2m_gateway.c
3 * @brief
4 *
5 * Copyright (c) 2021 Laird Connectivity
6 *
7 * SPDX-License-Identifier: Apache-2.0
8 */
9
10 /*
11 * Gateway
12 * https://github.com/OpenMobileAlliance/lwm2m-registry/blob/prod/25.xml
13 */
14
15 #define LOG_MODULE_NAME net_lwm2m_gateway
16 #define LOG_LEVEL CONFIG_LWM2M_LOG_LEVEL
17
18 #include <zephyr/logging/log.h>
19 LOG_MODULE_REGISTER(LOG_MODULE_NAME);
20
21 #include <string.h>
22 #include <zephyr/init.h>
23
24 #include "lwm2m_object.h"
25 #include "lwm2m_engine.h"
26 #include "lwm2m_resource_ids.h"
27 #include "lwm2m_obj_gateway.h"
28
29 #define GATEWAY_VERSION_MAJOR 2
30 #define GATEWAY_VERSION_MINOR 0
31 #define GATEWAY_MAX_ID 4
32
33 #define MAX_INSTANCE_COUNT CONFIG_LWM2M_GATEWAY_MAX_INSTANCES
34
35 BUILD_ASSERT(CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE >= CONFIG_LWM2M_GATEWAY_PREFIX_MAX_STR_SIZE,
36 "Gateway prefix requires validation");
37
38 /*
39 * Calculate resource instances as follows:
40 * start with GATEWAY_MAX_ID
41 * subtract EXEC resources (1)
42 */
43 #define RESOURCE_INSTANCE_COUNT (GATEWAY_MAX_ID - 1)
44
45 struct lwm2m_gw_obj {
46 char device_id[CONFIG_LWM2M_GATEWAY_DEVICE_ID_MAX_STR_SIZE];
47 char prefix[CONFIG_LWM2M_GATEWAY_PREFIX_MAX_STR_SIZE];
48 char iot_device_objects[CONFIG_LWM2M_GATEWAY_IOT_DEVICE_OBJECTS_MAX_STR_SIZE];
49 };
50
51 static struct lwm2m_gw_obj device_table[MAX_INSTANCE_COUNT];
52 static struct lwm2m_engine_obj lwm2m_gw;
53 static struct lwm2m_engine_obj_field fields[] = {
54 OBJ_FIELD_DATA(LWM2M_GATEWAY_DEVICE_RID, R, STRING),
55 OBJ_FIELD_DATA(LWM2M_GATEWAY_PREFIX_RID, RW, STRING),
56 OBJ_FIELD_DATA(LWM2M_GATEWAY_IOT_DEVICE_OBJECTS_RID, R, STRING),
57 };
58
59 static struct lwm2m_engine_obj_inst inst[MAX_INSTANCE_COUNT];
60 static struct lwm2m_engine_res res[MAX_INSTANCE_COUNT][GATEWAY_MAX_ID];
61 static struct lwm2m_engine_res_inst res_inst[MAX_INSTANCE_COUNT][RESOURCE_INSTANCE_COUNT];
62 lwm2m_engine_gateway_msg_cb gateway_msg_cb[MAX_INSTANCE_COUNT];
63
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)64 static int prefix_validation_cb(uint16_t obj_inst_id, uint16_t res_id, uint16_t res_inst_id,
65 uint8_t *data, uint16_t data_len, bool last_block,
66 size_t total_size)
67 {
68 int i;
69 int length;
70 bool unique = true;
71
72 /* Prefix can't be empty because it is used to reference device */
73 if (data_len == 0) {
74 return -EINVAL;
75 }
76
77 /* Prefix of each gateway must be unique */
78 for (i = 0; i < MAX_INSTANCE_COUNT; i++) {
79 length = strlen(device_table[i].prefix);
80 if (length == data_len) {
81 if (strncmp(device_table[i].prefix, data, length) == 0) {
82 if (inst[i].obj_inst_id != obj_inst_id) {
83 unique = false;
84 break;
85 }
86 }
87 }
88 }
89
90 return unique ? 0 : -EINVAL;
91 }
92
lwm2m_gw_create(uint16_t obj_inst_id)93 static struct lwm2m_engine_obj_inst *lwm2m_gw_create(uint16_t obj_inst_id)
94 {
95 int index, i = 0, j = 0;
96
97 /* Check that there is no other instance with this ID */
98 for (index = 0; index < MAX_INSTANCE_COUNT; index++) {
99 if (inst[index].obj && inst[index].obj_inst_id == obj_inst_id) {
100 LOG_ERR("Can not create instance - "
101 "already existing: %u",
102 obj_inst_id);
103 return NULL;
104 }
105 }
106
107 for (index = 0; index < MAX_INSTANCE_COUNT; index++) {
108 if (!inst[index].obj) {
109 break;
110 }
111 }
112
113 if (index >= MAX_INSTANCE_COUNT) {
114 LOG_ERR("Can not create instance - no more room: %u", obj_inst_id);
115 return NULL;
116 }
117
118 /* Set default values */
119 strncpy(device_table[index].device_id, CONFIG_LWM2M_GATEWAY_DEFAULT_DEVICE_ID,
120 CONFIG_LWM2M_GATEWAY_DEVICE_ID_MAX_STR_SIZE);
121 snprintk(device_table[index].prefix, CONFIG_LWM2M_GATEWAY_PREFIX_MAX_STR_SIZE,
122 CONFIG_LWM2M_GATEWAY_DEFAULT_DEVICE_PREFIX "%u", index);
123 strncpy(device_table[index].iot_device_objects,
124 CONFIG_LWM2M_GATEWAY_DEFAULT_IOT_DEVICE_OBJECTS,
125 CONFIG_LWM2M_GATEWAY_IOT_DEVICE_OBJECTS_MAX_STR_SIZE);
126
127 (void)memset(res[index], 0, sizeof(res[index][0]) * ARRAY_SIZE(res[index]));
128 init_res_instance(res_inst[index], ARRAY_SIZE(res_inst[index]));
129
130 /* initialize instance resource data */
131 INIT_OBJ_RES_DATA_LEN(LWM2M_GATEWAY_DEVICE_RID, res[index], i, res_inst[index], j,
132 device_table[index].device_id,
133 CONFIG_LWM2M_GATEWAY_DEVICE_ID_MAX_STR_SIZE,
134 strlen(device_table[index].device_id) + 1);
135 INIT_OBJ_RES_LEN(LWM2M_GATEWAY_PREFIX_RID, res[index], i, res_inst[index], j, 1, false,
136 true, device_table[index].prefix, CONFIG_LWM2M_GATEWAY_PREFIX_MAX_STR_SIZE,
137 strlen(device_table[index].prefix) + 1, NULL, NULL, prefix_validation_cb,
138 NULL, NULL);
139 INIT_OBJ_RES_DATA_LEN(LWM2M_GATEWAY_IOT_DEVICE_OBJECTS_RID, res[index], i, res_inst[index],
140 j, device_table[index].iot_device_objects,
141 sizeof(device_table[index].iot_device_objects),
142 strlen(device_table[index].iot_device_objects) + 1);
143
144 inst[index].resources = res[index];
145 inst[index].resource_count = i;
146 LOG_DBG("Created LWM2M gateway instance: %d", obj_inst_id);
147 return &inst[index];
148 }
149
lwm2m_gw_handle_req(struct lwm2m_message * msg)150 int lwm2m_gw_handle_req(struct lwm2m_message *msg)
151 {
152 struct coap_option options[4];
153 int ret;
154
155 ret = coap_find_options(msg->in.in_cpkt, COAP_OPTION_URI_PATH, options,
156 ARRAY_SIZE(options));
157 if (ret < 0) {
158 return ret;
159 }
160
161 for (int index = 0; index < MAX_INSTANCE_COUNT; index++) {
162 /* Skip uninitialized objects */
163 if (!inst[index].obj) {
164 continue;
165 }
166
167 char *prefix = device_table[index].prefix;
168 size_t prefix_len = strlen(prefix);
169
170 if (prefix_len != options[0].len) {
171 continue;
172 }
173 if (strncmp(options[0].value, prefix, prefix_len) != 0) {
174 continue;
175 }
176
177 if (gateway_msg_cb[index] == NULL) {
178 return -ENOENT;
179 }
180 /* Delete prefix from path*/
181 ret = coap_options_to_path(&options[1], ret - 1, &msg->path);
182 if (ret < 0) {
183 return ret;
184 }
185 return gateway_msg_cb[index](msg);
186 }
187 return -ENOENT;
188 }
189
lwm2m_register_gw_callback(uint16_t obj_inst_id,lwm2m_engine_gateway_msg_cb cb)190 int lwm2m_register_gw_callback(uint16_t obj_inst_id, lwm2m_engine_gateway_msg_cb cb)
191 {
192 for (int index = 0; index < MAX_INSTANCE_COUNT; index++) {
193 if (inst[index].obj_inst_id == obj_inst_id) {
194 gateway_msg_cb[index] = cb;
195 return 0;
196 }
197 }
198 return -ENOENT;
199 }
200
lwm2m_gw_init(void)201 static int lwm2m_gw_init(void)
202 {
203 int ret = 0;
204
205 /* initialize the LwM2M Gateway field data */
206 lwm2m_gw.obj_id = LWM2M_OBJECT_GATEWAY_ID;
207 lwm2m_gw.version_major = GATEWAY_VERSION_MAJOR;
208 lwm2m_gw.version_minor = GATEWAY_VERSION_MINOR;
209 lwm2m_gw.is_core = false;
210 lwm2m_gw.fields = fields;
211 lwm2m_gw.field_count = ARRAY_SIZE(fields);
212 lwm2m_gw.max_instance_count = MAX_INSTANCE_COUNT;
213 lwm2m_gw.create_cb = lwm2m_gw_create;
214 lwm2m_register_obj(&lwm2m_gw);
215 return ret;
216 }
217
218 LWM2M_OBJ_INIT(lwm2m_gw_init);
219