Newer
Older
mbed-os / connectivity / drivers / emac / TARGET_Cypress / COMPONENT_SCL / interface / SclSTAInterface.cpp
/*
 * Copyright 2018-2020 Cypress Semiconductor Corporation
 * 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 "SclSTAInterface.h"
#include "nsapi.h"
#include "lwipopts.h"
#include "lwip/etharp.h"
#include "lwip/ethip6.h"
#include "rtos.h"
#include "scl_emac.h"
#include "scl_ipc.h"
#include "mbed_wait_api.h"


/** @file
 *  Provides SCL interface functions to be used with WiFiInterface or NetworkInterface Objects
 */
#define MIN_SSID_LENGTH         (0)
#define MIN_PASSWORD_LENGTH     (0)

struct scl_tx_net_credentials {
    nsapi_security_t network_security_type;
    int ssid_len;
    int pass_len;
    const char *network_ssid;
    const char *network_passphrase;
} scl_tx_network_credentials;

network_params_t network_parameter;

int scl_toerror(scl_result_t res)
{
    switch (res) {
        case SCL_SUCCESS:
            return NSAPI_ERROR_OK;
        case SCL_UNSUPPORTED:
            return NSAPI_ERROR_UNSUPPORTED;
        case SCL_BADARG:
            return NSAPI_ERROR_PARAMETER;
        case SCL_INVALID_JOIN_STATUS:
            return NSAPI_ERROR_NO_CONNECTION;
        case SCL_BUFFER_UNAVAILABLE_PERMANENT:
        case SCL_BUFFER_UNAVAILABLE_TEMPORARY:
        case SCL_RX_BUFFER_ALLOC_FAIL:
        case SCL_BUFFER_ALLOC_FAIL:
        case SCL_MALLOC_FAILURE:
            return NSAPI_ERROR_NO_MEMORY;
        case SCL_ACCESS_POINT_NOT_FOUND:
        case SCL_NETWORK_NOT_FOUND:
            return NSAPI_ERROR_NO_SSID;
        case SCL_NOT_AUTHENTICATED:
        case SCL_INVALID_KEY:
        case SCL_NOT_KEYED:
            return NSAPI_ERROR_AUTH_FAILURE;
        case SCL_PENDING:
        case SCL_JOIN_IN_PROGRESS:
            return NSAPI_ERROR_IN_PROGRESS;
        case SCL_CONNECTION_LOST:
            return NSAPI_ERROR_CONNECTION_LOST;
        case SCL_TIMEOUT:
        case SCL_EAPOL_KEY_PACKET_M1_TIMEOUT:
        case SCL_EAPOL_KEY_PACKET_M3_TIMEOUT:
        case SCL_EAPOL_KEY_PACKET_G1_TIMEOUT:
            return NSAPI_ERROR_CONNECTION_TIMEOUT;
        default:
            return -res;
    }
}

nsapi_security_t scl_tosecurity(scl_security_t sec)
{
    switch (sec) {
        case SCL_SECURITY_OPEN:
            return NSAPI_SECURITY_NONE;
        case SCL_SECURITY_WEP_PSK:
        case SCL_SECURITY_WEP_SHARED:
            return NSAPI_SECURITY_WEP;
        case SCL_SECURITY_WPA_TKIP_PSK:
        case SCL_SECURITY_WPA_TKIP_ENT:
            return NSAPI_SECURITY_WPA;
        case SCL_SECURITY_WPA2_MIXED_PSK:
            return NSAPI_SECURITY_WPA_WPA2;
        case SCL_SECURITY_WPA2_AES_PSK:
        case SCL_SECURITY_WPA2_AES_ENT:
        case SCL_SECURITY_WPA2_FBT_PSK:
        case SCL_SECURITY_WPA2_FBT_ENT:
            return NSAPI_SECURITY_WPA2;
        default:
            return NSAPI_SECURITY_UNKNOWN;
    }
}

scl_security_t scl_fromsecurity(nsapi_security_t sec)
{
    switch (sec) {
        case NSAPI_SECURITY_NONE:
            return SCL_SECURITY_OPEN;
        case NSAPI_SECURITY_WEP:
            return SCL_SECURITY_WEP_PSK;
        case NSAPI_SECURITY_WPA:
            return SCL_SECURITY_WPA_MIXED_PSK;
        case NSAPI_SECURITY_WPA2:
            return SCL_SECURITY_WPA2_AES_PSK;
        case NSAPI_SECURITY_WPA_WPA2:
            return SCL_SECURITY_WPA2_MIXED_PSK;
        default:
            return SCL_SECURITY_UNKNOWN;
    }
}

SclSTAInterface::SclSTAInterface(SCL_EMAC &emac, OnboardNetworkStack &stack)
    : EMACInterface(emac, stack),
      _ssid("\0"),
      _pass("\0"),
      _security(NSAPI_SECURITY_NONE),
      _scl_emac(emac)
{
}

