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