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