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 "esp_local_ctrl.h"
21 #include "esp_local_ctrl_priv.h"
22 #include "esp_local_ctrl.pb-c.h"
23
24 #define SAFE_ALLOCATION(type, var) \
25 type *var = (type *) malloc(sizeof(type)); \
26 if (!var) { \
27 ESP_LOGE(TAG, "Error allocating memory"); \
28 return ESP_ERR_NO_MEM; \
29 }
30
31 static const char* TAG = "esp_local_ctrl_handler";
32
33 typedef struct esp_local_ctrl_cmd {
34 int cmd_num;
35 esp_err_t (*command_handler)(LocalCtrlMessage *req,
36 LocalCtrlMessage *resp, void **ctx);
37 } esp_local_ctrl_cmd_t;
38
39 static esp_err_t cmd_get_prop_count_handler(LocalCtrlMessage *req,
40 LocalCtrlMessage *resp, void **ctx);
41
42 static esp_err_t cmd_get_prop_vals_handler(LocalCtrlMessage *req,
43 LocalCtrlMessage *resp, void **ctx);
44
45 static esp_err_t cmd_set_prop_vals_handler(LocalCtrlMessage *req,
46 LocalCtrlMessage *resp, void **ctx);
47
48 static esp_local_ctrl_cmd_t cmd_table[] = {
49 {
50 .cmd_num = LOCAL_CTRL_MSG_TYPE__TypeCmdGetPropertyCount,
51 .command_handler = cmd_get_prop_count_handler
52 },
53 {
54 .cmd_num = LOCAL_CTRL_MSG_TYPE__TypeCmdGetPropertyValues,
55 .command_handler = cmd_get_prop_vals_handler
56 },
57 {
58 .cmd_num = LOCAL_CTRL_MSG_TYPE__TypeCmdSetPropertyValues,
59 .command_handler = cmd_set_prop_vals_handler
60 }
61 };
62
err_to_status(esp_err_t err)63 static uint16_t err_to_status(esp_err_t err)
64 {
65 uint16_t status;
66 switch (err) {
67 case ESP_OK:
68 status = STATUS__Success;
69 break;
70 case ESP_ERR_INVALID_ARG:
71 status = STATUS__InvalidArgument;
72 break;
73 case ESP_ERR_INVALID_STATE:
74 status = STATUS__InvalidProto;
75 break;
76 default:
77 status = STATUS__InternalError;
78 }
79 return status;
80 }
81
cmd_get_prop_count_handler(LocalCtrlMessage * req,LocalCtrlMessage * resp,void ** ctx)82 static esp_err_t cmd_get_prop_count_handler(LocalCtrlMessage *req,
83 LocalCtrlMessage *resp, void **ctx)
84 {
85 SAFE_ALLOCATION(RespGetPropertyCount, resp_payload);
86 resp_get_property_count__init(resp_payload);
87
88 size_t prop_count = 0;
89 resp_payload->status = err_to_status(esp_local_ctrl_get_prop_count(&prop_count));
90 resp_payload->count = prop_count;
91 resp->payload_case = LOCAL_CTRL_MESSAGE__PAYLOAD_RESP_GET_PROP_COUNT;
92 resp->resp_get_prop_count = resp_payload;
93 ESP_LOGD(TAG, "Got properties count %d", prop_count);
94 return ESP_OK;
95 }
96
97 typedef void (*prop_val_free_fn_t)(void *val);
98
cmd_get_prop_vals_handler(LocalCtrlMessage * req,LocalCtrlMessage * resp,void ** ctx)99 static esp_err_t cmd_get_prop_vals_handler(LocalCtrlMessage *req,
100 LocalCtrlMessage *resp, void **ctx)
101 {
102 SAFE_ALLOCATION(RespGetPropertyValues, resp_payload);
103 resp_get_property_values__init(resp_payload);
104
105 esp_local_ctrl_prop_val_t *vals = calloc(req->cmd_get_prop_vals->n_indices,
106 sizeof(esp_local_ctrl_prop_val_t));
107 esp_local_ctrl_prop_t *descs = calloc(req->cmd_get_prop_vals->n_indices,
108 sizeof(esp_local_ctrl_prop_t));
109 prop_val_free_fn_t *free_fns = calloc(req->cmd_get_prop_vals->n_indices,
110 sizeof(prop_val_free_fn_t));
111 resp_payload->props = calloc(req->cmd_get_prop_vals->n_indices,
112 sizeof(PropertyInfo *));
113 if (!vals || !descs || !free_fns || !resp_payload->props) {
114 ESP_LOGE(TAG, "Failed to allocate memory for getting values");
115 free(vals);
116 free(descs);
117 free(free_fns);
118 free(resp_payload->props);
119 free(resp_payload);
120 return ESP_ERR_NO_MEM;
121 }
122
123 esp_err_t ret = esp_local_ctrl_get_prop_values(req->cmd_get_prop_vals->n_indices,
124 req->cmd_get_prop_vals->indices,
125 descs, vals);
126 resp_payload->status = err_to_status(ret);
127 if (ret == ESP_OK) {
128 resp_payload->n_props = 0;
129 for (size_t i = 0; i < req->cmd_get_prop_vals->n_indices; i++) {
130 resp_payload->props[i] = malloc(sizeof(PropertyInfo));
131 if (!resp_payload->props[i]) {
132 resp_payload->status = STATUS__InternalError;
133 break;
134 }
135 resp_payload->n_props++;
136 property_info__init(resp_payload->props[i]);
137 resp_payload->props[i]->name = descs[i].name;
138 resp_payload->props[i]->type = descs[i].type;
139 resp_payload->props[i]->flags = descs[i].flags;
140 resp_payload->props[i]->value.data = vals[i].data;
141 resp_payload->props[i]->value.len = vals[i].size;
142 free_fns[i] = vals[i].free_fn;
143 }
144 }
145 resp->payload_case = LOCAL_CTRL_MESSAGE__PAYLOAD_RESP_GET_PROP_VALS;
146 resp->resp_get_prop_vals = resp_payload;
147 (*ctx) = (void *)free_fns;
148 free(vals);
149 free(descs);
150
151 /* Unless it's a fatal error, always return ESP_OK, otherwise
152 * the underlying connection will be closed by protocomm */
153 return ESP_OK;
154 }
155
cmd_set_prop_vals_handler(LocalCtrlMessage * req,LocalCtrlMessage * resp,void ** ctx)156 static esp_err_t cmd_set_prop_vals_handler(LocalCtrlMessage *req,
157 LocalCtrlMessage *resp, void **ctx)
158 {
159 SAFE_ALLOCATION(RespSetPropertyValues, resp_payload);
160 resp_set_property_values__init(resp_payload);
161
162 uint32_t *idxs = calloc(req->cmd_set_prop_vals->n_props, sizeof(uint32_t));
163 esp_local_ctrl_prop_val_t *vals = calloc(req->cmd_set_prop_vals->n_props,
164 sizeof(esp_local_ctrl_prop_val_t));
165 if (!idxs || !vals) {
166 ESP_LOGE(TAG, "Failed to allocate memory for setting values");
167 free(idxs);
168 free(vals);
169 free(resp_payload);
170 return ESP_ERR_NO_MEM;
171 }
172 for (size_t i = 0; i < req->cmd_set_prop_vals->n_props; i++) {
173 idxs[i] = req->cmd_set_prop_vals->props[i]->index;
174 vals[i].data = req->cmd_set_prop_vals->props[i]->value.data;
175 vals[i].size = req->cmd_set_prop_vals->props[i]->value.len;
176 }
177
178 esp_err_t ret = esp_local_ctrl_set_prop_values(req->cmd_set_prop_vals->n_props,
179 idxs, vals);
180 resp_payload->status = err_to_status(ret);
181 resp->payload_case = LOCAL_CTRL_MESSAGE__PAYLOAD_RESP_SET_PROP_VALS;
182 resp->resp_set_prop_vals = resp_payload;
183 free(idxs);
184 free(vals);
185
186 /* Unless it's a fatal error, always return ESP_OK, otherwise
187 * the underlying connection will be closed by protocomm */
188 return ESP_OK;
189 }
190
lookup_cmd_handler(int cmd_id)191 static int lookup_cmd_handler(int cmd_id)
192 {
193 for (size_t i = 0; i < sizeof(cmd_table)/sizeof(esp_local_ctrl_cmd_t); i++) {
194 if (cmd_table[i].cmd_num == cmd_id) {
195 return i;
196 }
197 }
198
199 return -1;
200 }
201
esp_local_ctrl_command_cleanup(LocalCtrlMessage * resp,void ** ctx)202 static void esp_local_ctrl_command_cleanup(LocalCtrlMessage *resp, void **ctx)
203 {
204 if (!resp) {
205 return;
206 }
207
208 switch (resp->msg) {
209 case LOCAL_CTRL_MSG_TYPE__TypeRespGetPropertyCount:
210 free(resp->resp_get_prop_count);
211 break;
212 case LOCAL_CTRL_MSG_TYPE__TypeRespGetPropertyValues: {
213 if (resp->resp_get_prop_vals) {
214 prop_val_free_fn_t *free_fns = (prop_val_free_fn_t *)(*ctx);
215 for (size_t i = 0; i < resp->resp_get_prop_vals->n_props; i++) {
216 if (free_fns && free_fns[i]) {
217 free_fns[i](resp->resp_get_prop_vals->props[i]->value.data);
218 }
219 free(resp->resp_get_prop_vals->props[i]);
220 }
221 free(free_fns);
222 free(resp->resp_get_prop_vals->props);
223 free(resp->resp_get_prop_vals);
224 }
225 }
226 break;
227 case LOCAL_CTRL_MSG_TYPE__TypeRespSetPropertyValues:
228 free(resp->resp_set_prop_vals);
229 break;
230 default:
231 ESP_LOGE(TAG, "Unsupported response type in cleanup_handler");
232 }
233 return;
234 }
235
esp_local_ctrl_command_dispatcher(LocalCtrlMessage * req,LocalCtrlMessage * resp,void ** ctx)236 static esp_err_t esp_local_ctrl_command_dispatcher(LocalCtrlMessage *req,
237 LocalCtrlMessage *resp,
238 void **ctx)
239 {
240 int cmd_index = lookup_cmd_handler(req->msg);
241 if (cmd_index < 0) {
242 ESP_LOGE(TAG, "Invalid command handler lookup");
243 return ESP_ERR_INVALID_ARG;
244 }
245
246 esp_err_t ret = cmd_table[cmd_index].command_handler(req, resp, ctx);
247 if (ret != ESP_OK) {
248 ESP_LOGE(TAG, "Error executing command handler");
249 return ret;
250 }
251
252 return ESP_OK;
253 }
254
esp_local_ctrl_data_handler(uint32_t session_id,const uint8_t * inbuf,ssize_t inlen,uint8_t ** outbuf,ssize_t * outlen,void * priv_data)255 esp_err_t esp_local_ctrl_data_handler(uint32_t session_id, const uint8_t *inbuf, ssize_t inlen,
256 uint8_t **outbuf, ssize_t *outlen, void *priv_data)
257 {
258 void *temp_ctx = NULL;
259 LocalCtrlMessage *req = local_ctrl_message__unpack(NULL, inlen, inbuf);
260 if (!req) {
261 ESP_LOGE(TAG, "Unable to unpack payload data");
262 return ESP_ERR_INVALID_ARG;
263 }
264
265 LocalCtrlMessage resp;
266 local_ctrl_message__init(&resp);
267 resp.msg = req->msg + 1; /* Response is request + 1 */
268
269 esp_err_t ret = esp_local_ctrl_command_dispatcher(req, &resp, &temp_ctx);
270 if (ret != ESP_OK) {
271 ESP_LOGE(TAG, "command dispatcher failed");
272 esp_local_ctrl_command_cleanup(&resp, &temp_ctx);
273 local_ctrl_message__free_unpacked(req, NULL);
274 return ESP_FAIL;
275 }
276
277 local_ctrl_message__free_unpacked(req, NULL);
278
279 *outlen = local_ctrl_message__get_packed_size(&resp);
280 if (*outlen <= 0) {
281 ESP_LOGE(TAG, "Invalid encoding for response");
282 esp_local_ctrl_command_cleanup(&resp, &temp_ctx);
283 return ESP_FAIL;
284 }
285
286 *outbuf = (uint8_t *) malloc(*outlen);
287 if (!*outbuf) {
288 ESP_LOGE(TAG, "System out of memory");
289 esp_local_ctrl_command_cleanup(&resp, &temp_ctx);
290 return ESP_ERR_NO_MEM;
291 }
292
293 local_ctrl_message__pack(&resp, *outbuf);
294 esp_local_ctrl_command_cleanup(&resp, &temp_ctx);
295 ESP_LOG_BUFFER_HEX_LEVEL(TAG, *outbuf, *outlen, ESP_LOG_DEBUG);
296 return ESP_OK;
297 }
298