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 <logging/log.h>
17 LOG_MODULE_REGISTER(LOG_MODULE_NAME);
18
19 #include <string.h>
20 #include <stdio.h>
21 #include <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 int32_t time_temp;
94 static uint32_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 return 0;
146 }
147
current_time_read_cb(uint16_t obj_inst_id,uint16_t res_id,uint16_t res_inst_id,size_t * data_len)148 static void *current_time_read_cb(uint16_t obj_inst_id, uint16_t res_id,
149 uint16_t res_inst_id, size_t *data_len)
150 {
151 time_temp = time_offset + (k_uptime_get() / 1000);
152 *data_len = sizeof(time_temp);
153
154 return &time_temp;
155 }
156
current_time_pre_write_cb(uint16_t obj_inst_id,uint16_t res_id,uint16_t res_inst_id,size_t * data_len)157 static void *current_time_pre_write_cb(uint16_t obj_inst_id, uint16_t res_id,
158 uint16_t res_inst_id, size_t *data_len)
159 {
160 *data_len = sizeof(time_temp);
161 return &time_temp;
162 }
163
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)164 static int current_time_post_write_cb(uint16_t obj_inst_id, uint16_t res_id,
165 uint16_t res_inst_id,
166 uint8_t *data, uint16_t data_len,
167 bool last_block, size_t total_size)
168 {
169 if (data_len == 4U) {
170 time_offset = *(int32_t *)data - (int32_t)(k_uptime_get() / 1000);
171 return 0;
172 }
173
174 LOG_ERR("unknown size %u", data_len);
175 return -EINVAL;
176 }
177
178 /* error code function */
179
lwm2m_device_add_err(uint8_t error_code)180 int lwm2m_device_add_err(uint8_t error_code)
181 {
182 int i;
183
184 for (i = 0; i < DEVICE_ERROR_CODE_MAX; i++) {
185 if (error_code_ri[i].res_inst_id == RES_INSTANCE_NOT_CREATED) {
186 break;
187 }
188
189 /* No duplicate error codes allowed */
190 if (*(uint8_t *)error_code_ri[i].data_ptr == error_code) {
191 return 0;
192 }
193 }
194
195 if (i >= DEVICE_ERROR_CODE_MAX) {
196 return -ENOMEM;
197 }
198
199 error_code_list[i] = error_code;
200 error_code_ri[i].res_inst_id = i;
201 NOTIFY_OBSERVER(LWM2M_OBJECT_DEVICE_ID, 0, DEVICE_ERROR_CODE_ID);
202
203 return 0;
204 }
205
device_periodic_service(struct k_work * work)206 static void device_periodic_service(struct k_work *work)
207 {
208 NOTIFY_OBSERVER(LWM2M_OBJECT_DEVICE_ID, 0, DEVICE_CURRENT_TIME_ID);
209 }
210
device_create(uint16_t obj_inst_id)211 static struct lwm2m_engine_obj_inst *device_create(uint16_t obj_inst_id)
212 {
213 int i = 0, j = 0;
214
215 init_res_instance(res_inst, ARRAY_SIZE(res_inst));
216
217 /* initialize instance resource data */
218 INIT_OBJ_RES_OPTDATA(DEVICE_MANUFACTURER_ID, res, i, res_inst, j);
219 INIT_OBJ_RES_OPTDATA(DEVICE_MODEL_NUMBER_ID, res, i, res_inst, j);
220 INIT_OBJ_RES_OPTDATA(DEVICE_SERIAL_NUMBER_ID, res, i, res_inst, j);
221 INIT_OBJ_RES_OPTDATA(DEVICE_FIRMWARE_VERSION_ID, res, i, res_inst, j);
222 INIT_OBJ_RES_EXECUTE(DEVICE_REBOOT_ID, res, i, NULL);
223 INIT_OBJ_RES_EXECUTE(DEVICE_FACTORY_DEFAULT_ID, res, i, NULL);
224 INIT_OBJ_RES_MULTI_OPTDATA(DEVICE_AVAILABLE_POWER_SOURCES_ID, res, i,
225 res_inst, j, DEVICE_PWRSRC_MAX, false);
226 INIT_OBJ_RES_MULTI_OPTDATA(DEVICE_POWER_SOURCE_VOLTAGE_ID, res, i,
227 res_inst, j, DEVICE_PWRSRC_MAX, false);
228 INIT_OBJ_RES_MULTI_OPTDATA(DEVICE_POWER_SOURCE_CURRENT_ID, res, i,
229 res_inst, j, DEVICE_PWRSRC_MAX, false);
230 INIT_OBJ_RES_OPTDATA(DEVICE_BATTERY_LEVEL_ID, res, i, res_inst, j);
231 INIT_OBJ_RES_OPTDATA(DEVICE_MEMORY_FREE_ID, res, i, res_inst, j);
232 error_code_ri = &res_inst[j];
233 INIT_OBJ_RES_MULTI_DATA(DEVICE_ERROR_CODE_ID, res, i,
234 res_inst, j, DEVICE_ERROR_CODE_MAX, false,
235 error_code_list, sizeof(*error_code_list));
236 INIT_OBJ_RES_EXECUTE(DEVICE_RESET_ERROR_CODE_ID, res, i,
237 reset_error_list_cb);
238 INIT_OBJ_RES_OPT(DEVICE_CURRENT_TIME_ID, res, i, res_inst, j, 1, false,
239 true, current_time_read_cb, current_time_pre_write_cb,
240 NULL, current_time_post_write_cb, NULL);
241 INIT_OBJ_RES_OPTDATA(DEVICE_UTC_OFFSET_ID, res, i, res_inst, j);
242 INIT_OBJ_RES_OPTDATA(DEVICE_TIMEZONE_ID, res, i, res_inst, j);
243 INIT_OBJ_RES_DATA(DEVICE_SUPPORTED_BINDING_MODES_ID, res, i,
244 res_inst, j, binding_mode, DEVICE_STRING_SHORT);
245 INIT_OBJ_RES_OPTDATA(DEVICE_TYPE_ID, res, i, res_inst, j);
246 INIT_OBJ_RES_OPTDATA(DEVICE_HARDWARE_VERSION_ID, res, i, res_inst, j);
247 INIT_OBJ_RES_OPTDATA(DEVICE_SOFTWARE_VERSION_ID, res, i, res_inst, j);
248 INIT_OBJ_RES_OPTDATA(DEVICE_BATTERY_STATUS_ID, res, i, res_inst, j);
249 INIT_OBJ_RES_OPTDATA(DEVICE_MEMORY_TOTAL_ID, res, i, res_inst, j);
250 INIT_OBJ_RES_MULTI_OPTDATA(DEVICE_EXT_DEV_INFO_ID, res, i, res_inst, j,
251 DEVICE_EXT_DEV_INFO_MAX, false);
252
253 inst.resources = res;
254 inst.resource_count = i;
255
256 LOG_DBG("Create LWM2M device instance: %d", obj_inst_id);
257 return &inst;
258 }
259
lwm2m_device_init(const struct device * dev)260 static int lwm2m_device_init(const struct device *dev)
261 {
262 struct lwm2m_engine_obj_inst *obj_inst = NULL;
263 int ret = 0;
264
265 /* Set default values */
266 time_offset = 0U;
267 lwm2m_engine_get_binding(binding_mode);
268
269 /* initialize the device field data */
270 device.obj_id = LWM2M_OBJECT_DEVICE_ID;
271 device.version_major = DEVICE_VERSION_MAJOR;
272 device.version_minor = DEVICE_VERSION_MINOR;
273 device.is_core = true;
274 device.fields = fields;
275 device.field_count = ARRAY_SIZE(fields);
276 device.max_instance_count = 1U;
277 device.create_cb = device_create;
278 lwm2m_register_obj(&device);
279
280 /* auto create the only instance */
281 ret = lwm2m_create_obj_inst(LWM2M_OBJECT_DEVICE_ID, 0, &obj_inst);
282 if (ret < 0) {
283 LOG_DBG("Create LWM2M instance 0 error: %d", ret);
284 }
285
286 /* call device_periodic_service() every 10 seconds */
287 ret = lwm2m_engine_add_service(device_periodic_service,
288 DEVICE_SERVICE_INTERVAL_MS);
289 return ret;
290 }
291
292 SYS_INIT(lwm2m_device_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
293