esp_local_ctrl.c 15 KB

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