Add support for the Telit ME310 module (LwIP/PPP and AT command-based IP stacks)

pull/13009/head
Chris Trowbridge 2020-05-22 15:30:43 -04:00
parent d0d35c2743
commit 125f169342
9 changed files with 1247 additions and 0 deletions

View File

@ -0,0 +1,193 @@
/*
* 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 "TELIT_ME310.h"
#include "TELIT_ME310_CellularContext.h"
#include "TELIT_ME310_CellularStack.h"
#include "TELIT_ME310_CellularNetwork.h"
#include "AT_CellularNetwork.h"
#include "PinNames.h"
#include "rtos/ThisThread.h"
using namespace std::chrono_literals;
using namespace mbed;
using namespace rtos;
using namespace events;
#if !defined(MBED_CONF_TELIT_ME310_PWR)
#define MBED_CONF_TELIT_ME310_PWR NC
#endif
#if !defined(MBED_CONF_TELIT_ME310_TX)
#define MBED_CONF_TELIT_ME310_TX NC
#endif
#if !defined(MBED_CONF_TELIT_ME310_RX)
#define MBED_CONF_TELIT_ME310_RX NC
#endif
#if !defined(MBED_CONF_TELIT_ME310_POLARITY)
#define MBED_CONF_TELIT_ME310_POLARITY 1 // active high
#endif
static const intptr_t cellular_properties[AT_CellularDevice::PROPERTY_MAX] = {
AT_CellularNetwork::RegistrationModeLAC, // C_EREG
AT_CellularNetwork::RegistrationModeLAC, // C_GREG
AT_CellularNetwork::RegistrationModeLAC, // C_REG
0, // AT_CGSN_WITH_TYPE
0, // AT_CGDATA
1, // AT_CGAUTH
1, // AT_CNMI
1, // AT_CSMP
1, // AT_CMGF
1, // AT_CSDH
1, // PROPERTY_IPV4_STACK
1, // PROPERTY_IPV6_STACK
1, // PROPERTY_IPV4V6_STACK
0, // PROPERTY_NON_IP_PDP_TYPE
1, // PROPERTY_AT_CGEREP,
1, // PROPERTY_AT_COPS_FALLBACK_AUTO
6, // PROPERTY_SOCKET_COUNT
1, // PROPERTY_IP_TCP
1, // PROPERTY_IP_UDP
20, // PROPERTY_AT_SEND_DELAY
};
TELIT_ME310::TELIT_ME310(FileHandle *fh, PinName pwr, bool active_high)
: AT_CellularDevice(fh),
_active_high(active_high),
_pwr_key(pwr, !_active_high)
{
set_cellular_properties(cellular_properties);
}
AT_CellularContext *TELIT_ME310::create_context_impl(ATHandler &at, const char *apn, bool cp_req, bool nonip_req)
{
return new TELIT_ME310_CellularContext(at, this, apn, cp_req, nonip_req);
}
nsapi_error_t TELIT_ME310::init()
{
nsapi_error_t err = AT_CellularDevice::init();
if (err != NSAPI_ERROR_OK) {
return err;
}
_at.lock();
#if defined (MBED_CONF_TELIT_ME310_RTS) && defined (MBED_CONF_TELIT_ME310_CTS)
_at.at_cmd_discard("&K3;&C1;&D0", "");
#else
_at.at_cmd_discard("&K0;&C1;&D0", "");
#endif
// AT#QSS=1
// Enable the Query SIM Status unsolicited indication in the ME. The format of the
// unsolicited indication is the following:
// #QSS: <status>
// The ME informs at
// every SIM status change through the basic unsolicited indication where <status> range is 0...1
// <status> values:
// - 0: SIM not inserted
// - 1: SIM inserted
_at.at_cmd_discard("#QSS", "=1");
// AT#PSNT=1
// Set command enables unsolicited result code for packet service network type (PSNT)
// having the following format:
// #PSNT:<nt>
// <nt> values:
// - 0: GPRS network
// - 4: LTE network
// - 5: unknown or not registered
_at.at_cmd_discard("#PSNT", "=1");
// AT+CMER=2
// Set command enables sending of unsolicited result codes from TA to TE in the case of
// indicator state changes.
// Current setting: buffer +CIEV Unsolicited Result Codes in the TA when TA-TE link is
// reserved (e.g. on-line data mode) and flush them to the TE after
// reservation; otherwise forward them directly to the TE
_at.at_cmd_discard("+CMER", "=2");
// AT+CMEE=2
// Set command disables the use of result code +CME ERROR: <err> as an indication of an
// error relating to the +Cxxx command issued. When enabled, device related errors cause the +CME
// ERROR: <err> final result code instead of the default ERROR final result code. ERROR is returned
// normally when the error message is related to syntax, invalid parameters or DTE functionality.
// Current setting: enable and use verbose <err> values
_at.at_cmd_discard("+CMEE", "=2");
// AT&W&P
// - AT&W: Execution command stores on profile <n> the complete configuration of the device. If
// parameter is omitted, the command has the same behavior of AT&W0.
// - AT&P: Execution command defines which full profile will be loaded at startup. If parameter
// is omitted, the command has the same behavior as AT&P0
_at.at_cmd_discard("&W&P", "");
return _at.unlock_return_error();
}
#if MBED_CONF_TELIT_ME310_PROVIDE_DEFAULT
#include "drivers/BufferedSerial.h"
CellularDevice *CellularDevice::get_default_instance()
{
static BufferedSerial serial(MBED_CONF_TELIT_ME310_TX, MBED_CONF_TELIT_ME310_RX, MBED_CONF_TELIT_ME310_BAUDRATE);
#if defined (MBED_CONF_TELIT_ME310_RTS) && defined (MBED_CONF_TELIT_ME310_CTS)
serial.set_flow_control(SerialBase::RTSCTS, MBED_CONF_TELIT_ME310_RTS, MBED_CONF_TELIT_ME310_CTS);
#endif
static TELIT_ME310 device(&serial,
MBED_CONF_TELIT_ME310_PWR,
MBED_CONF_TELIT_ME310_POLARITY);
return &device;
}
#endif
nsapi_error_t TELIT_ME310::hard_power_on()
{
soft_power_on();
return NSAPI_ERROR_OK;
}
nsapi_error_t TELIT_ME310::soft_power_on()
{
_pwr_key = _active_high;
ThisThread::sleep_for(500ms);
_pwr_key = !_active_high;
ThisThread::sleep_for(5s);
_pwr_key = _active_high;
ThisThread::sleep_for(5s);
return NSAPI_ERROR_OK;
}
nsapi_error_t TELIT_ME310::hard_power_off()
{
_pwr_key = !_active_high;
ThisThread::sleep_for(10s);
return NSAPI_ERROR_OK;
}
nsapi_error_t TELIT_ME310::soft_power_off()
{
return AT_CellularDevice::soft_power_off();
}
AT_CellularNetwork *TELIT_ME310::open_network_impl(ATHandler &at)
{
return new TELIT_ME310_CellularNetwork(at, *this);
}

View File

@ -0,0 +1,57 @@
/*
* 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.
*/
#ifndef CELLULAR_TARGETS_TELIT_ME310_TELIT_ME310_H_
#define CELLULAR_TARGETS_TELIT_ME310_TELIT_ME310_H_
#ifdef TARGET_FF_ARDUINO
#ifndef MBED_CONF_TELIT_ME310_TX
#define MBED_CONF_TELIT_ME310_TX D1
#endif
#ifndef MBED_CONF_TELIT_ME310_RX
#define MBED_CONF_TELIT_ME310_RX D0
#endif
#endif /* TARGET_FF_ARDUINO */
#include "DigitalOut.h"
#include "AT_CellularDevice.h"
namespace mbed {
class TELIT_ME310 : public AT_CellularDevice {
public:
/**
* Constructs the Telit ME310 series driver. It is mandatory to provide
* a FileHandle object, the power pin and the polarity of the pin.
*/
TELIT_ME310(FileHandle *fh, PinName pwr, bool active_high);
protected: // AT_CellularDevice
virtual AT_CellularContext *create_context_impl(ATHandler &at, const char *apn, bool cp_req = false, bool nonip_req = false);
virtual nsapi_error_t init();
virtual nsapi_error_t hard_power_on();
virtual nsapi_error_t hard_power_off();
virtual nsapi_error_t soft_power_on();
virtual nsapi_error_t soft_power_off();
virtual AT_CellularNetwork *open_network_impl(ATHandler &at);
private:
bool _active_high;
DigitalOut _pwr_key;
};
} // namespace mbed
#endif /* CELLULAR_TARGETS_TELIT_ME310_TELIT_ME310_H_ */

