1 /*
2 * Copyright (c) 2017 Linaro Limited
3 * Copyright (c) 2018-2019 Foundries.io
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 /*
9 * TODO:
10 * - Configurable CURRENT_TIME notification delay
11 */
12
13 #define LOG_MODULE_NAME net_lwm2m_obj_device
14 #define LOG_LEVEL CONFIG_LWM2M_LOG_LEVEL
15
16 #include <zephyr/logging/log.h>
17 LOG_MODULE_REGISTER(LOG_MODULE_NAME);
18
19 #include <string.h>
20 #include <stdio.h>
21 #include <zephyr/init.h>
22
23 #include "lwm2m_object.h"
24 #include "lwm2m_engine.h"
25
26 #define DEVICE_VERSION_MAJOR 1
27 #define DEVICE_VERSION_MINOR 0
28
29 /* Device resource IDs */
30 #define DEVICE_MANUFACTURER_ID 0
31 #define DEVICE_MODEL_NUMBER_ID 1
32 #define DEVICE_SERIAL_NUMBER_ID 2
33 #define DEVICE_FIRMWARE_VERSION_ID 3
34 #define DEVICE_REBOOT_ID 4
35 #define DEVICE_FACTORY_DEFAULT_ID 5
36 #define DEVICE_AVAILABLE_POWER_SOURCES_ID 6
37 #define DEVICE_POWER_SOURCE_VOLTAGE_ID 7
38 #define DEVICE_POWER_SOURCE_CURRENT_ID 8
39 #define DEVICE_BATTERY_LEVEL_ID 9
40 #define DEVICE_MEMORY_FREE_ID 10
41 #define DEVICE_ERROR_CODE_ID 11
42 #define DEVICE_RESET_ERROR_CODE_ID 12
43 #define DEVICE_CURRENT_TIME_ID 13
44 #define DEVICE_UTC_OFFSET_ID 14
45 #define DEVICE_TIMEZONE_ID 15
46 #define DEVICE_SUPPORTED_BINDING_MODES_ID 16
47 #define DEVICE_TYPE_ID 17
48 #define DEVICE_HARDWARE_VERSION_ID 18
49 #define DEVICE_SOFTWARE_VERSION_ID 19
50 #define DEVICE_BATTERY_STATUS_ID 20
51 #define DEVICE_MEMORY_TOTAL_ID 21
52 #define DEVICE_EXT_DEV_INFO_ID 22
53
54 #define DEVICE_MAX_ID 23
55
56 #ifdef CONFIG_LWM2M_DEVICE_ERROR_CODE_MAX
57 #define DEVICE_ERROR_CODE_MAX CONFIG_LWM2M_DEVICE_ERROR_CODE_MAX
58 #else
59 #define DEVICE_ERROR_CODE_MAX 10
60 #endif
61
62 #ifdef CONFIG_LWM2M_DEVICE_PWRSRC_MAX
63 #define DEVICE_PWRSRC_MAX CONFIG_LWM2M_DEVICE_PWRSRC_MAX
64 #else
65 #define DEVICE_PWRSRC_MAX 5
66 #endif
67
68 #ifdef CONFIG_LWM2M_DEVICE_EXT_DEV_INFO_MAX
69 #define DEVICE_EXT_DEV_INFO_MAX CONFIG_LWM2M_DEVICE_EXT_DEV_INFO_MAX
70 #else
71 #define DEVICE_EXT_DEV_INFO_MAX 1
72 #endif
73
74 #define DEVICE_STRING_SHORT 8
75
76 #define DEVICE_SERVICE_INTERVAL_MS (MSEC_PER_SEC * 10)
77
78 /*
79 * Calculate resource instances as follows:
80 * start with DEVICE_MAX_ID
81 * subtract EXEC resources (3)
82 * subtract MULTI resources because their counts include 0 resource (5)
83 * add 3x DEVICE_PWRSRC_MAX for POWER SOURCES resource instances
84 * add DEVICE_ERROR_CODE_MAX for ERROR CODE resource instances
85 * add DEVICE_EXT_DEV_INFO_MAX for EXT DEV INFO resource instances
86 */
87 #define RESOURCE_INSTANCE_COUNT (DEVICE_MAX_ID - 3 - 5 + \
88 DEVICE_PWRSRC_MAX*3 + DEVICE_ERROR_CODE_MAX + \
89 DEVICE_EXT_DEV_INFO_MAX)
90
91 /* resource state variables */
92 static uint8_t error_code_list[DEVICE_ERROR_CODE_MAX];
93 static time_t time_temp;
94 static time_t time_offset;
95 static uint8_t binding_mode[DEVICE_STRING_SHORT];
96
97 /* only 1 instance of device object exists */
98 static struct lwm2m_engine_obj device;
99 static struct lwm2m_engine_obj_field fields[] = {
100 OBJ_FIELD_DATA(DEVICE_MANUFACTURER_ID, R_OPT, STRING),
101 OBJ_FIELD_DATA(DEVICE_MODEL_NUMBER_ID, R_OPT, STRING),
102 OBJ_FIELD_DATA(DEVICE_SERIAL_NUMBER_ID, R_OPT, STRING),
103 OBJ_FIELD_DATA(DEVICE_FIRMWARE_VERSION_ID, R_OPT, STRING),
104 OBJ_FIELD_EXECUTE_OPT(DEVICE_REBOOT_ID),
105 OBJ_FIELD_EXECUTE_OPT(DEVICE_FACTORY_DEFAULT_ID),
106 OBJ_FIELD_DATA(DEVICE_AVAILABLE_POWER_SOURCES_ID, R_OPT, U8),
107 OBJ_FIELD_DATA(DEVICE_POWER_SOURCE_VOLTAGE_ID, R_OPT, S32),
108 OBJ_FIELD_DATA(DEVICE_POWER_SOURCE_CURRENT_ID, R_OPT, S32),
109 OBJ_FIELD_DATA(DEVICE_BATTERY_LEVEL_ID, R_OPT, U8),
110 OBJ_FIELD_DATA(DEVICE_MEMORY_FREE_ID, R_OPT, S32),
111 OBJ_FIELD_DATA(DEVICE_ERROR_CODE_ID, R, U8),
112 OBJ_FIELD_EXECUTE_OPT(DEVICE_RESET_ERROR_CODE_ID),
113 OBJ_FIELD_DATA(DEVICE_CURRENT_TIME_ID, RW_OPT, TIME),
114 OBJ_FIELD_DATA(DEVICE_UTC_OFFSET_ID, RW_OPT, STRING),
115 OBJ_FIELD_DATA(DEVICE_TIMEZONE_ID, RW_OPT, STRING),
116 OBJ_FIELD_DATA(DEVICE_SUPPORTED_BINDING_MODES_ID, R, STRING),
117 OBJ_FIELD_DATA(DEVICE_TYPE_ID, R_OPT, STRING),
118 OBJ_FIELD_DATA(DEVICE_HARDWARE_VERSION_ID, R_OPT, STRING),
119 OBJ_FIELD_DATA(DEVICE_SOFTWARE_VERSION_ID, R_OPT, STRING),
120 OBJ_FIELD_DATA(DEVICE_BATTERY_STATUS_ID, R_OPT, U8),
121 OBJ_FIELD_DATA(DEVICE_MEMORY_TOTAL_ID, R_OPT, S32),
122 OBJ_FIELD_DATA(DEVICE_EXT_DEV_INFO_ID, R_OPT, OBJLNK)
123 };
124
125 static struct lwm2m_engine_obj_inst inst;
126 static struct lwm2m_engine_res res[DEVICE_MAX_ID];
127 static struct lwm2m_engine_res_inst res_inst[RESOURCE_INSTANCE_COUNT];
128
129 /* save error code resource instance point so we can easily clear later */
130 static struct lwm2m_engine_res_inst *error_code_ri;
131
132 /* callbacks */
133
reset_error_list_cb(uint16_t obj_inst_id,uint8_t * args,uint16_t args_len)134 static int reset_error_list_cb(uint16_t obj_inst_id,
135 uint8_t *args, uint16_t args_len)
136 {
137 int i;
138
139 /* "delete" error codes */
140 for (i = 0; i < DEVICE_ERROR_CODE_MAX; i++) {
141 error_code_list[i] = 0;
142 error_code_ri[i].res_inst_id = RES_INSTANCE_NOT_CREATED;
143 }
144
145 /* Default error code indicating no error */
146 error_code_ri[0].res_inst_id = 0;
147
148 return 0;
149 }
150
current_time_read_cb(uint16_t obj_inst_id,uint16_t res_id,uint16_t res_inst_id,size_t * data_len)151 static void *current_time_read_cb(uint16_t obj_inst_id, uint16_t res_id,
152 uint16_t res_inst_id, size_t *data_len)
153 {
154 time_temp = time_offset + (k_uptime_get() / 1000);
155 *data_len = sizeof(time_temp);
156
157 return &time_temp;
158 }
159
current_time_pre_write_cb(uint16_t obj_inst_id,uint16_t res_id,uint16_t res_inst_id,size_t * data_len)160 static void *current_time_pre_write_cb(uint16_t obj_inst_id, uint16_t res_id,
161 uint16_t res_inst_id, size_t *data_len)
162 {
163 *data_len = sizeof(time_temp);
164 return &time_temp;
165 }
166
current_time_post_write_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)167 static int current_time_post_write_cb(uint16_t obj_inst_id, uint16_t res_id,
168 uint16_t res_inst_id,
169 uint8_t *data, uint16_t data_len,
170 bool last_block, size_t total_size)
171 {
172 if (data_len == 4U) {
173 time_offset = *(uint32_t *)data - (uint32_t)(k_uptime_get() / 1000);
174 return 0;
175 } else if (data_len == 8U) {
176 time_offset = *(time_t *)data - (time_t)(k_uptime_get() / 1000);
177 return 0;
178 }
179
180 LOG_ERR("unknown size %u", data_len);
181 return -EINVAL;
182 }
183
184 /* error code function */
185
lwm2m_device_add_err(uint8_t error_code)186 int lwm2m_device_add_err(uint8_t error_code)
187 {
188 int i;
189
190 for (i = 0; i < DEVICE_ERROR_CODE_MAX; i++) {
191 if (error_code_list[i] == 0) {
192 break;
193 }
194
195 /* No duplicate error codes allowed */
196 if (error_code_list[i] == error_code) {
197 return 0;
198 }
199 }
200
201 if (i >= DEVICE_ERROR_CODE_MAX) {
202 return -ENOMEM;
203 }
204
205 error_code_list[i] = error_code;
206 error_code_ri[i].res_inst_id = i;
207 lwm2m_notify_observer(LWM2M_OBJECT_DEVICE_ID, 0, DEVICE_ERROR_CODE_ID);
208
209 return 0;
210 }
211
device_periodic_service(struct k_work * work)212 static void device_periodic_service(struct k_work *work)
213 {
214 lwm2m_notify_observer(LWM2M_OBJECT_DEVICE_ID, 0, DEVICE_CURRENT_TIME_ID);
215 }
216
lwm2m_update_device_service_period(uint32_t period_ms)217 int lwm2m_update_device_service_period(uint32_t period_ms)
218 {
219 return lwm2m_engine_update_service_period(device_periodic_service, period_ms);
220 }
221
device_create(uint16_t obj_inst_id)222 static struct lwm2m_engine_obj_inst *device_create(uint16_t obj_inst_id)
223 {
224 int i = 0, j = 0;
225
226 init_res_instance(res_inst, ARRAY_SIZE(res_inst));
227
228 /* initialize instance resource data */
229 INIT_OBJ_RES_OPTDATA(DEVICE_MANUFACTURER_ID, res, i, res_inst, j);
230 INIT_OBJ_RES_OPTDATA(DEVICE_MODEL_NUMBER_ID, res, i, res_inst, j);
231 INIT_OBJ_RES_OPTDATA(DEVICE_SERIAL_NUMBER_ID, res, i, res_inst, j);
232 INIT_OBJ_RES_OPTDATA(DEVICE_FIRMWARE_VERSION_ID, res, i, res_inst, j);
233 INIT_OBJ_RES_EXECUTE(DEVICE_REBOOT_ID, res, i, NULL);
234 INIT_OBJ_RES_EXECUTE(DEVICE_FACTORY_DEFAULT_ID, res, i, NULL);
235 INIT_OBJ_RES_MULTI_OPTDATA(DEVICE_AVAILABLE_POWER_SOURCES_ID, res, i,
236 res_inst, j, DEVICE_PWRSRC_MAX, false);
237 INIT_OBJ_RES_MULTI_OPTDATA(DEVICE_POWER_SOURCE_VOLTAGE_ID, res, i,
238 res_inst, j, DEVICE_PWRSRC_MAX, false);
239 INIT_OBJ_RES_MULTI_OPTDATA(DEVICE_POWER_SOURCE_CURRENT_ID, res, i,
240 res_inst, j, DEVICE_PWRSRC_MAX, false);
241 INIT_OBJ_RES_OPTDATA(DEVICE_BATTERY_LEVEL_ID, res, i, res_inst, j);
242 INIT_OBJ_RES_OPTDATA(DEVICE_MEMORY_FREE_ID, res, i, res_inst, j);
243 error_code_ri = &res_inst[j];
244 INIT_OBJ_RES_MULTI_DATA(DEVICE_ERROR_CODE_ID, res, i,
245 res_inst, j, DEVICE_ERROR_CODE_MAX, false,
246 error_code_list, sizeof(*error_code_list));
247 INIT_OBJ_RES_EXECUTE(DEVICE_RESET_ERROR_CODE_ID, res, i,
248 reset_error_list_cb);
249 INIT_OBJ_RES_OPT(DEVICE_CURRENT_TIME_ID, res, i, res_inst, j, 1, false,
250 true, current_time_read_cb, current_time_pre_write_cb,
251 NULL, current_time_post_write_cb, NULL);
252 INIT_OBJ_RES_OPTDATA(DEVICE_UTC_OFFSET_ID, res, i, res_inst, j);
253 INIT_OBJ_RES_OPTDATA(DEVICE_TIMEZONE_ID, res, i, res_inst, j);
254 INIT_OBJ_RES_DATA(DEVICE_SUPPORTED_BINDING_MODES_ID, res, i,
255 res_inst, j, binding_mode, DEVICE_STRING_SHORT);
256 INIT_OBJ_RES_OPTDATA(DEVICE_TYPE_ID, res, i, res_inst, j);
257 INIT_OBJ_RES_OPTDATA(DEVICE_HARDWARE_VERSION_ID, res, i, res_inst, j);
258 INIT_OBJ_RES_OPTDATA(DEVICE_SOFTWARE_VERSION_ID, res, i, res_inst, j);
259 INIT_OBJ_RES_OPTDATA(DEVICE_BATTERY_STATUS_ID, res, i, res_inst, j);
260 INIT_OBJ_RES_OPTDATA(DEVICE_MEMORY_TOTAL_ID, res, i, res_inst, j);
261 INIT_OBJ_RES_MULTI_OPTDATA(DEVICE_EXT_DEV_INFO_ID, res, i, res_inst, j,
262 DEVICE_EXT_DEV_INFO_MAX, false);
263
264 inst.resources = res;
265 inst.resource_count = i;
266
267 LOG_DBG("Create LWM2M device instance: %d", obj_inst_id);
268 return &inst;
269 }
270
lwm2m_device_init(void)271 static int lwm2m_device_init(void)
272 {
273 struct lwm2m_engine_obj_inst *obj_inst = NULL;
274 int ret = 0;
275
276 /* Set default values */
277 time_offset = 0U;
278 lwm2m_engine_get_binding(binding_mode);
279
280 /* initialize the device field data */
281 device.obj_id = LWM2M_OBJECT_DEVICE_ID;
282 device.version_major = DEVICE_VERSION_MAJOR;
283 device.version_minor = DEVICE_VERSION_MINOR;
284 device.is_core = true;
285 device.fields = fields;
286 device.field_count = ARRAY_SIZE(fields);
287 device.max_instance_count = 1U;
288 device.create_cb = device_create;
289 lwm2m_register_obj(&device);
290
291 /* auto create the only instance */
292 ret = lwm2m_create_obj_inst(LWM2M_OBJECT_DEVICE_ID, 0, &obj_inst);
293 if (ret < 0) {
294 LOG_DBG("Create LWM2M instance 0 error: %d", ret);
295 }
296
297 /* Create the default error code resource instance */
298 lwm2m_device_add_err(0);
299
300 /* call device_periodic_service() every 10 seconds */
301 ret = lwm2m_engine_add_service(device_periodic_service,
302 DEVICE_SERVICE_INTERVAL_MS);
303 return ret;
304 }
305
306 SYS_INIT(lwm2m_device_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
307