1 // Copyright 2019 Espressif Systems (Shanghai) PTE LTD
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include <stdio.h>
16 #include <string.h>
17 #include <esp_err.h>
18 #include <esp_log.h>
19
20 #include <protocomm.h>
21 #include <protocomm_security0.h>
22 #include <protocomm_security1.h>
23
24 #include <esp_local_ctrl.h>
25 #include "esp_local_ctrl_priv.h"
26 #include "esp_local_ctrl.pb-c.h"
27
28 #define ESP_LOCAL_CTRL_VERSION "v1.0"
29
30 struct inst_ctx {
31 protocomm_t *pc;
32 esp_local_ctrl_config_t config;
33 esp_local_ctrl_prop_t **props;
34 size_t props_count;
35 };
36
37 struct inst_ctx *local_ctrl_inst_ctx;
38
39 static const char *TAG = "esp_local_ctrl";
40
esp_local_ctrl_start(const esp_local_ctrl_config_t * config)41 esp_err_t esp_local_ctrl_start(const esp_local_ctrl_config_t *config)
42 {
43 esp_err_t ret;
44
45 if (!config) {
46 ESP_LOGE(TAG, "NULL configuration provided");
47 return ESP_ERR_INVALID_ARG;
48 }
49
50 if (!config->transport) {
51 ESP_LOGE(TAG, "No transport provided");
52 return ESP_ERR_INVALID_ARG;
53 }
54
55 if (config->max_properties == 0) {
56 ESP_LOGE(TAG, "max_properties must be greater than 0");
57 return ESP_ERR_INVALID_ARG;
58 }
59
60 if (!config->handlers.get_prop_values ||
61 !config->handlers.set_prop_values) {
62 ESP_LOGE(TAG, "Handlers cannot be null");
63 return ESP_ERR_INVALID_ARG;
64 }
65
66 if (local_ctrl_inst_ctx) {
67 ESP_LOGW(TAG, "Service already active");
68 return ESP_ERR_INVALID_STATE;
69 }
70
71 local_ctrl_inst_ctx = calloc(1, sizeof(struct inst_ctx));
72 if (!local_ctrl_inst_ctx) {
73 ESP_LOGE(TAG, "Failed to allocate memory for instance");
74 return ESP_ERR_NO_MEM;
75 }
76 memcpy(&local_ctrl_inst_ctx->config, config, sizeof(local_ctrl_inst_ctx->config));
77
78 local_ctrl_inst_ctx->props = calloc(local_ctrl_inst_ctx->config.max_properties,
79 sizeof(esp_local_ctrl_prop_t *));
80 if (!local_ctrl_inst_ctx->props) {
81 ESP_LOGE(TAG, "Failed to allocate memory for properties");
82 free(local_ctrl_inst_ctx);
83 local_ctrl_inst_ctx = NULL;
84 return ESP_ERR_NO_MEM;
85 }
86
87 /* Since the config structure will be different for different transport modes, each transport may
88 * implement a `copy_config()` function, which accepts a configuration structure as input and
89 * creates a copy of that, which can be kept in the context structure of the `esp_local_ctrl` instance.
90 * This copy can be later be freed using `free_config()` */
91 if (config->transport->copy_config) {
92 ret = config->transport->copy_config(&local_ctrl_inst_ctx->config.transport_config,
93 &config->transport_config);
94 if (ret != ESP_OK) {
95 esp_local_ctrl_stop();
96 return ret;
97 }
98 }
99
100 /* For a selected transport mode, endpoints may need to be declared prior to starting the
101 * `esp_local_ctrl` service, e.g. in case of BLE. By declaration it means that the transport layer
102 * allocates some resources for an endpoint, and later, after service has started, a handler
103 * is assigned for that endpoint */
104 if (config->transport->declare_ep) {
105 /* UUIDs are 16bit unique IDs for each endpoint. This may or may not be relevant for
106 * a chosen transport. We reserve all values from FF50 to FFFF for the internal endpoints.
107 * The remaining endpoints can be used by the application for its own custom endpoints */
108 uint16_t start_uuid = 0xFF50;
109 ret = config->transport->declare_ep(&local_ctrl_inst_ctx->config.transport_config,
110 "esp_local_ctrl/version", start_uuid++);
111 if (ret != ESP_OK) {
112 esp_local_ctrl_stop();
113 return ret;
114 }
115 ret = config->transport->declare_ep(&local_ctrl_inst_ctx->config.transport_config,
116 "esp_local_ctrl/session", start_uuid++);
117 if (ret != ESP_OK) {
118 esp_local_ctrl_stop();
119 return ret;
120 }
121 ret = config->transport->declare_ep(&local_ctrl_inst_ctx->config.transport_config,
122 "esp_local_ctrl/control", start_uuid++);
123 if (ret != ESP_OK) {
124 esp_local_ctrl_stop();
125 return ret;
126 }
127 }
128
129 local_ctrl_inst_ctx->pc = protocomm_new();
130 if (!local_ctrl_inst_ctx->pc) {
131 ESP_LOGE(TAG, "Failed to create new protocomm instance");
132 esp_local_ctrl_stop();
133 return ESP_FAIL;
134 }
135
136 if (config->transport->start_service) {
137 ret = config->transport->start_service(local_ctrl_inst_ctx->pc,
138 &local_ctrl_inst_ctx->config.transport_config);
139 if (ret != ESP_OK) {
140 esp_local_ctrl_stop();
141 return ret;
142 }
143 }
144
145 ret = protocomm_set_version(local_ctrl_inst_ctx->pc, "esp_local_ctrl/version",
146 ESP_LOCAL_CTRL_VERSION);
147 if (ret != ESP_OK) {
148 ESP_LOGE(TAG, "Failed to set version endpoint");
149 esp_local_ctrl_stop();
150 return ret;
151 }
152
153 protocomm_security_t *proto_sec_handle;
154 switch (local_ctrl_inst_ctx->config.proto_sec.version) {
155 case PROTOCOM_SEC_CUSTOM:
156 proto_sec_handle = local_ctrl_inst_ctx->config.proto_sec.custom_handle;
157 break;
158 case PROTOCOM_SEC1:
159 proto_sec_handle = (protocomm_security_t *) &protocomm_security1;
160 break;
161 case PROTOCOM_SEC0:
162 default:
163 proto_sec_handle = (protocomm_security_t *) &protocomm_security0;
164 break;
165 }
166 ret = protocomm_set_security(local_ctrl_inst_ctx->pc, "esp_local_ctrl/session",
167 proto_sec_handle, local_ctrl_inst_ctx->config.proto_sec.pop);
168 if (ret != ESP_OK) {
169 ESP_LOGE(TAG, "Failed to set session endpoint");
170 esp_local_ctrl_stop();
171 return ret;
172 }
173
174 ret = protocomm_add_endpoint(local_ctrl_inst_ctx->pc, "esp_local_ctrl/control",
175 esp_local_ctrl_data_handler, NULL);
176 if (ret != ESP_OK) {
177 ESP_LOGE(TAG, "Failed to set control endpoint");
178 esp_local_ctrl_stop();
179 return ret;
180 }
181 return ESP_OK;
182 }
183
esp_local_ctrl_stop(void)184 esp_err_t esp_local_ctrl_stop(void)
185 {
186 if (local_ctrl_inst_ctx) {
187 if (local_ctrl_inst_ctx->config.transport->free_config) {
188 local_ctrl_inst_ctx->config.transport->free_config(&local_ctrl_inst_ctx->config.transport_config);
189 }
190 if (local_ctrl_inst_ctx->pc) {
191 if (local_ctrl_inst_ctx->config.transport->stop_service) {
192 local_ctrl_inst_ctx->config.transport->stop_service(local_ctrl_inst_ctx->pc);
193 }
194 protocomm_delete(local_ctrl_inst_ctx->pc);
195 }
196 if (local_ctrl_inst_ctx->config.handlers.usr_ctx_free_fn) {
197 local_ctrl_inst_ctx->config.handlers.usr_ctx_free_fn(
198 local_ctrl_inst_ctx->config.handlers.usr_ctx);
199 }
200
201 /* Iterate through all properties one by one and free them */
202 for (uint32_t i = 0; i < local_ctrl_inst_ctx->config.max_properties; i++) {
203 if (local_ctrl_inst_ctx->props[i] == NULL) {
204 continue;
205 }
206 /* Release memory allocated for property data */
207 free(local_ctrl_inst_ctx->props[i]->name);
208 if (local_ctrl_inst_ctx->props[i]->ctx_free_fn) {
209 local_ctrl_inst_ctx->props[i]->ctx_free_fn(local_ctrl_inst_ctx->props[i]->ctx);
210 }
211 free(local_ctrl_inst_ctx->props[i]);
212 }
213 free(local_ctrl_inst_ctx->props);
214 free(local_ctrl_inst_ctx);
215 local_ctrl_inst_ctx = NULL;
216 }
217 return ESP_OK;
218 }
219
esp_local_ctrl_get_property_index(const char * name)220 static int esp_local_ctrl_get_property_index(const char *name)
221 {
222 if (!local_ctrl_inst_ctx || !name) {
223 return -1;
224 }
225
226 /* Iterate through all properties one by one
227 * and find the one with matching name */
228 for (uint32_t i = 0; i < local_ctrl_inst_ctx->props_count; i++) {
229 if (strcmp(local_ctrl_inst_ctx->props[i]->name, name) == 0) {
230 return i;
231 }
232 }
233 return -1;
234 }
235
esp_local_ctrl_add_property(const esp_local_ctrl_prop_t * prop)236 esp_err_t esp_local_ctrl_add_property(const esp_local_ctrl_prop_t *prop)
237 {
238 if (!local_ctrl_inst_ctx) {
239 ESP_LOGE(TAG, "Service not running");
240 return ESP_ERR_INVALID_STATE;
241 }
242 if (!prop || !prop->name) {
243 return ESP_ERR_INVALID_ARG;
244 }
245 if (esp_local_ctrl_get_property_index(prop->name) >= 0) {
246 ESP_LOGE(TAG, "Property with name %s exists", prop->name);
247 return ESP_ERR_INVALID_STATE;
248 }
249
250 if (local_ctrl_inst_ctx->config.max_properties
251 == local_ctrl_inst_ctx->props_count) {
252 ESP_LOGE(TAG, "Max properties limit reached. Cannot add property %s", prop->name);
253 return ESP_ERR_NO_MEM;
254 }
255
256 uint32_t i = local_ctrl_inst_ctx->props_count;
257 local_ctrl_inst_ctx->props[i] = calloc(1, sizeof(esp_local_ctrl_prop_t));
258 if (!local_ctrl_inst_ctx->props[i]) {
259 ESP_LOGE(TAG, "Failed to allocate memory for new property %s", prop->name);
260 return ESP_ERR_NO_MEM;
261 }
262 local_ctrl_inst_ctx->props[i]->name = strdup(prop->name);
263 if (!local_ctrl_inst_ctx->props[i]->name) {
264 ESP_LOGE(TAG, "Failed to allocate memory for property data %s", prop->name);
265 free(local_ctrl_inst_ctx->props[i]);
266 local_ctrl_inst_ctx->props[i] = NULL;
267 return ESP_ERR_NO_MEM;
268 }
269 local_ctrl_inst_ctx->props[i]->type = prop->type;
270 local_ctrl_inst_ctx->props[i]->size = prop->size;
271 local_ctrl_inst_ctx->props[i]->flags = prop->flags;
272 local_ctrl_inst_ctx->props[i]->ctx = prop->ctx;
273 local_ctrl_inst_ctx->props[i]->ctx_free_fn = prop->ctx_free_fn;
274 local_ctrl_inst_ctx->props_count++;
275 return ESP_OK;
276 }
277
278
esp_local_ctrl_remove_property(const char * name)279 esp_err_t esp_local_ctrl_remove_property(const char *name)
280 {
281 int idx = esp_local_ctrl_get_property_index(name);
282 if (idx < 0) {
283 ESP_LOGE(TAG, "Property %s not found", name);
284 return ESP_ERR_NOT_FOUND;
285 }
286
287 /* Release memory allocated for property data */
288 if (local_ctrl_inst_ctx->props[idx]->ctx_free_fn) {
289 local_ctrl_inst_ctx->props[idx]->ctx_free_fn(
290 local_ctrl_inst_ctx->props[idx]->ctx);
291 }
292 free(local_ctrl_inst_ctx->props[idx]->name);
293 free(local_ctrl_inst_ctx->props[idx]);
294 local_ctrl_inst_ctx->props[idx++] = NULL;
295
296 /* Move the following properties forward, so that there is
297 * no empty space between two properties */
298 for (uint32_t i = idx; i < local_ctrl_inst_ctx->props_count; i++) {
299 if (local_ctrl_inst_ctx->props[i] == NULL) {
300 break;
301 }
302 local_ctrl_inst_ctx->props[i-1] = local_ctrl_inst_ctx->props[i];
303 }
304 local_ctrl_inst_ctx->props_count--;
305 return ESP_OK;
306 }
307
esp_local_ctrl_get_property(const char * name)308 const esp_local_ctrl_prop_t *esp_local_ctrl_get_property(const char *name)
309 {
310 int idx = esp_local_ctrl_get_property_index(name);
311 if (idx < 0) {
312 ESP_LOGE(TAG, "Property %s not found", name);
313 return NULL;
314 }
315
316 return local_ctrl_inst_ctx->props[idx];
317 }
318
esp_local_ctrl_get_prop_count(size_t * count)319 esp_err_t esp_local_ctrl_get_prop_count(size_t *count)
320 {
321 if (!local_ctrl_inst_ctx) {
322 ESP_LOGE(TAG, "Service not running");
323 return ESP_ERR_INVALID_STATE;
324 }
325 if (!count) {
326 return ESP_ERR_INVALID_ARG;
327 }
328 *count = local_ctrl_inst_ctx->props_count;
329 return ESP_OK;
330 }
331
esp_local_ctrl_get_prop_values(size_t total_indices,uint32_t * indices,esp_local_ctrl_prop_t * props,esp_local_ctrl_prop_val_t * values)332 esp_err_t esp_local_ctrl_get_prop_values(size_t total_indices, uint32_t *indices,
333 esp_local_ctrl_prop_t *props,
334 esp_local_ctrl_prop_val_t *values)
335 {
336 if (!local_ctrl_inst_ctx) {
337 ESP_LOGE(TAG, "Service not running");
338 return ESP_ERR_INVALID_STATE;
339 }
340 if (!indices || !props || !values) {
341 return ESP_ERR_INVALID_ARG;
342 }
343
344 /* Convert indices to names */
345 for (size_t i = 0; i < total_indices; i++) {
346 if (indices[i] >= local_ctrl_inst_ctx->props_count) {
347 ESP_LOGE(TAG, "Invalid property index %d", indices[i]);
348 return ESP_ERR_INVALID_ARG;
349 }
350 props[i].name = local_ctrl_inst_ctx->props[indices[i]]->name;
351 props[i].type = local_ctrl_inst_ctx->props[indices[i]]->type;
352 props[i].flags = local_ctrl_inst_ctx->props[indices[i]]->flags;
353 props[i].size = local_ctrl_inst_ctx->props[indices[i]]->size;
354 props[i].ctx = local_ctrl_inst_ctx->props[indices[i]]->ctx;
355 }
356
357 esp_local_ctrl_handlers_t *h = &local_ctrl_inst_ctx->config.handlers;
358 esp_err_t ret = h->get_prop_values(total_indices, props, values, h->usr_ctx);
359
360 /* Properties with fixed sizes need to be checked */
361 for (size_t i = 0; i < total_indices; i++) {
362 if (local_ctrl_inst_ctx->props[indices[i]]->size != 0) {
363 values[i].size = local_ctrl_inst_ctx->props[indices[i]]->size;
364 }
365 }
366 return ret;
367 }
368
esp_local_ctrl_set_prop_values(size_t total_indices,uint32_t * indices,const esp_local_ctrl_prop_val_t * values)369 esp_err_t esp_local_ctrl_set_prop_values(size_t total_indices, uint32_t *indices,
370 const esp_local_ctrl_prop_val_t *values)
371 {
372 if (!local_ctrl_inst_ctx) {
373 ESP_LOGE(TAG, "Service not running");
374 return ESP_ERR_INVALID_STATE;
375 }
376 if (!indices || !values) {
377 return ESP_ERR_INVALID_ARG;
378 }
379
380 esp_local_ctrl_prop_t *props = calloc(total_indices,
381 sizeof(esp_local_ctrl_prop_t));
382 if (!props) {
383 ESP_LOGE(TAG, "Unable to allocate memory for properties array");
384 return ESP_ERR_NO_MEM;
385 }
386 for (size_t i = 0; i < total_indices; i++) {
387 if (indices[i] >= local_ctrl_inst_ctx->props_count) {
388 ESP_LOGE(TAG, "Invalid property index %d", indices[i]);
389 free(props);
390 return ESP_ERR_INVALID_ARG;
391 }
392
393 /* Properties with fixed sizes need to be checked */
394 if ((local_ctrl_inst_ctx->props[indices[i]]->size != values[i].size) &&
395 (local_ctrl_inst_ctx->props[indices[i]]->size != 0)) {
396 ESP_LOGE(TAG, "Invalid property size %d. Expected %d",
397 values[i].size, local_ctrl_inst_ctx->props[indices[i]]->size);
398 free(props);
399 return ESP_ERR_INVALID_ARG;
400 }
401
402 props[i].name = local_ctrl_inst_ctx->props[indices[i]]->name;
403 props[i].type = local_ctrl_inst_ctx->props[indices[i]]->type;
404 props[i].flags = local_ctrl_inst_ctx->props[indices[i]]->flags;
405 props[i].size = local_ctrl_inst_ctx->props[indices[i]]->size;
406 props[i].ctx = local_ctrl_inst_ctx->props[indices[i]]->ctx;
407 }
408
409 esp_local_ctrl_handlers_t *h = &local_ctrl_inst_ctx->config.handlers;
410 esp_err_t ret = h->set_prop_values(total_indices, props, values, h->usr_ctx);
411
412 free(props);
413 return ret;
414 }
415
esp_local_ctrl_set_handler(const char * ep_name,protocomm_req_handler_t handler,void * priv_data)416 esp_err_t esp_local_ctrl_set_handler(const char *ep_name,
417 protocomm_req_handler_t handler,
418 void *priv_data)
419 {
420 esp_err_t ret = ESP_ERR_INVALID_STATE;
421
422 if (local_ctrl_inst_ctx) {
423 ret = protocomm_add_endpoint(local_ctrl_inst_ctx->pc, ep_name,
424 handler, priv_data);
425 }
426
427 if (ret != ESP_OK) {
428 ESP_LOGE(TAG, "Failed to register endpoint handler");
429 }
430 return ret;
431 }
432