/
WhdSTAInterface.cpp
573 lines (491 loc) · 16.8 KB
/
WhdSTAInterface.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
/* WHD STAION implementation of NetworkInterfaceAPI
* Copyright (c) 2017-2019 ARM Limited
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <cstring>
#include <algorithm>
#include <vector>
#include "WhdSTAInterface.h"
#include "nsapi.h"
#include "lwipopts.h"
#include "lwip/etharp.h"
#include "lwip/ethip6.h"
#include "rtos.h"
#include "whd_emac.h"
#include "whd_wifi_api.h"
#include "whd_wlioctl.h"
#define CMP_MAC( a, b ) (((((unsigned char*)a)[0])==(((unsigned char*)b)[0]))&& \
((((unsigned char*)a)[1])==(((unsigned char*)b)[1]))&& \
((((unsigned char*)a)[2])==(((unsigned char*)b)[2]))&& \
((((unsigned char*)a)[3])==(((unsigned char*)b)[3]))&& \
((((unsigned char*)a)[4])==(((unsigned char*)b)[4]))&& \
((((unsigned char*)a)[5])==(((unsigned char*)b)[5])))
struct whd_scan_userdata {
rtos::Semaphore *sema;
scan_result_type sres_type;
WiFiAccessPoint *aps;
std::vector<whd_scan_result_t> *result_buff;
unsigned count;
unsigned offset;
bool scan_in_progress;
};
static whd_scan_userdata interal_scan_data;
static whd_scan_result_t internal_scan_result;
static uint16_t sta_link_update_entry = 0xFF;
static const whd_event_num_t sta_link_change_events[] = {
WLC_E_SET_SSID, WLC_E_LINK, WLC_E_AUTH, WLC_E_ASSOC, WLC_E_DEAUTH_IND, WLC_E_DISASSOC_IND, WLC_E_DISASSOC,
WLC_E_REASSOC, WLC_E_PSK_SUP, WLC_E_ACTION_FRAME_COMPLETE, WLC_E_NONE
};
extern "C" void whd_emac_wifi_link_state_changed(whd_interface_t ifp, whd_bool_t state_up);
int whd_toerror(whd_result_t res)
{
switch (res) {
case WHD_SUCCESS:
return NSAPI_ERROR_OK;
case WHD_UNSUPPORTED:
case WHD_WLAN_UNSUPPORTED:
case WHD_WLAN_ACM_NOTSUPPORTED:
return NSAPI_ERROR_UNSUPPORTED;
case WHD_BADARG:
case WHD_WLAN_BADARG:
return NSAPI_ERROR_PARAMETER;
case WHD_WLAN_NOTASSOCIATED:
case WHD_INVALID_JOIN_STATUS:
return NSAPI_ERROR_NO_CONNECTION;
case WHD_BUFFER_UNAVAILABLE_PERMANENT:
case WHD_BUFFER_UNAVAILABLE_TEMPORARY:
case WHD_RX_BUFFER_ALLOC_FAIL:
case WHD_BUFFER_ALLOC_FAIL:
case WHD_WLAN_NOMEM:
case WHD_MALLOC_FAILURE:
return NSAPI_ERROR_NO_MEMORY;
case WHD_ACCESS_POINT_NOT_FOUND:
case WHD_NETWORK_NOT_FOUND:
return NSAPI_ERROR_NO_SSID;
case WHD_NOT_AUTHENTICATED:
case WHD_INVALID_KEY:
case WHD_NOT_KEYED:
return NSAPI_ERROR_AUTH_FAILURE;
case WHD_PENDING:
case WHD_JOIN_IN_PROGRESS:
return NSAPI_ERROR_IN_PROGRESS;
case WHD_CONNECTION_LOST:
return NSAPI_ERROR_CONNECTION_LOST;
case WHD_TIMEOUT:
case WHD_EAPOL_KEY_PACKET_M1_TIMEOUT:
case WHD_EAPOL_KEY_PACKET_M3_TIMEOUT:
case WHD_EAPOL_KEY_PACKET_G1_TIMEOUT:
return NSAPI_ERROR_CONNECTION_TIMEOUT;
case WHD_WLAN_BUSY:
return NSAPI_ERROR_BUSY;
case WHD_WLAN_NODEVICE:
return NSAPI_ERROR_DEVICE_ERROR;
default:
return -res;
}
}
static nsapi_security_t whd_tosecurity(whd_security_t sec)
{
switch (sec) {
case WHD_SECURITY_OPEN:
return NSAPI_SECURITY_NONE;
case WHD_SECURITY_WEP_PSK:
case WHD_SECURITY_WEP_SHARED:
return NSAPI_SECURITY_WEP;
case WHD_SECURITY_WPA_TKIP_PSK:
case WHD_SECURITY_WPA_TKIP_ENT:
return NSAPI_SECURITY_WPA;
case WHD_SECURITY_WPA2_MIXED_PSK:
return NSAPI_SECURITY_WPA_WPA2;
case WHD_SECURITY_WPA2_MIXED_ENT:
return NSAPI_SECURITY_WPA2_ENT;
case WHD_SECURITY_WPA2_AES_PSK:
case WHD_SECURITY_WPA2_AES_ENT:
case WHD_SECURITY_WPA2_FBT_PSK:
case WHD_SECURITY_WPA2_FBT_ENT:
return NSAPI_SECURITY_WPA2;
default:
return NSAPI_SECURITY_UNKNOWN;
}
}
whd_security_t whd_fromsecurity(nsapi_security_t sec)
{
switch (sec) {
case NSAPI_SECURITY_NONE:
return WHD_SECURITY_OPEN;
case NSAPI_SECURITY_WEP:
return WHD_SECURITY_WEP_PSK;
case NSAPI_SECURITY_WPA:
return WHD_SECURITY_WPA_MIXED_PSK;
case NSAPI_SECURITY_WPA2:
return WHD_SECURITY_WPA2_AES_PSK;
case NSAPI_SECURITY_WPA_WPA2:
return WHD_SECURITY_WPA2_MIXED_PSK;
case NSAPI_SECURITY_WPA2_ENT:
return WHD_SECURITY_WPA2_MIXED_ENT;
default:
return WHD_SECURITY_UNKNOWN;
}
}
static void *whd_wifi_link_state_change_handler(whd_interface_t ifp,
const whd_event_header_t *event_header,
const uint8_t *event_data,
void *handler_user_data)
{
UNUSED_PARAMETER(event_data);
if (event_header->bsscfgidx >= WHD_INTERFACE_MAX) {
WPRINT_WHD_DEBUG(("%s: event_header: Bad interface\n", __FUNCTION__));
return NULL;
}
if (event_header->event_type == WLC_E_DEAUTH_IND ||
event_header->event_type == WLC_E_DISASSOC_IND) {
whd_emac_wifi_link_state_changed(ifp, WHD_FALSE);
}
if (whd_wifi_is_ready_to_transceive(ifp) == WHD_SUCCESS) {
whd_emac_wifi_link_state_changed(ifp, WHD_TRUE);
}
return handler_user_data;
}
MBED_WEAK WhdSTAInterface::OlmInterface &WhdSTAInterface::OlmInterface::get_default_instance()
{
static OlmInterface olm;
return olm;
}
WhdSTAInterface::WhdSTAInterface(WHD_EMAC &emac, OnboardNetworkStack &stack, OlmInterface &olm)
: EMACInterface(emac, stack),
_ssid("\0"),
_pass("\0"),
_security(NSAPI_SECURITY_NONE),
_whd_emac(emac),
_olm(&olm)
{
}
nsapi_error_t WhdSTAInterface::connect(
const char *ssid, const char *pass,
nsapi_security_t security,
uint8_t channel)
{
int err = set_channel(channel);
if (err) {
return err;
}
err = set_credentials(ssid, pass, security);
if (err) {
return err;
}
return connect();
}
nsapi_error_t WhdSTAInterface::set_credentials(const char *ssid, const char *pass, nsapi_security_t security)
{
if ((ssid == NULL) ||
(strlen(ssid) == 0) ||
(pass == NULL && (security != NSAPI_SECURITY_NONE && security != NSAPI_SECURITY_WPA2_ENT)) ||
(strlen(pass) == 0 && (security != NSAPI_SECURITY_NONE && security != NSAPI_SECURITY_WPA2_ENT)) ||
(strlen(pass) > 63 && (security == NSAPI_SECURITY_WPA2 || security == NSAPI_SECURITY_WPA || security == NSAPI_SECURITY_WPA_WPA2))
) {
return NSAPI_ERROR_PARAMETER;
}
memset(_ssid, 0, sizeof(_ssid));
strncpy(_ssid, ssid, sizeof(_ssid));
memset(_pass, 0, sizeof(_pass));
strncpy(_pass, pass, sizeof(_pass));
_security = security;
return NSAPI_ERROR_OK;
}
nsapi_error_t WhdSTAInterface::connect()
{
#define MAX_RETRY_COUNT ( 5 )
int i;
whd_result_t res;
// initialize wiced, this is noop if already init
if (!_whd_emac.powered_up) {
_whd_emac.power_up();
}
res = whd_management_set_event_handler(_whd_emac.ifp, sta_link_change_events,
whd_wifi_link_state_change_handler, NULL, &sta_link_update_entry);
if (res != WHD_SUCCESS) {
return whd_toerror(res);
}
if (!_interface) {
nsapi_error_t err = _stack.add_ethernet_interface(_emac, true, &_interface);
if (err != NSAPI_ERROR_OK) {
_interface = NULL;
return err;
}
_interface->attach(_connection_status_cb);
}
// Initialize the Offload Manager
if (_olm != NULL) {
_olm->init_ols(_whd_emac.ifp, this);
}
if ((_ssid == NULL) ||
(strlen(_ssid) == 0)) {
return NSAPI_ERROR_PARAMETER;
}
// setup ssid
whd_ssid_t ssid;
strncpy((char *)ssid.value, _ssid, SSID_NAME_SIZE);
ssid.value[SSID_NAME_SIZE - 1] = '\0';
ssid.length = strlen((char *)ssid.value);
// choose network security
whd_security_t security = whd_fromsecurity(_security);
// join the network
for (i = 0; i < MAX_RETRY_COUNT; i++) {
res = (whd_result_t)whd_wifi_join(_whd_emac.ifp,
&ssid,
security,
(const uint8_t *)_pass, strlen(_pass));
if (res == WHD_SUCCESS) {
break;
}
}
if (res != WHD_SUCCESS) {
return whd_toerror(res);
}
if (whd_wifi_is_ready_to_transceive(_whd_emac.ifp) == WHD_SUCCESS) {
whd_emac_wifi_link_state_changed(_whd_emac.ifp, WHD_TRUE);
}
// bring up
return _interface->bringup(_dhcp,
_ip_address[0] ? _ip_address : 0,
_netmask[0] ? _netmask : 0,
_gateway[0] ? _gateway : 0,
DEFAULT_STACK);
}
void WhdSTAInterface::wifi_on()
{
if (!_whd_emac.powered_up) {
_whd_emac.power_up();
}
}
nsapi_error_t WhdSTAInterface::disconnect()
{
if (!_interface) {
return NSAPI_STATUS_DISCONNECTED;
}
// bring down
int err = _interface->bringdown();
if (err) {
return err;
}
// leave network
whd_result_t res = whd_wifi_leave(_whd_emac.ifp);
if (res != WHD_SUCCESS) {
return whd_toerror(res);
}
whd_emac_wifi_link_state_changed(_whd_emac.ifp, WHD_FALSE);
res = whd_wifi_deregister_event_handler(_whd_emac.ifp, sta_link_update_entry);
if (res != WHD_SUCCESS) {
return whd_toerror(res);
}
// de-init Offload Manager
if (_olm != NULL) {
_olm->deinit_ols();
}
return NSAPI_ERROR_OK;
}
int8_t WhdSTAInterface::get_rssi()
{
int32_t rssi;
whd_result_t res;
// initialize wiced, this is noop if already init
if (!_whd_emac.powered_up) {
_whd_emac.power_up();
}
res = (whd_result_t)whd_wifi_get_rssi(_whd_emac.ifp, &rssi);
if (res != 0) {
return 0;
}
return (int8_t)rssi;
}
static void whd_scan_handler(whd_scan_result_t **result_ptr,
void *user_data, whd_scan_status_t status)
{
whd_scan_userdata *data = (whd_scan_userdata *)user_data;
/* Even after stopping scan, some results will still come as results are already present in the queue */
if (data->scan_in_progress == false) {
return;
}
// finished scan, either succesfully or through an abort
if (status != WHD_SCAN_INCOMPLETE) {
data->scan_in_progress = false;
data->sema->release();
return;
}
// can't really keep anymore scan results
if (data->count > 0 && data->offset >= data->count) {
/* We can not abort the scan as this function is getting executed in WHD context,
Note that to call any WHD API, caller function should not in WHD context */
return;
}
whd_scan_result_t *record = *result_ptr;
for (unsigned int i = 0; i < data->result_buff->size(); i++) {
if (CMP_MAC((*data->result_buff)[i].BSSID.octet, record->BSSID.octet)) {
return;
}
}
if (data->count > 0 && data->aps != NULL) {
// get ap stats
nsapi_wifi_ap ap;
uint8_t length = record->SSID.length;
if (length < sizeof(ap.ssid) - 1) {
length = sizeof(ap.ssid) - 1;
}
memcpy(ap.ssid, record->SSID.value, length);
ap.ssid[length] = '\0';
memcpy(ap.bssid, record->BSSID.octet, sizeof(ap.bssid));
ap.security = whd_tosecurity(record->security);
ap.rssi = record->signal_strength;
ap.channel = record->channel;
if (data->sres_type == SRES_TYPE_WIFI_ACCESS_POINT) {
data->aps[data->offset] = WiFiAccessPoint(ap);
} else if (data->sres_type == SRES_TYPE_WHD_ACCESS_POINT) {
WhdAccessPoint *aps_sres = static_cast<WhdAccessPoint *>(data->aps);
aps_sres[data->offset] = std::move(WhdAccessPoint(ap, record->bss_type,
record->ie_ptr, record->ie_len));
}
}
// store to result_buff for future duplication removal
data->result_buff->push_back(*record);
data->offset = data->result_buff->size();
}
int WhdSTAInterface::internal_scan(WiFiAccessPoint *aps, unsigned count, scan_result_type sres_type)
{
// initialize wiced, this is noop if already init
if (!_whd_emac.powered_up) {
_whd_emac.power_up();
}
interal_scan_data.sema = new Semaphore();
interal_scan_data.sres_type = sres_type;
interal_scan_data.aps = aps;
interal_scan_data.count = count;
interal_scan_data.offset = 0;
interal_scan_data.scan_in_progress = true;
interal_scan_data.result_buff = new std::vector<whd_scan_result_t>();
whd_result_t whd_res;
int res;
whd_res = (whd_result_t)whd_wifi_scan(_whd_emac.ifp, WHD_SCAN_TYPE_ACTIVE, WHD_BSS_TYPE_ANY,
NULL, NULL, NULL, NULL, whd_scan_handler, &internal_scan_result, &interal_scan_data);
if (whd_res != WHD_SUCCESS) {
res = whd_toerror(whd_res);
} else {
interal_scan_data.sema->acquire();
res = interal_scan_data.offset;
}
delete interal_scan_data.sema;
delete interal_scan_data.result_buff;
return res;
}
int WhdSTAInterface::scan(WiFiAccessPoint *aps, unsigned count)
{
return internal_scan(aps, count, SRES_TYPE_WIFI_ACCESS_POINT);
}
int WhdSTAInterface::scan_whd(WhdAccessPoint *aps, unsigned count)
{
return internal_scan(aps, count, SRES_TYPE_WHD_ACCESS_POINT);
}
int WhdSTAInterface::is_interface_connected(void)
{
if (!_whd_emac.ifp) {
return WHD_INTERFACE_NOT_UP;
}
_whd_emac.ifp->role = WHD_STA_ROLE;
if ((whd_wifi_is_ready_to_transceive(_whd_emac.ifp) == WHD_SUCCESS)) {
return WHD_SUCCESS;
} else {
return WHD_CONNECTION_LOST;
}
}
int WhdSTAInterface::get_bssid(uint8_t *bssid)
{
whd_mac_t ap_mac;
whd_result_t res = WHD_SUCCESS;
memset(&ap_mac, 0, sizeof(ap_mac));
_whd_emac.ifp->role = WHD_STA_ROLE;
if ((whd_wifi_is_ready_to_transceive(_whd_emac.ifp) == WHD_SUCCESS)) {
res = whd_wifi_get_bssid(_whd_emac.ifp, &ap_mac);
if (res == WHD_SUCCESS) {
memcpy(bssid, ap_mac.octet, sizeof(ap_mac.octet));
}
}
return res;
}
int WhdSTAInterface::whd_log_print(void)
{
return whd_wifi_print_whd_log(_whd_emac.drvp);
}
int WhdSTAInterface::whd_log_read(char *buffer, int buffer_size)
{
whd_result_t res = WHD_SUCCESS;
if (buffer != NULL) {
res = whd_wifi_read_wlan_log(_whd_emac.drvp, buffer, buffer_size);
}
return res;
}
nsapi_error_t WhdSTAInterface::wifi_get_ac_params_sta(void *acp)
{
whd_result_t res = WHD_SUCCESS;
edcf_acparam_t *ac_param = (edcf_acparam_t *)acp;
res = whd_wifi_get_acparams(_whd_emac.ifp, ac_param);
if (res != WHD_SUCCESS) {
return res;
}
return res;
}
int WhdSTAInterface::wifi_set_iovar_value(const char *iovar, uint32_t value)
{
whd_result_t res = WHD_SUCCESS;
_whd_emac.ifp->role = WHD_STA_ROLE;
res = whd_wifi_set_iovar_value(_whd_emac.ifp, iovar, value);
return res;
}
int WhdSTAInterface::wifi_get_iovar_value(const char *iovar, uint32_t *value)
{
int res = WHD_SUCCESS;
_whd_emac.ifp->role = WHD_STA_ROLE;
res = whd_wifi_get_iovar_value(_whd_emac.ifp, iovar, value);
return res;
}
int WhdSTAInterface::wifi_set_ioctl_value(uint32_t ioctl, uint32_t value)
{
int res = WHD_SUCCESS;
_whd_emac.ifp->role = WHD_STA_ROLE;
res = whd_wifi_set_ioctl_value(_whd_emac.ifp, ioctl, value);
return res;
}
int WhdSTAInterface::wifi_get_ifp(whd_interface_t *ifp)
{
int res = WHD_SUCCESS;
*ifp = _whd_emac.ifp;
return res;
}
int WhdSTAInterface::wifi_set_up(void)
{
int res = WHD_SUCCESS;
res = whd_wifi_set_up(_whd_emac.ifp);
return res;
}
int WhdSTAInterface::wifi_set_down(void)
{
int res = WHD_SUCCESS;
res = whd_wifi_set_down(_whd_emac.ifp);
return res;
}
int WhdSTAInterface::wifi_set_coex_config(whd_coex_config_t *coex_config)
{
int res = WHD_SUCCESS;
res = whd_wifi_set_coex_config(_whd_emac.ifp, coex_config);
return res;
}