View File

@ -0,0 +1,107 @@
/*
* Copyright (c) 2018, 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 "TELIT_ME310_CellularContext.h"
#include "TELIT_ME310_CellularStack.h"
#include "CellularLog.h"
#include "Semaphore.h"
using namespace mbed_cellular_util;
namespace mbed {
TELIT_ME310_CellularContext::TELIT_ME310_CellularContext(ATHandler &at, CellularDevice *device, const char *apn, bool cp_req, bool nonip_req) :
AT_CellularContext(at, device, apn, cp_req, nonip_req)
{
}
TELIT_ME310_CellularContext::~TELIT_ME310_CellularContext()
{
}
#if !NSAPI_PPP_AVAILABLE
NetworkStack *TELIT_ME310_CellularContext::get_stack()
{
if (_pdp_type == NON_IP_PDP_TYPE || (_nonip_req && _pdp_type != DEFAULT_PDP_TYPE)) {
tr_error("Requesting stack for NON-IP context! Should request control plane netif: get_cp_netif()");
return NULL;
}
if (!_stack) {
_stack = new TELIT_ME310_CellularStack(_at, _cid, (nsapi_ip_stack_t)_pdp_type, *get_device());
}
return _stack;
}
#endif // #if !NSAPI_PPP_AVAILABLE
bool TELIT_ME310_CellularContext::get_context()
{
bool modem_supports_ipv6 = get_device()->get_property(AT_CellularDevice::PROPERTY_IPV6_PDP_TYPE);
bool modem_supports_ipv4 = get_device()->get_property(AT_CellularDevice::PROPERTY_IPV4_PDP_TYPE);
_at.cmd_start_stop("+CGDCONT", "?");
_at.resp_start("+CGDCONT:");
_cid = -1;
int cid_max = 0; // needed when creating new context
char apn[MAX_ACCESSPOINT_NAME_LENGTH];
int apn_len = 0;
while (_at.info_resp()) {
int cid = _at.read_int();
if (cid > cid_max) {
cid_max = cid;
}
char pdp_type_from_context[10];
int pdp_type_len = _at.read_string(pdp_type_from_context, sizeof(pdp_type_from_context));
if (pdp_type_len > 0) {
apn_len = _at.read_string(apn, sizeof(apn));
if (apn_len >= 0) {
if (_apn && apn_len > 0 && (strcmp(apn, _apn) != 0)) {
continue;
}
// APN matched -> Check PDP type
pdp_type_t pdp_type = string_to_pdp_type(pdp_type_from_context);
// Accept exact matching PDP context type or dual PDP context for IPv4/IPv6 only modems
if (get_device()->get_property(pdp_type_t_to_cellular_property(pdp_type)) ||
((pdp_type == IPV4V6_PDP_TYPE && (modem_supports_ipv4 || modem_supports_ipv6)) && !_nonip_req)) {
_pdp_type = pdp_type;
set_new_context(cid);
}
}
}
}
_at.resp_stop();
if (_cid == -1) { // no suitable context was found so create a new one
if (!set_new_context(1)) {
return false;
}
}
// save the apn
if (apn_len > 0 && !_apn) {
memcpy(_found_apn, apn, apn_len + 1);
}
tr_info("Found PDP context %d", _cid);
return true;
}
} /* namespace mbed */