nsapi_error_t SclSTAInterface::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 SclSTAInterface::set_credentials(const char *ssid, const char *pass, nsapi_security_t security)
{
    if ((ssid == NULL) ||
            (strlen(ssid) == 0) ||
            (pass == NULL && (security != NSAPI_SECURITY_NONE)) ||
            (strlen(pass) == 0 && (security != NSAPI_SECURITY_NONE)) ||
            (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 SclSTAInterface::connect()
{

    uint32_t delay_timeout = 0;
    scl_result_t ret_val;
    nsapi_error_t interface_status;
    uint32_t connection_status = 0;

    scl_tx_network_credentials.network_ssid = _ssid;
    if ((strlen(_ssid) < MAX_SSID_LENGTH) && (strlen(_ssid) > MIN_SSID_LENGTH)) {
        scl_tx_network_credentials.ssid_len = strlen(_ssid);
    } else {
        return NSAPI_ERROR_PARAMETER;
    }
    scl_tx_network_credentials.network_passphrase = _pass;
    if (((strlen(_pass) < MAX_PASSWORD_LENGTH) && (strlen(_pass) > MIN_PASSWORD_LENGTH)) || (_security == NSAPI_SECURITY_NONE)) {
        scl_tx_network_credentials.pass_len = strlen(_pass);
    } else {
        return NSAPI_ERROR_PARAMETER;
    }
    scl_tx_network_credentials.network_security_type = _security;

    ret_val = scl_send_data(SCL_TX_CONNECT, (char *)&scl_tx_network_credentials, TIMER_DEFAULT_VALUE);

    if (ret_val == SCL_SUCCESS) {
        SCL_LOG(("wifi provisioning in progress\r\n"));
    }

    network_parameter.connection_status = NSAPI_STATUS_DISCONNECTED;


    //Get the network parameter from NP
    while ((network_parameter.connection_status != NSAPI_STATUS_GLOBAL_UP) && delay_timeout < NW_CONNECT_TIMEOUT) {
        ret_val = scl_get_nw_parameters(&network_parameter);
        wait_us(NW_DELAY_TIME_US);
        delay_timeout++;
    }

    if (delay_timeout >= NW_CONNECT_TIMEOUT || ret_val != SCL_SUCCESS) {
        return NSAPI_ERROR_NO_CONNECTION;
    }

    if (!_scl_emac.powered_up) {
        _scl_emac.power_up();
    }

    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);
    }

    if (!scl_wifi_is_ready_to_transceive()) {
        scl_emac_wifi_link_state_changed(true);
    }

    interface_status = _interface->bringup(false,
                                           network_parameter.ip_address,
                                           network_parameter.netmask,
                                           network_parameter.gateway,
                                           DEFAULT_STACK);

    if (interface_status == NSAPI_ERROR_OK) {
        scl_send_data(SCL_TX_CONNECTION_STATUS, (char *)&connection_status, TIMER_DEFAULT_VALUE);
    }

    return interface_status;
}

void SclSTAInterface::wifi_on()
{
    if (!_scl_emac.powered_up) {
        _scl_emac.power_up();
    }
}

nsapi_error_t SclSTAInterface::disconnect()
{
    scl_result_t ret_val;
    nsapi_error_t disconnect_status;
    uint32_t delay_timeout = 0;

    ret_val = scl_send_data(SCL_TX_DISCONNECT, (char *)&disconnect_status, TIMER_DEFAULT_VALUE);

    if (ret_val == SCL_ERROR) {
        return NSAPI_ERROR_TIMEOUT;
    }

    if (!_interface) {
        return NSAPI_ERROR_NO_CONNECTION;
    }

    // block till disconnected from network
    while ((network_parameter.connection_status != NSAPI_STATUS_DISCONNECTED) && delay_timeout < NW_DISCONNECT_TIMEOUT) {
        ret_val = scl_get_nw_parameters(&network_parameter);
        wait_us(NW_DELAY_TIME_US);
        delay_timeout++;
    }

    if (delay_timeout >= NW_DISCONNECT_TIMEOUT) {
        return NSAPI_ERROR_TIMEOUT;
    }

    // bring down
    int err = _interface->bringdown();
    if (err) {
        return err;
    }

    scl_emac_wifi_link_state_changed(false);

    return NSAPI_ERROR_OK;
}

int SclSTAInterface::scan(WiFiAccessPoint *res, unsigned count)
{
    /* To Do */
    return NSAPI_ERROR_UNSUPPORTED;
}

int8_t SclSTAInterface::get_rssi()
{
    int32_t rssi;
    scl_result_t res;

    if (!_scl_emac.powered_up) {
        _scl_emac.power_up();
    }

    res = (scl_result_t) scl_wifi_get_rssi(&rssi);
    if (res == SCL_ERROR) {
        return SCL_ERROR;
    }

    return (int8_t)rssi;
}

int SclSTAInterface::is_interface_connected(void)
{
    if (scl_wifi_is_ready_to_transceive() == SCL_SUCCESS) {
        return SCL_SUCCESS;
    } else {
        return SCL_CONNECTION_LOST;
    }
}

int SclSTAInterface::get_bssid(uint8_t *bssid)
{
    scl_mac_t ap_mac;
    scl_result_t res = SCL_SUCCESS;

    if (bssid == NULL) {
        return SCL_BADARG;
    }

    memset(&ap_mac, 0, sizeof(ap_mac));
    if (scl_wifi_is_ready_to_transceive() == SCL_SUCCESS) {
        res = (scl_result_t) scl_wifi_get_bssid(&ap_mac);
        if (res == SCL_SUCCESS) {
            memcpy(bssid, ap_mac.octet, sizeof(ap_mac.octet));
        }
    } else {
        return SCL_CONNECTION_LOST;
    }
    return res;
}

int SclSTAInterface::wifi_set_up(void)
{
    int res = SCL_SUCCESS;
    res = scl_wifi_set_up();
    return res;
}