esp_local_ctrl.c 16 KB

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