View File

@ -0,0 +1,37 @@
/*
* 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.
*/
#ifndef TELIT_ME310_CELLULARCONTEXT_H_
#define TELIT_ME310_CELLULARCONTEXT_H_
#include "AT_CellularContext.h"
namespace mbed {
class TELIT_ME310_CellularContext: public AT_CellularContext {
public:
TELIT_ME310_CellularContext(ATHandler &at, CellularDevice *device, const char *apn, bool cp_req = false, bool nonip_req = false);
virtual ~TELIT_ME310_CellularContext();
protected:
#if !NSAPI_PPP_AVAILABLE
virtual NetworkStack *get_stack();
#endif // #if !NSAPI_PPP_AVAILABLE
virtual bool get_context();
};
} /* namespace mbed */
#endif // TELIT_ME310_CELLULARCONTEXT_H_

View File

@ -0,0 +1,45 @@
/*
* Copyright (c) 2019, 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 "TELIT_ME310_CellularNetwork.h"
using namespace mbed;
TELIT_ME310_CellularNetwork::TELIT_ME310_CellularNetwork(ATHandler &atHandler, AT_CellularDevice &device) : AT_CellularNetwork(atHandler, device)
{
}
TELIT_ME310_CellularNetwork::~TELIT_ME310_CellularNetwork()
{
}
nsapi_error_t TELIT_ME310_CellularNetwork::set_access_technology_impl(RadioAccessTechnology opsAct)
{
switch (opsAct) {
case RAT_GSM:
case RAT_CATM1:
case RAT_NB1:
_op_act = opsAct;
return NSAPI_ERROR_OK;
default:
_op_act = RAT_UNKNOWN;
return NSAPI_ERROR_UNSUPPORTED;
}
}

View File

@ -0,0 +1,37 @@
/*
* Copyright (c) 2019, 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.
*/
#ifndef TELIT_ME310_CELLULAR_NETWORK_H_
#define TELIT_ME310_CELLULAR_NETWORK_H_
#include "AT_CellularNetwork.h"
namespace mbed {
class TELIT_ME310_CellularNetwork : public AT_CellularNetwork {
public:
TELIT_ME310_CellularNetwork(ATHandler &atHandler, AT_CellularDevice &device);
virtual ~ TELIT_ME310_CellularNetwork();
protected:
virtual nsapi_error_t set_access_technology_impl(RadioAccessTechnology opRat);
};
} // namespace mbed
#endif // TELIT_ME310_CELLULAR_NETWORK_H_

