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