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 <esp_log.h>
17 #include <string.h>
18 #include <esp_err.h>
19 #include <esp_wifi.h>
20 
21 #include "wifi_scan.pb-c.h"
22 
23 #include <wifi_provisioning/wifi_scan.h>
24 
25 static const char *TAG = "proto_wifi_scan";
26 
27 typedef struct wifi_prov_scan_cmd {
28     int cmd_num;
29     esp_err_t (*command_handler)(WiFiScanPayload *req,
30                                  WiFiScanPayload *resp, void *priv_data);
31 } wifi_prov_scan_cmd_t;
32 
33 static esp_err_t cmd_scan_start_handler(WiFiScanPayload *req,
34                                         WiFiScanPayload *resp,
35                                         void *priv_data);
36 
37 static esp_err_t cmd_scan_status_handler(WiFiScanPayload *req,
38                                          WiFiScanPayload *resp,
39                                          void *priv_data);
40 
41 static esp_err_t cmd_scan_result_handler(WiFiScanPayload *req,
42                                          WiFiScanPayload *resp,
43                                          void *priv_data);
44 
45 static wifi_prov_scan_cmd_t cmd_table[] = {
46     {
47         .cmd_num = WI_FI_SCAN_MSG_TYPE__TypeCmdScanStart,
48         .command_handler = cmd_scan_start_handler
49     },
50     {
51         .cmd_num = WI_FI_SCAN_MSG_TYPE__TypeCmdScanStatus,
52         .command_handler = cmd_scan_status_handler
53     },
54     {
55         .cmd_num = WI_FI_SCAN_MSG_TYPE__TypeCmdScanResult,
56         .command_handler = cmd_scan_result_handler
57     }
58 };
59 
cmd_scan_start_handler(WiFiScanPayload * req,WiFiScanPayload * resp,void * priv_data)60 static esp_err_t cmd_scan_start_handler(WiFiScanPayload *req,
61                                         WiFiScanPayload *resp, void *priv_data)
62 {
63     wifi_prov_scan_handlers_t *h = (wifi_prov_scan_handlers_t *) priv_data;
64     if (!h) {
65         ESP_LOGE(TAG, "Command invoked without handlers");
66         return ESP_ERR_INVALID_STATE;
67     }
68 
69     RespScanStart *resp_payload = (RespScanStart *) malloc(sizeof(RespScanStart));
70     if (!resp_payload) {
71         ESP_LOGE(TAG, "Error allocating memory");
72         return ESP_ERR_NO_MEM;
73     }
74 
75     resp_scan_start__init(resp_payload);
76     resp->status = (h->scan_start(req->cmd_scan_start->blocking,
77                                   req->cmd_scan_start->passive,
78                                   req->cmd_scan_start->group_channels,
79                                   req->cmd_scan_start->period_ms,
80                                   &h->ctx) == ESP_OK ?
81                             STATUS__Success : STATUS__InternalError);
82     resp->payload_case = WI_FI_SCAN_PAYLOAD__PAYLOAD_RESP_SCAN_START;
83     resp->resp_scan_start = resp_payload;
84     return ESP_OK;
85 }
86 
cmd_scan_status_handler(WiFiScanPayload * req,WiFiScanPayload * resp,void * priv_data)87 static esp_err_t cmd_scan_status_handler(WiFiScanPayload *req,
88                                          WiFiScanPayload *resp, void *priv_data)
89 {
90     bool scan_finished = false;
91     uint16_t result_count = 0;
92 
93     wifi_prov_scan_handlers_t *h = (wifi_prov_scan_handlers_t *) priv_data;
94     if (!h) {
95         ESP_LOGE(TAG, "Command invoked without handlers");
96         return ESP_ERR_INVALID_STATE;
97     }
98 
99     RespScanStatus *resp_payload = (RespScanStatus *) malloc(sizeof(RespScanStatus));
100     if (!resp_payload) {
101         ESP_LOGE(TAG, "Error allocating memory");
102         return ESP_ERR_NO_MEM;
103     }
104 
105     resp_scan_status__init(resp_payload);
106     resp->status = (h->scan_status(&scan_finished, &result_count, &h->ctx) == ESP_OK ?
107                             STATUS__Success : STATUS__InternalError);
108     resp_payload->scan_finished = scan_finished;
109     resp_payload->result_count = result_count;
110     resp->payload_case = WI_FI_SCAN_PAYLOAD__PAYLOAD_RESP_SCAN_STATUS;
111     resp->resp_scan_status = resp_payload;
112     return ESP_OK;
113 }
114 
cmd_scan_result_handler(WiFiScanPayload * req,WiFiScanPayload * resp,void * priv_data)115 static esp_err_t cmd_scan_result_handler(WiFiScanPayload *req,
116                                          WiFiScanPayload *resp, void *priv_data)
117 {
118     esp_err_t err;
119     wifi_prov_scan_result_t scan_result = {{0}, {0}, 0, 0, 0};
120     WiFiScanResult **results = NULL;
121     wifi_prov_scan_handlers_t *h = (wifi_prov_scan_handlers_t *) priv_data;
122     if (!h) {
123         ESP_LOGE(TAG, "Command invoked without handlers");
124         return ESP_ERR_INVALID_STATE;
125     }
126 
127     RespScanResult *resp_payload = (RespScanResult *) malloc(sizeof(RespScanResult));
128     if (!resp_payload) {
129         ESP_LOGE(TAG, "Error allocating memory");
130         return ESP_ERR_NO_MEM;
131     }
132     resp_scan_result__init(resp_payload);
133 
134     resp->status = STATUS__Success;
135     resp->payload_case = WI_FI_SCAN_PAYLOAD__PAYLOAD_RESP_SCAN_RESULT;
136     resp->resp_scan_result = resp_payload;
137     /* Allocate memory only if there are non-zero scan results */
138     if (req->cmd_scan_result->count) {
139         results = (WiFiScanResult **) calloc(req->cmd_scan_result->count,
140                 sizeof(WiFiScanResult *));
141         if (!results) {
142             ESP_LOGE(TAG, "Failed to allocate memory for results array");
143             return ESP_ERR_NO_MEM;
144         }
145     }
146     resp_payload->entries = results;
147     resp_payload->n_entries = req->cmd_scan_result->count;
148 
149     /* If req->cmd_scan_result->count is 0, the below loop will
150      * be skipped.
151      */
152     for (uint16_t i = 0; i < req->cmd_scan_result->count; i++) {
153         err = h->scan_result(i + req->cmd_scan_result->start_index,
154                              &scan_result, &h->ctx);
155         if (err != ESP_OK) {
156             resp->status = STATUS__InternalError;
157             break;
158         }
159 
160         results[i] = (WiFiScanResult *) malloc(sizeof(WiFiScanResult));
161         if (!results[i]) {
162             ESP_LOGE(TAG, "Failed to allocate memory for result entry");
163             return ESP_ERR_NO_MEM;
164         }
165         wi_fi_scan_result__init(results[i]);
166 
167         results[i]->ssid.len = strnlen(scan_result.ssid, 32);
168         results[i]->ssid.data = (uint8_t *) strndup(scan_result.ssid, 32);
169         if (!results[i]->ssid.data) {
170             ESP_LOGE(TAG, "Failed to allocate memory for scan result entry SSID");
171             return ESP_ERR_NO_MEM;
172         }
173 
174         results[i]->channel = scan_result.channel;
175         results[i]->rssi = scan_result.rssi;
176         results[i]->auth = scan_result.auth;
177 
178         results[i]->bssid.len = sizeof(scan_result.bssid);
179         results[i]->bssid.data = malloc(results[i]->bssid.len);
180         if (!results[i]->bssid.data) {
181             ESP_LOGE(TAG, "Failed to allocate memory for scan result entry BSSID");
182             return ESP_ERR_NO_MEM;
183         }
184         memcpy(results[i]->bssid.data, scan_result.bssid, results[i]->bssid.len);
185     }
186     return ESP_OK;
187 }
188 
189 
lookup_cmd_handler(int cmd_id)190 static int lookup_cmd_handler(int cmd_id)
191 {
192     for (size_t i = 0; i < sizeof(cmd_table)/sizeof(wifi_prov_scan_cmd_t); i++) {
193         if (cmd_table[i].cmd_num == cmd_id) {
194             return i;
195         }
196     }
197 
198     return -1;
199 }
200 
wifi_prov_scan_cmd_cleanup(WiFiScanPayload * resp,void * priv_data)201 static void wifi_prov_scan_cmd_cleanup(WiFiScanPayload *resp, void *priv_data)
202 {
203     switch (resp->msg) {
204         case WI_FI_SCAN_MSG_TYPE__TypeRespScanStart:
205             {
206                 free(resp->resp_scan_start);
207             }
208             break;
209         case WI_FI_SCAN_MSG_TYPE__TypeRespScanStatus:
210             {
211                 free(resp->resp_scan_status);
212             }
213             break;
214         case WI_FI_SCAN_MSG_TYPE__TypeRespScanResult:
215             {
216                 if (!resp->resp_scan_result) return;
217                 if (resp->resp_scan_result->entries) {
218                     for (uint16_t i = 0; i < resp->resp_scan_result->n_entries; i++) {
219                         if (!resp->resp_scan_result->entries[i]) continue;
220                         free(resp->resp_scan_result->entries[i]->ssid.data);
221                         free(resp->resp_scan_result->entries[i]->bssid.data);
222                         free(resp->resp_scan_result->entries[i]);
223                     }
224                     free(resp->resp_scan_result->entries);
225                 }
226                 free(resp->resp_scan_result);
227             }
228             break;
229         default:
230             ESP_LOGE(TAG, "Unsupported response type in cleanup_handler");
231             break;
232     }
233     return;
234 }
235 
wifi_prov_scan_cmd_dispatcher(WiFiScanPayload * req,WiFiScanPayload * resp,void * priv_data)236 static esp_err_t wifi_prov_scan_cmd_dispatcher(WiFiScanPayload *req,
237                                                WiFiScanPayload *resp, void *priv_data)
238 {
239     esp_err_t ret;
240 
241     ESP_LOGD(TAG, "In wifi_prov_scan_cmd_dispatcher Cmd=%d", req->msg);
242 
243     int cmd_index = lookup_cmd_handler(req->msg);
244     if (cmd_index < 0) {
245         ESP_LOGE(TAG, "Invalid command handler lookup");
246         return ESP_FAIL;
247     }
248 
249     ret = cmd_table[cmd_index].command_handler(req, resp, priv_data);
250     if (ret != ESP_OK) {
251         ESP_LOGE(TAG, "Error executing command handler");
252         return ESP_FAIL;
253     }
254 
255     return ESP_OK;
256 }
257 
wifi_prov_scan_handler(uint32_t session_id,const uint8_t * inbuf,ssize_t inlen,uint8_t ** outbuf,ssize_t * outlen,void * priv_data)258 esp_err_t wifi_prov_scan_handler(uint32_t session_id, const uint8_t *inbuf, ssize_t inlen,
259                                  uint8_t **outbuf, ssize_t *outlen, void *priv_data)
260 {
261     WiFiScanPayload *req;
262     WiFiScanPayload resp;
263     esp_err_t ret = ESP_OK;
264 
265     req = wi_fi_scan_payload__unpack(NULL, inlen, inbuf);
266     if (!req) {
267         ESP_LOGE(TAG, "Unable to unpack scan message");
268         return ESP_ERR_INVALID_ARG;
269     }
270 
271     wi_fi_scan_payload__init(&resp);
272     ret = wifi_prov_scan_cmd_dispatcher(req, &resp, priv_data);
273     if (ret != ESP_OK) {
274         ESP_LOGE(TAG, "Command dispatcher error %d", ret);
275         ret = ESP_FAIL;
276         goto exit;
277     }
278 
279     resp.msg = req->msg + 1; /* Response is request + 1 */
280     *outlen = wi_fi_scan_payload__get_packed_size(&resp);
281     if (*outlen <= 0) {
282         ESP_LOGE(TAG, "Invalid encoding for response");
283         ret = ESP_FAIL;
284         goto exit;
285     }
286 
287     *outbuf = (uint8_t *) malloc(*outlen);
288     if (!*outbuf) {
289         ESP_LOGE(TAG, "System out of memory");
290         ret = ESP_ERR_NO_MEM;
291         goto exit;
292     }
293     wi_fi_scan_payload__pack(&resp, *outbuf);
294     ESP_LOGD(TAG, "Response packet size : %d", *outlen);
295     exit:
296 
297     wi_fi_scan_payload__free_unpacked(req, NULL);
298     wifi_prov_scan_cmd_cleanup(&resp, priv_data);
299     return ret;
300 }
301