mbed-os/connectivity/drivers/cellular/TELIT/ME310/TELIT_ME310_CellularStack.cpp

646 lines
21 KiB
C++

/*
* Copyright (c) 2020, Arm Limited and affiliates.
* 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 <stdio.h>
#include <string.h>
#include "TELIT_ME310_CellularStack.h"
#include "CellularLog.h"
#include "netsocket/TLSSocket.h"
static const int sslctxID = 1;
using namespace mbed;
TELIT_ME310_CellularStack::TELIT_ME310_CellularStack(ATHandler &atHandler, int cid, nsapi_ip_stack_t stack_type, AT_CellularDevice &device) :
AT_CellularStack(atHandler, cid, stack_type, device)
, _tls_sec_level(0)
{
_at.set_urc_handler("SRING:", mbed::Callback<void()>(this, &TELIT_ME310_CellularStack::urc_sring));
// TODO: this needs to be handled properly, but now making just a quick hack
// Close all SSL sockets if open. This can happen for example if application processor
// was reset but modem not. Old sockets are still up and running and it prevents
// new SSL configurations and creating new sockets.
for (int i = 1; i <= ME310_SOCKET_MAX; i++) {
_at.clear_error();
tr_debug("Closing SSL socket %d...", i);
_at.at_cmd_discard("#SSLH", "=", "%d", "0", i);
}
_at.clear_error();
}
TELIT_ME310_CellularStack::~TELIT_ME310_CellularStack()
{
}
nsapi_error_t TELIT_ME310_CellularStack::socket_listen(nsapi_socket_t handle, int backlog)
{
return NSAPI_ERROR_UNSUPPORTED;
}
nsapi_error_t TELIT_ME310_CellularStack::socket_accept(void *server, void **socket, SocketAddress *addr)
{
return NSAPI_ERROR_UNSUPPORTED;
}
nsapi_error_t TELIT_ME310_CellularStack::socket_connect(nsapi_socket_t handle, const SocketAddress &address)
{
CellularSocket *socket = (CellularSocket *)handle;
if (!is_ipeasy_context_activated(_cid)) {
activate_ipeasy_context(_cid);
}
int modem_connect_id = -1;
int err = NSAPI_ERROR_NO_CONNECTION;
int request_connect_id = find_socket_index(socket);
// assert here as its a programming error if the socket container doesn't contain
// specified handle
MBED_ASSERT(request_connect_id != -1);
_at.lock();
// Configure SRING URC
_at.at_cmd_discard("#SCFGEXT", "=", "%d%d%d%d",
request_connect_id + 1,
1, // SRING URC mode - data amount mode
0, // Data view mode - text mode
0); // TCP keepalive - deactivated
if (_at.get_last_error() != NSAPI_ERROR_OK) {
tr_warn("Unable to configure socket %d", request_connect_id);
}
if (socket->proto == NSAPI_TCP) {
if (socket->tls_socket) {
if (_tls_sec_level == 0) {
_at.unlock();
return NSAPI_ERROR_AUTH_FAILURE;
}
_at.at_cmd_discard("#SSLD", "=", "%d%d%s%d", sslctxID,
address.get_port(),
address.get_ip_address(),
0); // Closure type (0)
if (_at.get_last_error() != NSAPI_ERROR_OK) {
// Hit some sort of error opening the socket
socket->id = -1;
_at.unlock();
return NSAPI_ERROR_PARAMETER;
}
} else {
_at.at_cmd_discard("#SD", "=", "%d%d%d%s%d%d%d", request_connect_id + 1, 0, address.get_port(), address.get_ip_address(), 0,
0, 1);
if (_at.get_last_error() != NSAPI_ERROR_OK) {
// Hit some sort of error opening the socket
socket->id = -1;
_at.unlock();
return NSAPI_ERROR_PARAMETER;
}
}
}
nsapi_error_t ret_val = _at.get_last_error();
_at.unlock();
if (ret_val == NSAPI_ERROR_OK) {
socket->id = request_connect_id;
socket->remoteAddress = address;
socket->connected = true;
return NSAPI_ERROR_OK;
}
return err;
}
void TELIT_ME310_CellularStack::urc_sring()
{
_at.lock();
const int sock_id = _at.read_int() - 1;
const int data_bytes_remaining = _at.read_int();
const nsapi_error_t err = _at.unlock_return_error();
if (err != NSAPI_ERROR_OK) {
return;
}
CellularSocket *sock = find_socket(sock_id);
if (sock) {
sock->pending_bytes = data_bytes_remaining;
if (sock->_cb) {
sock->_cb(sock->_data);
}
}
}
int TELIT_ME310_CellularStack::get_max_socket_count()
{
return ME310_SOCKET_MAX;
}
bool TELIT_ME310_CellularStack::is_protocol_supported(nsapi_protocol_t protocol)
{
return (protocol == NSAPI_UDP || protocol == NSAPI_TCP);
}
nsapi_error_t TELIT_ME310_CellularStack::socket_close_impl(int sock_id)
{
_at.set_at_timeout(ME310_CLOSE_SOCKET_TIMEOUT);
nsapi_error_t err;
CellularSocket *socket = find_socket(sock_id);
if (socket && socket->tls_socket) {
err = _at.at_cmd_discard("#SSLH", "=", "%d%d", sock_id, 0);
if (err == NSAPI_ERROR_OK) {
// Disable TLSSocket settings to prevent reuse on next socket without setting the values
_tls_sec_level = 0;
err = _at.at_cmd_discard("#SSLEN", "=,", "%d%d", sslctxID, 0);
}
} else {
err = _at.at_cmd_discard("#SH", "=", "%d", sock_id + 1);
}
_at.restore_at_timeout();
return err;
}
bool TELIT_ME310_CellularStack::is_ipeasy_context_activated(int context_id)
{
_at.lock();
_at.cmd_start_stop("#SGACT?", "");
_at.resp_start("#SGACT:");
int current_context_id = -1;
int current_stat = -1;
for (int i = 0; i < ME310_CONTEXT_MAX; i++) {
current_context_id = _at.read_int();
current_stat = _at.read_int();
if (current_context_id == context_id) {
_at.resp_stop();
_at.unlock();
return current_stat == ME310_IPEASY_ACTIVATED_CONTEXT;
}
}
_at.resp_stop();
_at.unlock();
return false;
}
nsapi_error_t TELIT_ME310_CellularStack::activate_ipeasy_context(int context_id)
{
_at.lock();
_at.at_cmd_discard("#SGACT", "=", "%d%d", context_id, ME310_IPEASY_ACTIVATED_CONTEXT);
return _at.unlock_return_error();
}
nsapi_error_t TELIT_ME310_CellularStack::deactivate_ipeasy_context(int context_id)
{
_at.lock();
_at.at_cmd_discard("#SGACT", "=", "%d%d", context_id, ME310_IPEASY_DEACTIVATED_CONTEXT);
return _at.unlock_return_error();
}
nsapi_error_t TELIT_ME310_CellularStack::create_socket_impl(CellularSocket *socket)
{
int modem_connect_id = -1;
int remote_port = 1;
int err = -1;
if (!is_ipeasy_context_activated(_cid)) {
tr_debug("IPEasy context not active for %d", _cid);
activate_ipeasy_context(_cid);
}
int request_connect_id = find_socket_index(socket);
// assert here as its a programming error if the socket container doesn't contain
// specified handle
MBED_ASSERT(request_connect_id != -1);
// Configure SRING URC
_at.at_cmd_discard("#SCFGEXT", "=", "%d%d%d%d",
request_connect_id + 1,
1, // SRING URC mode - data amount mode
0, // Data view mode - text mode
0); // TCP keepalive - deactivated
if (_at.get_last_error() != NSAPI_ERROR_OK) {
tr_warn("Unable to configure socket %d", request_connect_id);
}
if (socket->proto == NSAPI_UDP) {
_at.at_cmd_discard("#SD", "=", "%d%d%d%s%d%d%d", request_connect_id + 1, 1, remote_port,
(_ip_ver_sendto == NSAPI_IPv4) ? "127.0.0.1" : "0:0:0:0:0:0:0:1",
0, socket->localAddress.get_port(), 1);
if (_at.get_last_error() != NSAPI_ERROR_OK) {
// Hit some sort of error opening the socket
socket->id = -1;
return NSAPI_ERROR_PARAMETER;
}
} else if (socket->proto == NSAPI_TCP) {
_at.at_cmd_discard("#SD", "=", "%d%d%d%s%d%d%d%d", request_connect_id + 1, 0, remote_port,
socket->remoteAddress.get_ip_address(), 0, 0, 1);
if (_at.get_last_error() != NSAPI_ERROR_OK) {
// Hit some sort of error opening the socket
socket->id = -1;
return NSAPI_ERROR_PARAMETER;
}
}
nsapi_error_t ret_val = _at.get_last_error();
if (ret_val == NSAPI_ERROR_OK) {
socket->id = request_connect_id;
}
return ret_val;
}
nsapi_size_or_error_t TELIT_ME310_CellularStack::socket_sendto_impl(CellularSocket *socket, const SocketAddress &address,
const void *data, nsapi_size_t size)
{
tr_debug("TELIT_ME310_CellularStack::socket_sendto_impl()");
int sent_len = 0;
bool success = true;
const char *buf = (const char *) data;
nsapi_size_t blk = ME310_MAX_SEND_SIZE;
nsapi_size_t count = size;
int sent_len_before = 0;
int sent_len_after = 0;
while ((count > 0) && success) {
if (count < blk) {
blk = count;
}
if (socket->tls_socket) {
sent_len_after = blk;
} else {
// Get the sent count before sending
_at.cmd_start_stop("#SI", "=", "%d", socket->id + 1);
_at.resp_start("#SI:");
_at.skip_param();
sent_len_before = _at.read_int();
_at.resp_stop();
}
// Send
if (socket->proto == NSAPI_UDP) {
_at.cmd_start_stop("#SSENDUDPEXT", "=", "%d%d%s%d", socket->id + 1, size,
address.get_ip_address(), address.get_port());
} else {
if (socket->tls_socket) {
_at.cmd_start_stop("#SSLSENDEXT", "=", "%d%d", socket->id + 1, size);
} else {
_at.cmd_start_stop("#SSENDEXT", "=", "%d%d", socket->id + 1, size);
}
}
_at.resp_start("> ", true);
_at.write_bytes((uint8_t *)buf, blk);
_at.cmd_start(CTRL_Z);
_at.cmd_stop();
_at.resp_start("\r\nOK", true);
_at.resp_stop();
if (!socket->tls_socket) {
_at.cmd_start_stop("#SI", "=", "%d", socket->id + 1);
_at.resp_start("#SI:");
_at.skip_param();
sent_len_after = _at.read_int();
_at.resp_stop();
}
sent_len = sent_len_after - sent_len_before;
if ((sent_len >= (int) blk) &&
(_at.get_last_error() == NSAPI_ERROR_OK)) {
} else {
success = false;
}
buf += blk;
count -= blk;
}
if (success && _at.get_last_error() == NSAPI_ERROR_OK) {
return size - count;
}
return _at.get_last_error();
}
nsapi_size_or_error_t TELIT_ME310_CellularStack::socket_recvfrom_impl(CellularSocket *socket, SocketAddress *address,
void *buffer, nsapi_size_t size)
{
tr_debug("TELIT_ME310_CellularStack::socket_recvfrom_impl()");
nsapi_size_or_error_t nsapi_error_size = NSAPI_ERROR_DEVICE_ERROR;
bool success = true;
nsapi_size_t read_blk;
nsapi_size_t count = 0;
nsapi_size_t srecv_size;
Timer timer;
int port = -1;
char ip_address[NSAPI_IP_SIZE + 1];
if (socket->pending_bytes == 0) {
_at.process_oob(); // check for SRING URC
if (socket->pending_bytes == 0) {
tr_debug("Socket %d recv would block", socket->id);
return NSAPI_ERROR_WOULD_BLOCK;
}
}
timer.start();
while (success && (size > 0)) {
read_blk = ME310_MAX_RECV_SIZE;
if (read_blk > size) {
read_blk = size;
}
if (socket->pending_bytes > 0) {
if (socket->proto == NSAPI_TCP) {
if (socket->tls_socket) {
_at.cmd_start_stop("#SSLRECV", "=", "%d%d", socket->id + 1, read_blk);
} else {
_at.cmd_start_stop("#SRECV", "=", "%d%d", socket->id + 1, read_blk);
}
} else {
_at.cmd_start_stop("#SRECV", "=", "%d%d%d", socket->id + 1, read_blk, 1);
}
if (socket->tls_socket) {
_at.resp_start("#SSLRECV:");
} else {
_at.resp_start("#SRECV:");
}
if (socket->proto == NSAPI_UDP) {
int data_left = -1;
// UDP has remote_IP and remote_port parameters
_at.read_string(ip_address, sizeof(ip_address));
port = _at.read_int();
// Skip connId
_at.skip_param();
srecv_size = _at.read_int();
data_left = _at.read_int();
if (srecv_size > size) {
srecv_size = size;
}
_at.read_bytes((uint8_t *)buffer + count, srecv_size);
} else {
// Skip connId
_at.skip_param();
srecv_size = _at.read_int();
if (srecv_size > size) {
srecv_size = size;
}
_at.read_bytes((uint8_t *)buffer + count, srecv_size);
}
_at.resp_stop();
if (srecv_size > socket->pending_bytes) {
socket->pending_bytes = 0;
} else {
socket->pending_bytes -= srecv_size;
}
if (srecv_size > 0) {
count += srecv_size;
size -= srecv_size;
} else {
// read() should not fail
success = false;
}
} else if (timer.read_ms() < ME310_SOCKET_TIMEOUT) {
// Wait for URCs
_at.process_oob();
} else {
if (count == 0) {
// Timeout with nothing received
success = false;
}
break;
}
}
timer.stop();
if (!count || (_at.get_last_error() != NSAPI_ERROR_OK)) {
return NSAPI_ERROR_WOULD_BLOCK;
} else {
nsapi_error_size = count;
}
if (success && address) {
address->set_ip_address(ip_address);
address->set_port(port);
}
return nsapi_error_size;
}
#if defined(MBED_CONF_NSAPI_OFFLOAD_TLSSOCKET) && (MBED_CONF_NSAPI_OFFLOAD_TLSSOCKET)
nsapi_error_t TELIT_ME310_CellularStack::setsockopt(nsapi_socket_t handle, int level,
int optname, const void *optval, unsigned optlen)
{
CellularSocket *socket = (CellularSocket *)handle;
nsapi_error_t ret = NSAPI_ERROR_OK;
int ssl_enabled = 0;
uint8_t ctrl_z[] = { 0x1A };
if (level == NSAPI_TLSSOCKET_LEVEL) {
if (optval) {
_at.lock();
switch (optname) {
case NSAPI_TLSSOCKET_ENABLE: {
MBED_ASSERT(optlen == sizeof(bool));
bool *enabled = (bool *)optval;
if (socket->proto == NSAPI_TCP) {
socket->tls_socket = enabled;
if (enabled) {
_at.at_cmd_discard("#SSLEN", "=", "%d%d", sslctxID, 1);
_at.at_cmd_discard("#SSLSECCFG", "=", "%d%d%d", sslctxID, 0, _tls_sec_level);
_at.at_cmd_discard("#SSLCFG", "=", "%d%d%d%d%d%d",
sslctxID,
_cid, // PDP context ID
0, // Packet size (0 is default, select automatically)
90, // Max socket inactivity time (90 is default)
100, // Default timeout when no timeout is set (100 is default)
50); // Transmit timeout (50 is default)
ret = _at.get_last_error();
}
} else {
tr_error("Trying to set non-TCPSocket as TLSSocket");
ret = NSAPI_ERROR_PARAMETER;
}
}
break;
case NSAPI_TLSSOCKET_SET_CACERT: {
_at.cmd_start_stop("#SSLEN", "?");
_at.resp_start("#SSLEN:");
// Skip SSId
_at.skip_param();
ssl_enabled = _at.read_int();
_at.resp_stop();
if (ssl_enabled == 0) {
// SSL not enabled, so enable it
_at.at_cmd_discard("#SSLEN", "=", "%d%d", sslctxID, 1);
}
_at.flush();
const char *cacert = (const char *)optval;
int cacert_size = strlen(cacert);
_at.cmd_start_stop("#SSLSECDATA", "=", "%d%d%d%d", sslctxID,
1, // store data
1, // CA cert
cacert_size);
_at.resp_start("> ", true);
_at.write_bytes((uint8_t *)cacert, cacert_size);
_at.write_bytes(ctrl_z, 1); // Send Ctrl+Z
_at.resp_start("\r\nOK", true);
_at.resp_stop();
ret = _at.get_last_error();
// Set sec level to "Manage server authentication" if only cacert is in use
if (ret == NSAPI_ERROR_OK && _tls_sec_level == 0) {
_tls_sec_level = 1;
}
}
break;
case NSAPI_TLSSOCKET_SET_CLCERT: {
_at.cmd_start_stop("#SSLEN", "?");
_at.resp_start("#SSLEN:");
// Skip SSId
_at.skip_param();
ssl_enabled = _at.read_int();
_at.resp_stop();
if (ssl_enabled == 0) {
// SSL not enabled, so enable it
_at.at_cmd_discard("#SSLEN", "=", "%d%d", sslctxID, 1);
}
_at.flush();
const char *clcert = (const char *)optval;
int clcert_size = strlen(clcert);
_at.cmd_start_stop("#SSLSECDATA", "=", "%d%d%d%d", sslctxID,
1, // store data
0, // client cert
clcert_size);
_at.resp_start("> ", true);
_at.write_bytes((uint8_t *)clcert, clcert_size);
_at.write_bytes(ctrl_z, 1); // Send Ctrl+Z
_at.resp_start("\r\nOK", true);
_at.resp_stop();
ret = _at.get_last_error();
// Set sec level to "Manage server and client authentication if requested by the remote server"
if (ret == NSAPI_ERROR_OK) {
_tls_sec_level = 2;
}
}
break;
case NSAPI_TLSSOCKET_SET_CLKEY: {
_at.cmd_start_stop("#SSLEN", "?");
_at.resp_start("#SSLEN:");
// Skip SSId
_at.skip_param();
ssl_enabled = _at.read_int();
_at.resp_stop();
if (ssl_enabled == 0) {
// SSL not enabled, so enable it
_at.at_cmd_discard("#SSLEN", "=", "%d%d", sslctxID, 1);
}
_at.flush();
const char *clkey = (const char *)optval;
int clkey_size = strlen(clkey);
_at.cmd_start_stop("#SSLSECDATA", "=", "%d%d%d%d", sslctxID,
1, // store data
2, // client key
clkey_size);
_at.resp_start("> ", true);
_at.write_bytes((uint8_t *)clkey, clkey_size);
_at.write_bytes(ctrl_z, 1); // Send Ctrl+Z
_at.resp_start("\r\nOK", true);
_at.resp_stop();
ret = _at.get_last_error();
// Set sec level to "Manage server and client authentication if requested by the remote server"
if (ret == NSAPI_ERROR_OK) {
_tls_sec_level = 2;
}
}
break;
default:
tr_error("Unsupported sockopt (%d)", optname);
ret = NSAPI_ERROR_UNSUPPORTED;
}
_at.unlock();
} else {
tr_error("No optval!");
ret = NSAPI_ERROR_PARAMETER;
}
} else {
tr_warning("Unsupported level (%d)", level);
ret = NSAPI_ERROR_UNSUPPORTED;
}
return ret;
}
#endif