View File

@ -0,0 +1,646 @@
/*
* 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

View File

@ -0,0 +1,87 @@
/*
* 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.
*/
#ifndef TELIT_ME310_CELLULARSTACK_H_
#define TELIT_ME310_CELLULARSTACK_H_
#include "AT_CellularStack.h"
#include "mbed_trace.h"
#include "drivers/Timer.h"
namespace mbed {
#define ME310_SOCKET_MAX 6
#define ME310_CONTEXT_MAX 6
#define ME310_CREATE_SOCKET_TIMEOUT 150000 //150 seconds
#define ME310_CLOSE_SOCKET_TIMEOUT 20000 // TCP socket max timeout is >10sec
#define ME310_MAX_RECV_SIZE 1000
#define ME310_MAX_SEND_SIZE 1023
#define ME310_SOCKET_BIND_FAIL 556
#define ME310_IPEASY_ACTIVATED_CONTEXT 1
#define ME310_IPEASY_DEACTIVATED_CONTEXT 0
#define ME310_SOCKET_TIMEOUT 1000
#define CTRL_Z "\x1a"
#define ESC "\x1b"
class TELIT_ME310_CellularStack : public AT_CellularStack {
public:
TELIT_ME310_CellularStack(ATHandler &atHandler, int cid, nsapi_ip_stack_t stack_type, AT_CellularDevice &device);
virtual ~TELIT_ME310_CellularStack();
protected: // NetworkStack
virtual nsapi_error_t socket_listen(nsapi_socket_t handle, int backlog);
virtual nsapi_error_t socket_accept(nsapi_socket_t server,
nsapi_socket_t *handle, SocketAddress *address = 0);
virtual nsapi_error_t socket_connect(nsapi_socket_t handle, const SocketAddress &address);
#if defined(MBED_CONF_NSAPI_OFFLOAD_TLSSOCKET) && (MBED_CONF_NSAPI_OFFLOAD_TLSSOCKET)
virtual nsapi_error_t setsockopt(nsapi_socket_t handle, int level,
int optname, const void *optval, unsigned optlen);
#endif
protected: // AT_CellularStack
virtual int get_max_socket_count();
virtual bool is_protocol_supported(nsapi_protocol_t protocol);
virtual nsapi_error_t socket_close_impl(int sock_id);
virtual nsapi_error_t create_socket_impl(CellularSocket *socket);
virtual nsapi_size_or_error_t socket_sendto_impl(CellularSocket *socket, const SocketAddress &address,
const void *data, nsapi_size_t size);
virtual nsapi_size_or_error_t socket_recvfrom_impl(CellularSocket *socket, SocketAddress *address,
void *buffer, nsapi_size_t size);
private:
// URC handler for socket data being received
void urc_sring();
bool is_ipeasy_context_activated(int context_id);
nsapi_error_t activate_ipeasy_context(int context_id);
nsapi_error_t deactivate_ipeasy_context(int context_id);
uint8_t _tls_sec_level;
};
} // namespace mbed
#endif /* TELIT_ME310_CELLULARSTACK_H_ */

View File

@ -0,0 +1,38 @@
{
"name": "TELIT_ME310",
"config": {
"tx": {
"help": "TX pin for serial connection. D1 assumed if Arduino Form Factor, needs to be set/overwritten otherwise.",
"value": null
},
"rx": {
"help": "RX pin for serial connection. D0 assumed if Arduino Form Factor, needs to be set/overwritten otherwise.",
"value": null
},
"rts": {
"help": "RTS pin for serial connection",
"value": null
},
"cts": {
"help": "CTS pin for serial connection",
"value": null
},
"pwr": {
"help": "Power control pin",
"value": null
},
"polarity": {
"help": "Pin polarity, 1 = Active high, 0 = Active low",
"value": null
},
"baudrate" : {
"help": "Serial connection baud rate",
"value": 115200
},
"provide-default": {
"help": "Provide as default CellularDevice [true/false]",
"value": false
}
}
}