Cellular: Non-IP socket and PDP context for EPS control plane data delivery

pull/9472/head
Mirela Chirica 2018-11-16 09:40:38 +02:00 committed by Ari Parkkila
parent 84e5013a2d
commit d301e13610
43 changed files with 1212 additions and 196 deletions

View File

@ -9,6 +9,7 @@ set(unittest-includes ${unittest-includes}
../features/cellular/framework/common
../features/cellular/framework/AT
../features/cellular/framework/device
../features/netsocket/cellular
)
# Source files

View File

@ -12,6 +12,7 @@ set(unittest-includes ${unittest-includes}
../features/frameworks/mbed-client-randlib/mbed-client-randlib
../drivers
../hal
../features/netsocket/cellular
)
# Source files

View File

@ -8,6 +8,7 @@ set(unittest-includes ${unittest-includes}
/features/cellular/framework/device/cellulardevice
../features/cellular/framework/device
../features/cellular/framework/common
../features/netsocket/cellular
)
# Source files

View File

@ -8,6 +8,7 @@ set(unittest-includes ${unittest-includes}
/features/cellular/framework/device/cellularstatemachine
../features/cellular/framework/device
../features/cellular/framework/common
../features/netsocket/cellular
)
# Source files

View File

@ -49,6 +49,8 @@ intptr_t AT_CellularBase::get_property(CellularProperty key)
return AT_CellularNetwork::RegistrationModeEnable;
} else if (key == PROPERTY_AT_CGAUTH) {
return true;
} else if (key == PROPERTY_IPV4_PDP_TYPE) {
return true;
}
return AT_CellularBase_stub::supported_bool;

View File

@ -19,12 +19,12 @@
using namespace mbed;
AT_CellularContext::AT_CellularContext(ATHandler &at, CellularDevice *device, const char *apn) :
AT_CellularBase(at), _ip_stack_type_requested(DEFAULT_STACK), _is_connected(false), _is_blocking(true),
_current_op(OP_INVALID), _device(device), _nw(0), _fh(0)
AT_CellularContext::AT_CellularContext(ATHandler &at, CellularDevice *device, const char *apn, bool cp_req, bool nonip_req) :
AT_CellularBase(at), _is_blocking(true), _is_connected(false),
_current_op(OP_INVALID), _device(device), _nw(0), _fh(0), _cp_req(cp_req), _nonip_req(nonip_req), _cp_in_use(false), _cp_netif(NULL)
{
_stack = NULL;
_ip_stack_type = DEFAULT_STACK;
_pdp_type = DEFAULT_PDP_TYPE;
_authentication_type = CellularContext::CHAP;
_connect_status = NSAPI_STATUS_DISCONNECTED;
_is_context_active = false;
@ -143,25 +143,23 @@ const char *AT_CellularContext::get_gateway()
return NULL;
}
AT_CellularBase::CellularProperty AT_CellularContext::nsapi_ip_stack_t_to_cellular_property(nsapi_ip_stack_t stack)
AT_CellularBase::CellularProperty AT_CellularContext::pdp_type_t_to_cellular_property(pdp_type_t pdp_type)
{
AT_CellularBase::CellularProperty prop = PROPERTY_IPV4_STACK;
if (stack == IPV6_STACK) {
prop = PROPERTY_IPV6_STACK;
} else if (stack == IPV4V6_STACK) {
prop = PROPERTY_IPV4V6_STACK;
AT_CellularBase::CellularProperty prop = PROPERTY_IPV4_PDP_TYPE;
if (pdp_type == IPV6_PDP_TYPE) {
prop = PROPERTY_IPV6_PDP_TYPE;
} else if (pdp_type == IPV4V6_PDP_TYPE) {
prop = PROPERTY_IPV4V6_PDP_TYPE;
} else if (pdp_type == NON_IP_PDP_TYPE) {
prop = PROPERTY_NON_IP_PDP_TYPE;
}
return prop;
}
nsapi_ip_stack_t AT_CellularContext::get_stack_type()
pdp_type_t AT_CellularContext::string_to_pdp_type(const char *pdp_type)
{
return IPV4V6_STACK;
}
nsapi_ip_stack_t AT_CellularContext::string_to_stack_type(const char *pdp_type)
{
return IPV4V6_STACK;
return IPV4V6_PDP_TYPE;
}
// PDP Context handling
@ -236,3 +234,30 @@ void AT_CellularContext::cellular_callback(nsapi_event_t ev, intptr_t ptr)
void AT_CellularContext::call_network_cb(nsapi_connection_status_t status)
{
}
ControlPlane_netif *AT_CellularContext::get_cp_netif()
{
return NULL;
}
nsapi_error_t AT_CellularContext::activate_non_ip_context()
{
return NSAPI_ERROR_OK;
}
nsapi_error_t AT_CellularContext::setup_control_plane_opt()
{
return NSAPI_ERROR_OK;
}
void AT_CellularContext::deactivate_ip_context()
{
}
void AT_CellularContext::deactivate_non_ip_context()
{
}
void AT_CellularContext::set_disconnect()
{
}

View File

@ -65,7 +65,7 @@ nsapi_error_t AT_CellularDevice::release_at_handler(ATHandler *at_handler)
}
CellularContext *AT_CellularDevice::create_context(UARTSerial *serial, const char *const apn, PinName dcd_pin,
bool active_high)
bool active_high, bool cp_req, bool nonip_req)
{
}
@ -118,12 +118,12 @@ CellularContext *AT_CellularDevice::get_context_list() const
return NULL;
}
CellularContext *AT_CellularDevice::create_context(FileHandle *fh, const char *apn)
CellularContext *AT_CellularDevice::create_context(FileHandle *fh, const char *apn, bool cp_req, bool nonip_req)
{
return NULL;
}
AT_CellularContext *AT_CellularDevice::create_context_impl(ATHandler &at, const char *apn)
AT_CellularContext *AT_CellularDevice::create_context_impl(ATHandler &at, const char *apn, bool cp_req, bool nonip_req)
{
return NULL;
}

View File

@ -103,3 +103,6 @@ nsapi_error_t CellularDevice::shutdown()
return NSAPI_ERROR_OK;
}
void CellularDevice::cellular_callback(nsapi_event_t ev, intptr_t ptr)
{
}

View File

@ -53,12 +53,12 @@ public:
}
virtual CellularContext *create_context(UARTSerial *serial, const char *const apn, PinName dcd_pin,
bool active_high)
bool active_high, bool cp_req = false, bool nonip_req = false)
{
return NULL;
}
virtual CellularContext *create_context(FileHandle *fh = NULL, const char *apn = NULL)
virtual CellularContext *create_context(FileHandle *fh = NULL, const char *apn = NULL, bool cp_req = false, bool nonip_req = false)
{
EventQueue que;
FileHandle_stub fh1;

View File

@ -19,9 +19,18 @@
#include "CellularBase.h"
#include "CellularDevice.h"
#include "ControlPlane_netif.h"
namespace mbed {
typedef enum pdp_type {
DEFAULT_PDP_TYPE = DEFAULT_STACK,
IPV4_PDP_TYPE = IPV4_STACK,
IPV6_PDP_TYPE = IPV6_STACK,
IPV4V6_PDP_TYPE = IPV4V6_STACK,
NON_IP_PDP_TYPE
} pdp_type_t;
/**
* @addtogroup cellular
* @{
@ -31,6 +40,7 @@ namespace mbed {
class CellularContext : public CellularBase {
public:
// max simultaneous PDP contexts active
static const int PDP_CONTEXT_COUNT = 4;
@ -235,6 +245,10 @@ public: // from NetworkInterface
*/
virtual void set_file_handle(UARTSerial *serial, PinName dcd_pin = NC, bool active_high = false) = 0;
/** Returns the control plane AT command interface
*/
virtual ControlPlane_netif *get_cp_netif() = 0;
protected: // Device specific implementations might need these so protected
enum ContextOperation {
OP_INVALID = -1,
@ -264,7 +278,7 @@ protected: // Device specific implementations might need these so protected
// member variables needed in target override methods
NetworkStack *_stack; // must be pointer because of PPP
nsapi_ip_stack_t _ip_stack_type;
pdp_type_t _pdp_type;
CellularContext::AuthenticationType _authentication_type;
nsapi_connection_status_t _connect_status;
cell_callback_data_t _cb_data;

View File

@ -97,11 +97,13 @@ public:
* @param fh file handle used in communication to modem. This can be, for example, UART handle. If null, then the default
* file handle is used.
* @param apn access point to use with context, can be null.
* @param cp_req flag indicating if EPS control plane optimisation is required
* @param nonip_req flag indicating if this context is required to be Non-IP
*
* @return new instance of class CellularContext or NULL in case of failure
*
*/
virtual CellularContext *create_context(FileHandle *fh = NULL, const char *apn = NULL) = 0;
virtual CellularContext *create_context(FileHandle *fh = NULL, const char *apn = NULL, bool cp_req = false, bool nonip_req = false) = 0;
/** Creates a new CellularContext interface. This API should be used if serial is UART and PPP mode used.
* CellularContext created will use data carrier detect to be able to detect disconnection much faster in PPP mode.
@ -111,12 +113,14 @@ public:
* @param apn access point to use with context, can be null.
* @param dcd_pin Pin used to set data carrier detect on/off for the given UART
* @param active_high a boolean set to true if DCD polarity is active low
* @param cp_req Flag indicating if EPS control plane optimisation is required
* @param nonip_req Flag indicating if this context is required to be Non-IP
*
* @return new instance of class CellularContext or NULL in case of failure
*
*/
virtual CellularContext *create_context(UARTSerial *serial, const char *apn, PinName dcd_pin = NC,
bool active_high = false) = 0;
bool active_high = false, bool cp_req = false, bool nonip_req = false) = 0;
/** Deletes the given CellularContext instance
*

View File

@ -49,9 +49,10 @@ public:
PROPERTY_AT_CGSN_WITH_TYPE, // 0 = not supported, 1 = supported. AT+CGSN without type is likely always supported similar to AT+GSN.
PROPERTY_AT_CGDATA, // 0 = not supported, 1 = supported. Alternative is to support only ATD*99***<cid>#
PROPERTY_AT_CGAUTH, // 0 = not supported, 1 = supported. APN authentication AT commands supported
PROPERTY_IPV4_STACK, // 0 = not supported, 1 = supported. Does modem support IPV4?
PROPERTY_IPV6_STACK, // 0 = not supported, 1 = supported. Does modem support IPV6?
PROPERTY_IPV4V6_STACK, // 0 = not supported, 1 = supported. Does modem support dual stack IPV4V6?
PROPERTY_IPV4_PDP_TYPE, // 0 = not supported, 1 = supported. Does modem support IPV4?
PROPERTY_IPV6_PDP_TYPE, // 0 = not supported, 1 = supported. Does modem support IPV6?
PROPERTY_IPV4V6_PDP_TYPE, // 0 = not supported, 1 = supported. Does modem support dual stack IPV4V6?
PROPERTY_NON_IP_PDP_TYPE, // 0 = not supported, 1 = supported. Does modem support Non-IP?
PROPERTY_MAX
};

View File

@ -25,6 +25,9 @@
#define NETWORK_TIMEOUT 30 * 60 * 1000 // 30 minutes
#define DEVICE_TIMEOUT 5 * 60 * 1000 // 5 minutes
// Timeout to wait for URC indicating ciot optimization support from network
#define CP_OPT_NW_REPLY_TIMEOUT 3000 // 3 seconds
#if NSAPI_PPP_AVAILABLE
#define AT_SYNC_TIMEOUT 1000 // 1 second timeout
@ -41,13 +44,13 @@
using namespace mbed_cellular_util;
using namespace mbed;
AT_CellularContext::AT_CellularContext(ATHandler &at, CellularDevice *device, const char *apn) :
AT_CellularBase(at), _ip_stack_type_requested(DEFAULT_STACK), _is_connected(false), _is_blocking(true),
_current_op(OP_INVALID), _device(device), _nw(0), _fh(0)
AT_CellularContext::AT_CellularContext(ATHandler &at, CellularDevice *device, const char *apn, bool cp_req, bool nonip_req) :
AT_CellularBase(at), _is_connected(false), _is_blocking(true),
_current_op(OP_INVALID), _device(device), _nw(0), _fh(0), _cp_req(cp_req), _nonip_req(nonip_req), _cp_in_use(false), _cp_netif(NULL)
{
tr_info("New CellularContext %s (%p)", apn ? apn : "", this);
_stack = NULL;
_ip_stack_type = DEFAULT_STACK;
_pdp_type = DEFAULT_PDP_TYPE;
_authentication_type = CellularContext::CHAP;
_connect_status = NSAPI_STATUS_DISCONNECTED;
_is_context_active = false;
@ -258,24 +261,21 @@ void AT_CellularContext::set_credentials(const char *apn, const char *uname, con
_pwd = pwd;
}
nsapi_ip_stack_t AT_CellularContext::get_stack_type()
pdp_type_t AT_CellularContext::string_to_pdp_type(const char *pdp_type_str)
{
return _ip_stack_type;
}
pdp_type_t pdp_type = DEFAULT_PDP_TYPE;
int len = strlen(pdp_type_str);
nsapi_ip_stack_t AT_CellularContext::string_to_stack_type(const char *pdp_type)
{
nsapi_ip_stack_t stack = DEFAULT_STACK;
int len = strlen(pdp_type);
if (len == 6 && memcmp(pdp_type, "IPV4V6", len) == 0) {
stack = IPV4V6_STACK;
} else if (len == 4 && memcmp(pdp_type, "IPV6", len) == 0) {
stack = IPV6_STACK;
} else if (len == 2 && memcmp(pdp_type, "IP", len) == 0) {
stack = IPV4_STACK;
if (len == 6 && memcmp(pdp_type_str, "IPV4V6", len) == 0) {
pdp_type = IPV4V6_PDP_TYPE;
} else if (len == 4 && memcmp(pdp_type_str, "IPV6", len) == 0) {
pdp_type = IPV6_PDP_TYPE;
} else if (len == 2 && memcmp(pdp_type_str, "IP", len) == 0) {
pdp_type = IPV4_PDP_TYPE;
} else if (len == 6 && memcmp(pdp_type_str, "Non-IP", len) == 0) {
pdp_type = NON_IP_PDP_TYPE;
}
return stack;
return pdp_type;
}
// PDP Context handling
@ -316,19 +316,24 @@ nsapi_error_t AT_CellularContext::do_user_authentication()
return NSAPI_ERROR_OK;
}
AT_CellularBase::CellularProperty AT_CellularContext::nsapi_ip_stack_t_to_cellular_property(nsapi_ip_stack_t stack)
AT_CellularBase::CellularProperty AT_CellularContext::pdp_type_t_to_cellular_property(pdp_type_t pdp_type)
{
AT_CellularBase::CellularProperty prop = PROPERTY_IPV4_STACK;
if (stack == IPV6_STACK) {
prop = PROPERTY_IPV6_STACK;
} else if (stack == IPV4V6_STACK) {
prop = PROPERTY_IPV4V6_STACK;
AT_CellularBase::CellularProperty prop = PROPERTY_IPV4_PDP_TYPE;
if (pdp_type == IPV6_PDP_TYPE) {
prop = PROPERTY_IPV6_PDP_TYPE;
} else if (pdp_type == IPV4V6_PDP_TYPE) {
prop = PROPERTY_IPV4V6_PDP_TYPE;
} else if (pdp_type == NON_IP_PDP_TYPE) {
prop = PROPERTY_NON_IP_PDP_TYPE;
}
return prop;
}
bool AT_CellularContext::get_context()
{
bool modem_supports_ipv6 = get_property(PROPERTY_IPV6_PDP_TYPE);
bool modem_supports_ipv4 = get_property(PROPERTY_IPV4_PDP_TYPE);
_at.cmd_start("AT+CGDCONT?");
_at.cmd_stop();
_at.resp_start("+CGDCONT:");
@ -337,9 +342,6 @@ bool AT_CellularContext::get_context()
char apn[MAX_ACCESSPOINT_NAME_LENGTH];
int apn_len = 0;
bool modem_supports_ipv6 = get_property(PROPERTY_IPV6_STACK);
bool modem_supports_ipv4 = get_property(PROPERTY_IPV4_STACK);
while (_at.info_resp()) {
int cid = _at.read_int();
if (cid > cid_max) {
@ -353,52 +355,20 @@ bool AT_CellularContext::get_context()
if (_apn && (strcmp(apn, _apn) != 0)) {
continue;
}
nsapi_ip_stack_t pdp_stack = string_to_stack_type(pdp_type_from_context);
// Accept dual PDP context for IPv4/IPv6 only modems
if (pdp_stack != DEFAULT_STACK && (get_property(nsapi_ip_stack_t_to_cellular_property(pdp_stack))
|| pdp_stack == IPV4V6_STACK)) {
if (_ip_stack_type_requested == IPV4_STACK) {
if (pdp_stack == IPV4_STACK || pdp_stack == IPV4V6_STACK) {
_ip_stack_type = _ip_stack_type_requested;
_cid = cid;
break;
}
} else if (_ip_stack_type_requested == IPV6_STACK) {
if (pdp_stack == IPV6_STACK || pdp_stack == IPV4V6_STACK) {
_ip_stack_type = _ip_stack_type_requested;
_cid = cid;
break;
}
} else {
// requested dual stack or stack is not specified
// If dual PDP need to check for IPV4 or IPV6 modem support. Prefer IPv6.
if (pdp_stack == IPV4V6_STACK) {
if (modem_supports_ipv6) {
_ip_stack_type = IPV6_STACK;
_cid = cid;
break;
} else if (modem_supports_ipv4) {
_ip_stack_type = IPV4_STACK;
_cid = cid;
break;
}
// If PDP is IPV4 or IPV6 they are already checked if supported
} else {
_ip_stack_type = pdp_stack;
_cid = cid;
if (pdp_stack == IPV6_STACK) {
break;
}
if (pdp_stack == IPV4_STACK && !modem_supports_ipv6) {
break;
}
// 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_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;
_cid = cid;
}
}
}
}
}
}
}
}
_at.resp_stop();
if (_cid == -1) { // no suitable context was found so create a new one
if (!set_new_context(cid_max + 1)) {
@ -418,75 +388,77 @@ bool AT_CellularContext::get_context()
bool AT_CellularContext::set_new_context(int cid)
{
nsapi_ip_stack_t tmp_stack = _ip_stack_type_requested;
bool modem_supports_ipv6 = get_property(PROPERTY_IPV6_PDP_TYPE);
bool modem_supports_ipv4 = get_property(PROPERTY_IPV4_PDP_TYPE);
bool modem_supports_nonip = get_property(PROPERTY_NON_IP_PDP_TYPE);
if (tmp_stack == DEFAULT_STACK) {
bool modem_supports_ipv6 = get_property(PROPERTY_IPV6_STACK);
bool modem_supports_ipv4 = get_property(PROPERTY_IPV4_STACK);
char pdp_type_str[8 + 1] = {0};
pdp_type_t pdp_type = IPV4_PDP_TYPE;
if (modem_supports_ipv6 && modem_supports_ipv4) {
tmp_stack = IPV4V6_STACK;
if (_nonip_req && _cp_in_use && modem_supports_nonip) {
strncpy(pdp_type_str, "Non-IP", sizeof(pdp_type_str));
pdp_type = NON_IP_PDP_TYPE;
} else if (modem_supports_ipv6 && modem_supports_ipv4) {
strncpy(pdp_type_str, "IPV4V6", sizeof(pdp_type_str));
pdp_type = IPV4V6_PDP_TYPE;
} else if (modem_supports_ipv6) {
tmp_stack = IPV6_STACK;
strncpy(pdp_type_str, "IPV6", sizeof(pdp_type_str));
pdp_type = IPV6_PDP_TYPE;
} else if (modem_supports_ipv4) {
tmp_stack = IPV4_STACK;
strncpy(pdp_type_str, "IP", sizeof(pdp_type));
pdp_type = IPV4_PDP_TYPE;
} else {
return false;
}
}
char pdp_type[8 + 1] = {0};
switch (tmp_stack) {
case IPV4_STACK:
strncpy(pdp_type, "IP", sizeof(pdp_type));
break;
case IPV6_STACK:
strncpy(pdp_type, "IPV6", sizeof(pdp_type));
break;
case IPV4V6_STACK:
strncpy(pdp_type, "IPV6", sizeof(pdp_type)); // try first IPV6 and then fall-back to IPv4
break;
default:
break;
}
//apn: "If the value is null or omitted, then the subscription value will be requested."
bool success = false;
_at.cmd_start("AT+CGDCONT=");
_at.write_int(cid);
_at.write_string(pdp_type);
_at.write_string(pdp_type_str);
_at.write_string(_apn);
_at.cmd_stop_read_resp();
success = (_at.get_last_error() == NSAPI_ERROR_OK);
// Fall back to ipv4
if (!success && tmp_stack == IPV4V6_STACK) {
_at.clear_error();
tmp_stack = IPV4_STACK;
_at.cmd_start("AT+FCLASS=0;+CGDCONT=");
_at.write_int(cid);
_at.write_string("IP");
_at.write_string(_apn);
_at.cmd_stop_read_resp();
success = (_at.get_last_error() == NSAPI_ERROR_OK);
}
if (success) {
_ip_stack_type = tmp_stack;
_pdp_type = pdp_type;
_cid = cid;
_new_context_set = true;
tr_info("New PDP context %d, stack %s", _cid, pdp_type);
tr_info("New PDP context %d, type %s", _cid, pdp_type);
}
return success;
}
nsapi_error_t AT_CellularContext::do_activate_context()
{
if (_nonip_req && _cp_in_use) {
return activate_non_ip_context();
}
// In IP case but also when Non-IP is requested and
// control plane optimisation is not established -> activate ip context
_nonip_req = false;
return activate_ip_context();
}
nsapi_error_t AT_CellularContext::activate_ip_context()
{
return activate_context();
}
nsapi_error_t AT_CellularContext::activate_non_ip_context()
{
return activate_context();
}
nsapi_error_t AT_CellularContext::activate_context()
{
_at.lock();
nsapi_error_t err = NSAPI_ERROR_OK;
// try to find or create context with suitable stack
// try to find or create context of suitable type
if (get_context()) {
#if NSAPI_PPP_AVAILABLE
_at.unlock();
@ -593,6 +565,12 @@ void AT_CellularContext::do_connect()
#if NSAPI_PPP_AVAILABLE
nsapi_error_t AT_CellularContext::open_data_channel()
{
// If Non-IP in use fail
if (_pdp_type == NON_IP_PDP_TYPE) {
tr_error("Attempt of PPP connect over NON-IP: failed to CONNECT");
return NSAPI_ERROR_PARAMETER;
}
tr_info("CellularContext PPP connect");
if (get_property(PROPERTY_AT_CGDATA)) {
_at.cmd_start("AT+CGDATA=\"PPP\",");
@ -618,7 +596,7 @@ nsapi_error_t AT_CellularContext::open_data_channel()
/* Initialize PPP
* If blocking: mbed_ppp_init() is a blocking call, it will block until
connected, or timeout after 30 seconds*/
nsapi_error_t err = nsapi_ppp_connect(_at.get_file_handle(), callback(this, &AT_CellularContext::ppp_status_cb), _uname, _pwd, _ip_stack_type);
nsapi_error_t err = nsapi_ppp_connect(_at.get_file_handle(), callback(this, &AT_CellularContext::ppp_status_cb), _uname, _pwd, (nsapi_ip_stack_t)_pdp_type);
if (err) {
ppp_disconnected();
}
@ -677,6 +655,31 @@ nsapi_error_t AT_CellularContext::disconnect()
// deactivate a context only if we have activated
if (_is_context_activated) {
if (_nonip_req && _cp_in_use) {
deactivate_non_ip_context();
} else {
deactivate_ip_context();
}
}
_is_connected = false;
call_network_cb(NSAPI_STATUS_DISCONNECTED);
return _at.unlock_return_error();
}
void AT_CellularContext::deactivate_ip_context()
{
deactivate_context();
}
void AT_CellularContext::deactivate_non_ip_context()
{
deactivate_context();
}
void AT_CellularContext::deactivate_context()
{
// CGACT and CGATT commands might take up to 3 minutes to respond.
_at.set_at_timeout(180 * 1000);
_is_context_active = false;
@ -716,16 +719,11 @@ nsapi_error_t AT_CellularContext::disconnect()
_at.write_int(_cid);
_at.cmd_stop_read_resp();
}
_at.clear_error();
_at.cmd_start("AT+CGATT=0");
_at.cmd_stop_read_resp();
_at.restore_at_timeout();
}
_is_connected = false;
call_network_cb(NSAPI_STATUS_DISCONNECTED);
return _at.unlock_return_error();
}
nsapi_error_t AT_CellularContext::get_apn_backoff_timer(int &backoff_timer)
@ -900,6 +898,16 @@ void AT_CellularContext::cellular_callback(nsapi_event_t ev, intptr_t ptr)
if (!_nw && st == CellularDeviceReady && data->error == NSAPI_ERROR_OK) {
_nw = _device->open_network(_fh);
tr_error("OPEN NETWORK");
}
if (_cp_req && !_cp_in_use && (data->error == NSAPI_ERROR_OK) &&
(st == CellularSIMStatusChanged && data->status_data == CellularDevice::SimStateReady)) {
if (setup_control_plane_opt() != NSAPI_ERROR_OK) {
tr_error("Control plane SETUP failed!");
} else {
tr_info("Control plane SETUP success!");
}
}
if (_is_blocking) {
@ -969,3 +977,61 @@ void AT_CellularContext::call_network_cb(nsapi_connection_status_t status)
}
}
}
ControlPlane_netif *AT_CellularContext::get_cp_netif()
{
tr_error("No control plane interface available from base context!");
return NULL;
}
nsapi_error_t AT_CellularContext::setup_control_plane_opt()
{
// check if control plane optimization already set
mbed::CellularNetwork::CIoT_Supported_Opt supported_network_opt;
if (_nw->get_ciot_network_optimization_config(supported_network_opt)) {
return NSAPI_ERROR_DEVICE_ERROR;
}
if (supported_network_opt == mbed::CellularNetwork::CIOT_OPT_CONTROL_PLANE ||
supported_network_opt == mbed::CellularNetwork::CIOT_OPT_BOTH) {
_cp_in_use = true;
return NSAPI_ERROR_OK;
}
// ciot optimization not set by app so need to set it now
nsapi_error_t ciot_opt_ret;
ciot_opt_ret = _nw->set_ciot_optimization_config(mbed::CellularNetwork::CIOT_OPT_CONTROL_PLANE,
mbed::CellularNetwork::PREFERRED_UE_OPT_CONTROL_PLANE,
callback(this, &AT_CellularContext::ciot_opt_cb));
if (ciot_opt_ret != NSAPI_ERROR_OK) {
return ciot_opt_ret;
}
//wait for control plane opt call back to release semaphore
_cp_opt_semaphore.wait(CP_OPT_NW_REPLY_TIMEOUT);
if (_cp_in_use) {
return NSAPI_ERROR_OK;
}
return NSAPI_ERROR_DEVICE_ERROR;
}
void AT_CellularContext::ciot_opt_cb(mbed::CellularNetwork::CIoT_Supported_Opt ciot_opt)
{
if (ciot_opt == mbed::CellularNetwork::CIOT_OPT_CONTROL_PLANE ||
ciot_opt == mbed::CellularNetwork::CIOT_OPT_BOTH) {
_cp_in_use = true;
}
_cp_opt_semaphore.release();
}
void AT_CellularContext::set_disconnect()
{
_is_connected = false;
cell_callback_data_t data;
data.error = NSAPI_STATUS_DISCONNECTED;
_device->cellular_callback(NSAPI_EVENT_CONNECTION_STATUS_CHANGE, (intptr_t)&data);
}

View File

@ -27,7 +27,7 @@ namespace mbed {
class AT_CellularContext : public CellularContext, public AT_CellularBase {
public:
AT_CellularContext(ATHandler &at, CellularDevice *device, const char *apn = 0);
AT_CellularContext(ATHandler &at, CellularDevice *device, const char *apn = 0, bool cp_req = false, bool nonip_req = false);
virtual ~AT_CellularContext();
// from CellularBase/NetworkInterface
@ -60,6 +60,8 @@ public:
virtual void set_file_handle(UARTSerial *serial, PinName dcd_pin = NC, bool active_high = false);
virtual void enable_hup(bool enable);
virtual ControlPlane_netif *get_cp_netif();
protected:
virtual void cellular_callback(nsapi_event_t ev, intptr_t ptr);
@ -91,6 +93,11 @@ protected:
*/
void call_network_cb(nsapi_connection_status_t status);
virtual nsapi_error_t activate_non_ip_context();
virtual nsapi_error_t setup_control_plane_opt();
virtual void deactivate_non_ip_context();
virtual void set_disconnect();
private:
#if NSAPI_PPP_AVAILABLE
nsapi_error_t open_data_channel();
@ -98,16 +105,19 @@ private:
void ppp_disconnected();
#endif // #if NSAPI_PPP_AVAILABLE
nsapi_error_t do_activate_context();
nsapi_error_t activate_context();
nsapi_error_t activate_ip_context();
void deactivate_context();
void deactivate_ip_context();
bool set_new_context(int cid);
bool get_context();
nsapi_error_t delete_current_context();
nsapi_ip_stack_t string_to_stack_type(const char *pdp_type);
nsapi_ip_stack_t get_stack_type();
pdp_type_t string_to_pdp_type(const char *pdp_type);
nsapi_error_t check_operation(nsapi_error_t err, ContextOperation op);
AT_CellularBase::CellularProperty nsapi_ip_stack_t_to_cellular_property(nsapi_ip_stack_t stack);
AT_CellularBase::CellularProperty pdp_type_t_to_cellular_property(pdp_type_t pdp_type);
void ciot_opt_cb(mbed::CellularNetwork::CIoT_Supported_Opt ciot_opt);
private:
nsapi_ip_stack_t _ip_stack_type_requested;
bool _is_connected;
bool _is_blocking;
ContextOperation _current_op;
@ -116,6 +126,18 @@ private:
CellularNetwork *_nw;
FileHandle *_fh;
rtos::Semaphore _semaphore;
rtos::Semaphore _cp_opt_semaphore;
protected:
// flag indicating if CP was requested to be setup
bool _cp_req;
// flag indicating if Non-IP context was requested to be setup
bool _nonip_req;
// tells if CCIOTOPTI received green from network for CP optimisation use
bool _cp_in_use;
ControlPlane_netif *_cp_netif;
};
} // namespace mbed

View File

@ -168,19 +168,19 @@ CellularContext *AT_CellularDevice::get_context_list() const
}
CellularContext *AT_CellularDevice::create_context(UARTSerial *serial, const char *const apn, PinName dcd_pin,
bool active_high)
bool active_high, bool cp_req, bool nonip_req)
{
// Call FileHandle base version - explict upcast to avoid recursing into ourselves
CellularContext *ctx = create_context(static_cast<FileHandle *>(serial), apn);
CellularContext *ctx = create_context(static_cast<FileHandle *>(serial), apn, cp_req, nonip_req);
if (serial) {
ctx->set_file_handle(serial, dcd_pin, active_high);
}
return ctx;
}
CellularContext *AT_CellularDevice::create_context(FileHandle *fh, const char *apn)
CellularContext *AT_CellularDevice::create_context(FileHandle *fh, const char *apn, bool cp_req, bool nonip_req)
{
AT_CellularContext *ctx = create_context_impl(*get_at_handler(fh), apn);
AT_CellularContext *ctx = create_context_impl(*get_at_handler(fh), apn, cp_req, nonip_req);
AT_CellularContext *curr = _context_list;
if (_context_list == NULL) {
@ -198,9 +198,12 @@ CellularContext *AT_CellularDevice::create_context(FileHandle *fh, const char *a
return ctx;
}
AT_CellularContext *AT_CellularDevice::create_context_impl(ATHandler &at, const char *apn)
AT_CellularContext *AT_CellularDevice::create_context_impl(ATHandler &at, const char *apn, bool cp_req, bool nonip_req)
{
return new AT_CellularContext(at, this, apn);
if (cp_req) {
}
return new AT_CellularContext(at, this, apn, cp_req, nonip_req);
}
void AT_CellularDevice::delete_context(CellularContext *context)

View File

@ -44,9 +44,9 @@ public:
virtual nsapi_error_t get_sim_state(SimState &state);
virtual CellularContext *create_context(FileHandle *fh = NULL, const char *apn = NULL);
virtual CellularContext *create_context(FileHandle *fh = NULL, const char *apn = NULL, bool cp_req = false, bool nonip_req = false);
virtual CellularContext *create_context(UARTSerial *serial, const char *const apn, PinName dcd_pin = NC, bool active_high = false);
virtual CellularContext *create_context(UARTSerial *serial, const char *const apn, PinName dcd_pin = NC, bool active_high = false, bool cp_req = false, bool nonip_req = false);
virtual void delete_context(CellularContext *context);
@ -103,7 +103,7 @@ public:
* @return new instance of class AT_CellularContext
*
*/
virtual AT_CellularContext *create_context_impl(ATHandler &at, const char *apn);
virtual AT_CellularContext *create_context_impl(ATHandler &at, const char *apn, bool cp_req = false, bool nonip_req = false);
/** Create new instance of AT_CellularNetwork or if overridden, modem specific implementation.
*

View File

@ -0,0 +1,77 @@
/*AT_ControlPlane_netif.cpp*/
#include "AT_ControlPlane_netif.h"
namespace mbed {
AT_ControlPlane_netif::AT_ControlPlane_netif(ATHandler &at, int cid) : AT_CellularBase(at),
_cid(cid), _cb(NULL), _data(NULL), _recv_len(0)
{
_at.set_urc_handler("+CRTDCP:", mbed::Callback<void()>(this, &AT_ControlPlane_netif::urc_cp_recv));
}
AT_ControlPlane_netif::~AT_ControlPlane_netif()
{}
void AT_ControlPlane_netif::urc_cp_recv()
{
//+CRTDCP: <cid>,<cpdata_length>,<cpdata>
_at.lock();
int cid = _at.read_int();
int cpdata_length = _at.read_int();
int read_len = _at.read_string(_recv_buffer, sizeof(_recv_buffer));
_at.unlock();
// cid not expected to be different because: one context - one file handle
// so this file handle cannot get urc from different context
if (read_len > 0 && read_len == cpdata_length && cid == _cid) {
_recv_len = read_len;
data_received();
}
}
nsapi_size_or_error_t AT_ControlPlane_netif::send(const void *cpdata, nsapi_size_t cpdata_length)
{
//CSODCP
_at.lock();
_at.cmd_start("AT+CSODCP=");
_at.write_int(_cid);
_at.write_int(cpdata_length);
_at.write_bytes((uint8_t *)cpdata, cpdata_length);
return _at.unlock_return_error();
}
nsapi_size_or_error_t AT_ControlPlane_netif::recv(void *cpdata, nsapi_size_t cpdata_length)
{
// If no data received through CRTDCP URC
if (!_recv_len) {
return NSAPI_ERROR_WOULD_BLOCK;
}
// If too small buffer for data
if (_recv_len > cpdata_length) {
return NSAPI_ERROR_DEVICE_ERROR;
}
memcpy(cpdata, _recv_buffer, _recv_len);
return _recv_len = 0;
}
void AT_ControlPlane_netif::attach(void (*callback)(void *), void *data)
{
_cb = callback;
_data = data;
}
void AT_ControlPlane_netif::data_received()
{
// call socket event
if (!_cb) {
return;
}
_cb(_data);
}
} //mbed namespace

View File

@ -0,0 +1,32 @@
#include "ControlPlane_netif.h"
#include "ATHandler.h"
#include "AT_CellularBase.h"
namespace mbed {
class AT_ControlPlane_netif: public ControlPlane_netif, public AT_CellularBase {
public:
AT_ControlPlane_netif(ATHandler &at, int cid);
virtual ~AT_ControlPlane_netif();
// ControlPlane_netif
// +CSODCP: 3GPP 27007 10.1.43
virtual nsapi_size_or_error_t send(const void *cpdata, nsapi_size_t cpdata_length);
// +CRTDCP: 3GPP 27007 10.1.44
virtual nsapi_size_or_error_t recv(void *cpdata, nsapi_size_t cpdata_length);
virtual void attach(void (*callback)(void *), void *data);
virtual void data_received();
protected:
// Id of the PDP context that enables the control plane data connection
int _cid;
private:
void (*_cb)(void *);
void *_data;
char _recv_buffer[MAX_CP_DATA_RECV_LEN];
size_t _recv_len;
void urc_cp_recv();
};
} //mbed namespace

View File

@ -36,9 +36,9 @@ GEMALTO_CINTERION::~GEMALTO_CINTERION()
{
}
AT_CellularContext *GEMALTO_CINTERION::create_context_impl(ATHandler &at, const char *apn)
AT_CellularContext *GEMALTO_CINTERION::create_context_impl(ATHandler &at, const char *apn, bool cp_req, bool nonip_req)
{
return new GEMALTO_CINTERION_CellularContext(at, this, apn);
return new GEMALTO_CINTERION_CellularContext(at, this, apn, cp_req, nonip_req);
}
nsapi_error_t GEMALTO_CINTERION::init()

View File

@ -40,7 +40,7 @@ public:
static Module get_module();
protected: // AT_CellularDevice
virtual AT_CellularContext *create_context_impl(ATHandler &at, const char *apn);
virtual AT_CellularContext *create_context_impl(ATHandler &at, const char *apn, bool cp_req = false, bool nonip_req = false);
protected:
virtual uint16_t get_send_delay() const;
virtual nsapi_error_t init();

View File

@ -16,11 +16,12 @@
*/
#include "GEMALTO_CINTERION_CellularContext.h"
#include "GEMALTO_CINTERION_CellularStack.h"
#include "CellularLog.h"
namespace mbed {
GEMALTO_CINTERION_CellularContext::GEMALTO_CINTERION_CellularContext(ATHandler &at, CellularDevice *device,
const char *apn) : AT_CellularContext(at, device, apn)
const char *apn, bool cp_req, bool nonip_req) : AT_CellularContext(at, device, apn, cp_req, nonip_req)
{
}
@ -31,8 +32,13 @@ GEMALTO_CINTERION_CellularContext::~GEMALTO_CINTERION_CellularContext()
#if !NSAPI_PPP_AVAILABLE
NetworkStack *GEMALTO_CINTERION_CellularContext::get_stack()
{
if (_pdp_type == NON_IP_PDP_TYPE || _cp_in_use) {
tr_error("Requesting stack for NON-IP context! Should request control plane netif: get_cp_netif()");
return NULL;
}
if (!_stack) {
_stack = new GEMALTO_CINTERION_CellularStack(_at, _apn, _cid, _ip_stack_type);
_stack = new GEMALTO_CINTERION_CellularStack(_at, _apn, _cid, (nsapi_ip_stack_t)_pdp_type);
}
return _stack;
}

View File

@ -23,7 +23,7 @@ namespace mbed {
class GEMALTO_CINTERION_CellularContext: public AT_CellularContext {
public:
GEMALTO_CINTERION_CellularContext(ATHandler &at, CellularDevice *device, const char *apn);
GEMALTO_CINTERION_CellularContext(ATHandler &at, CellularDevice *device, const char *apn, bool cp_req = false, bool nonip_req = false);
virtual ~GEMALTO_CINTERION_CellularContext();
protected:

View File

@ -71,9 +71,9 @@ AT_CellularNetwork *QUECTEL_BC95::open_network_impl(ATHandler &at)
return new QUECTEL_BC95_CellularNetwork(at);
}
AT_CellularContext *QUECTEL_BC95::create_context_impl(ATHandler &at, const char *apn)
AT_CellularContext *QUECTEL_BC95::create_context_impl(ATHandler &at, const char *apn, bool cp_req, bool nonip_req)
{
return new QUECTEL_BC95_CellularContext(at, this, apn);
return new QUECTEL_BC95_CellularContext(at, this, apn, cp_req, nonip_req);
}
AT_CellularInformation *QUECTEL_BC95::open_information_impl(ATHandler &at)

View File

@ -32,7 +32,7 @@ public: // AT_CellularDevice
protected: // AT_CellularDevice
virtual AT_CellularNetwork *open_network_impl(ATHandler &at);
virtual AT_CellularContext *create_context_impl(ATHandler &at, const char *apn);
virtual AT_CellularContext *create_context_impl(ATHandler &at, const char *apn, bool cp_req = false, bool nonip_req = false);
virtual AT_CellularInformation *open_information_impl(ATHandler &at);
virtual nsapi_error_t init();
virtual nsapi_error_t reset();

View File

@ -16,11 +16,12 @@
*/
#include "QUECTEL_BC95_CellularContext.h"
#include "QUECTEL_BC95_CellularStack.h"
#include "CellularLog.h"
namespace mbed {
QUECTEL_BC95_CellularContext::QUECTEL_BC95_CellularContext(ATHandler &at, CellularDevice *device, const char *apn) :
AT_CellularContext(at, device, apn)
QUECTEL_BC95_CellularContext::QUECTEL_BC95_CellularContext(ATHandler &at, CellularDevice *device, const char *apn, bool cp_req, bool nonip_req) :
AT_CellularContext(at, device, apn, cp_req, nonip_req)
{
}
@ -31,8 +32,13 @@ QUECTEL_BC95_CellularContext::~QUECTEL_BC95_CellularContext()
#if !NSAPI_PPP_AVAILABLE
NetworkStack *QUECTEL_BC95_CellularContext::get_stack()
{
if (_pdp_type == NON_IP_PDP_TYPE || _cp_in_use) {
tr_error("Requesting stack for NON-IP context! Should request control plane netif: get_cp_netif()");
return NULL;
}
if (!_stack) {
_stack = new QUECTEL_BC95_CellularStack(_at, _cid, _ip_stack_type);
_stack = new QUECTEL_BC95_CellularStack(_at, _cid, (nsapi_ip_stack_t)_pdp_type);
}
return _stack;
}

View File

@ -23,7 +23,7 @@ namespace mbed {
class QUECTEL_BC95_CellularContext: public AT_CellularContext {
public:
QUECTEL_BC95_CellularContext(ATHandler &at, CellularDevice *device, const char *apn);
QUECTEL_BC95_CellularContext(ATHandler &at, CellularDevice *device, const char *apn, bool cp_req = false, bool nonip_req = false);
virtual ~QUECTEL_BC95_CellularContext();
protected:

View File

@ -40,6 +40,7 @@ static const intptr_t cellular_properties[AT_CellularBase::PROPERTY_MAX] = {
1, // PROPERTY_IPV4_STACK
0, // PROPERTY_IPV6_STACK
0, // PROPERTY_IPV4V6_STACK
1, // PROPERTY_NON_IP_PDP_TYPE
};
QUECTEL_BG96::QUECTEL_BG96(FileHandle *fh) : AT_CellularDevice(fh)
@ -56,9 +57,9 @@ AT_CellularNetwork *QUECTEL_BG96::open_network_impl(ATHandler &at)
return new QUECTEL_BG96_CellularNetwork(at);
}
AT_CellularContext *QUECTEL_BG96::create_context_impl(ATHandler &at, const char *apn)
AT_CellularContext *QUECTEL_BG96::create_context_impl(ATHandler &at, const char *apn, bool cp_req, bool nonip_req)
{
return new QUECTEL_BG96_CellularContext(at, this, apn);
return new QUECTEL_BG96_CellularContext(at, this, apn, cp_req, nonip_req);
}
AT_CellularInformation *QUECTEL_BG96::open_information_impl(ATHandler &at)

View File

@ -29,7 +29,7 @@ public:
protected: // AT_CellularDevice
virtual AT_CellularNetwork *open_network_impl(ATHandler &at);
virtual AT_CellularContext *create_context_impl(ATHandler &at, const char *apn);
virtual AT_CellularContext *create_context_impl(ATHandler &at, const char *apn, bool cp_req = false, bool nonip_req = false);
virtual AT_CellularInformation *open_information_impl(ATHandler &at);
virtual void set_ready_cb(Callback<void()> callback);

View File

@ -16,28 +16,57 @@
*/
#include "QUECTEL_BG96_CellularContext.h"
#include "QUECTEL_BG96_CellularStack.h"
#include "QUECTEL_BG96_ControlPlane_netif.h"
#include "CellularLog.h"
#include "Semaphore.h"
namespace mbed {
QUECTEL_BG96_CellularContext::QUECTEL_BG96_CellularContext(ATHandler &at, CellularDevice *device, const char *apn) :
AT_CellularContext(at, device, apn)
// Non-IP context supported only for context id 1
#define NIDD_PDP_CONTEXT_ID 1
// Timeout to wait for URC indicating NIDD connection opening
#define NIDD_OPEN_URC_TIMEOUT 3000
QUECTEL_BG96_CellularContext::QUECTEL_BG96_CellularContext(ATHandler &at, CellularDevice *device, const char *apn, bool cp_req, bool nonip_req) :
AT_CellularContext(at, device, apn, cp_req, nonip_req)
{
if (_nonip_req) {
_at.set_urc_handler("+QIND:", mbed::Callback<void()>(this, &QUECTEL_BG96_CellularContext::urc_nidd));
}
}
QUECTEL_BG96_CellularContext::~QUECTEL_BG96_CellularContext()
{
if (_nonip_req) {
_at.set_urc_handler("+QIND:", 0);
}
}
#if !NSAPI_PPP_AVAILABLE
NetworkStack *QUECTEL_BG96_CellularContext::get_stack()
{
if (!_stack) {
_stack = new QUECTEL_BG96_CellularStack(_at, _cid, _ip_stack_type);
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 QUECTEL_BG96_CellularStack(_at, _cid, (nsapi_ip_stack_t)_pdp_type);
}
return _stack;
}
#endif // #if !NSAPI_PPP_AVAILABLE
ControlPlane_netif *QUECTEL_BG96_CellularContext::get_cp_netif()
{
if (!_cp_netif) {
_cp_netif = new QUECTEL_BG96_ControlPlane_netif(_at, _cid);
}
return _cp_netif;
}
nsapi_error_t QUECTEL_BG96_CellularContext::do_user_authentication()
{
if (_pwd && _uname) {
@ -59,4 +88,110 @@ nsapi_error_t QUECTEL_BG96_CellularContext::do_user_authentication()
return NSAPI_ERROR_OK;
}
nsapi_error_t QUECTEL_BG96_CellularContext::activate_non_ip_context()
{
_at.lock();
// Open the NIDD connection
_at.cmd_start("AT+QCFGEXT=\"nipd\",1");
_at.cmd_stop();
_at.resp_start();
_at.resp_stop();
nsapi_size_or_error_t ret = _at.get_last_error();
_at.unlock();
if (ret == NSAPI_ERROR_OK) {
_semaphore.wait(NIDD_OPEN_URC_TIMEOUT);
if (_cid == -1) {
return NSAPI_ERROR_NO_CONNECTION;
}
}
return (ret == NSAPI_ERROR_OK) ? NSAPI_ERROR_OK : NSAPI_ERROR_NO_CONNECTION;
}
void QUECTEL_BG96_CellularContext::deactivate_non_ip_context()
{
// Close the NIDD connection
_at.cmd_start("AT+QCFGEXT=\"nipd\",0");
_at.cmd_stop();
_at.resp_start();
_at.resp_stop();
}
void QUECTEL_BG96_CellularContext::urc_nidd()
{
char nipd_string[6];
// skip "nipd"
_at.skip_param();
_at.read_string(nipd_string, sizeof(nipd_string));
if (!strcmp(nipd_string, "recv")) {
_cp_netif->data_received();
} else if (!strcmp(nipd_string, "open")) {
urc_nidd_open();
} else if (!strcmp(nipd_string, "close")) {
urc_nidd_close();
}
}
void QUECTEL_BG96_CellularContext::urc_nidd_open()
{
int err = _at.read_int();
if (!err) {
_is_context_active = true;
_is_context_activated = true;
_cid = NIDD_PDP_CONTEXT_ID;
} else {
tr_error("NIDD connection open failed with error: %d", err);
}
_semaphore.release();
}
void QUECTEL_BG96_CellularContext::urc_nidd_close()
{
set_disconnect();
}
nsapi_error_t QUECTEL_BG96_CellularContext::setup_control_plane_opt()
{
_at.lock();
_at.cmd_start("AT+QCFGEXT=\"pdp_type\",");
_at.write_int(NIDD_PDP_CONTEXT_ID);
_at.write_string("Non-IP");
_at.write_string(_apn);
_at.cmd_stop();
_at.resp_start();
_at.resp_stop();
_at.cmd_start("AT+CFUN=0");
_at.cmd_stop();
_at.resp_start();
_at.resp_stop();
_at.cmd_start("AT+CFUN=1");
_at.cmd_stop();
_at.resp_start();
_at.resp_stop();
// Configure Non-IP outgoing data type - 0 for no exception data
_at.cmd_start("AT+QCFGEXT=\"nipdcfg\",0,");
_at.write_string(_apn);
_at.cmd_stop();
_at.resp_start();
_at.resp_stop();
if (_at.get_last_error() == NSAPI_ERROR_OK) {
_cp_in_use = true;
if (_nonip_req) {
_pdp_type = NON_IP_PDP_TYPE;
}
}
return _at.unlock_return_error();
}
} /* namespace mbed */

View File

@ -23,14 +23,24 @@ namespace mbed {
class QUECTEL_BG96_CellularContext: public AT_CellularContext {
public:
QUECTEL_BG96_CellularContext(ATHandler &at, CellularDevice *device, const char *apn);
QUECTEL_BG96_CellularContext(ATHandler &at, CellularDevice *device, const char *apn, bool cp_req = false, bool nonip_req = false);
virtual ~QUECTEL_BG96_CellularContext();
protected:
#if !NSAPI_PPP_AVAILABLE
virtual NetworkStack *get_stack();
#endif // #if !NSAPI_PPP_AVAILABLE
virtual ControlPlane_netif *get_cp_netif();
virtual nsapi_error_t do_user_authentication();
virtual nsapi_error_t activate_non_ip_context();
virtual nsapi_error_t setup_control_plane_opt();
virtual void deactivate_non_ip_context();
rtos::Semaphore _semaphore;
private:
void urc_nidd();
void urc_nidd_open();
void urc_nidd_close();
};
} /* namespace mbed */

View File

@ -0,0 +1,68 @@
#include "QUECTEL_BG96_ControlPlane_netif.h"
namespace mbed {
QUECTEL_BG96_ControlPlane_netif::QUECTEL_BG96_ControlPlane_netif(ATHandler &at, int cid) : AT_ControlPlane_netif(at, cid)
{}
nsapi_size_or_error_t QUECTEL_BG96_ControlPlane_netif::send(const void *data, nsapi_size_t size)
{
_at.lock();
_at.cmd_start("AT+QCFGEXT=\"nipds\",0,");
_at.write_string((char *)data);
_at.write_int(size);
_at.cmd_stop();
_at.resp_start();
_at.resp_stop();
nsapi_error_t err = _at.get_last_error();
_at.unlock();
if (err == NSAPI_ERROR_OK) {
return size;
}
return err;
}
nsapi_size_or_error_t QUECTEL_BG96_ControlPlane_netif::recv(void *buffer, nsapi_size_t size)
{
_at.lock();
_at.cmd_start("AT+QCFGEXT=\"nipdr\",0");
_at.cmd_stop();
_at.resp_start("+QCFGEXT: ");
// skip 3 params: "nipdr",<total_receive_length>,<have_read_length>
_at.skip_param(3);
// get to <unread_length>
int unread_length = _at.read_int();
_at.resp_stop();
if (!unread_length || unread_length == -1) {
_at.unlock();
return NSAPI_ERROR_WOULD_BLOCK;
}
_at.cmd_start("AT+QCFGEXT=\"nipdr\",");
_at.write_int(unread_length);
_at.cmd_stop();
_at.resp_start("+QCFGEXT:");
// skip "nipdr"
_at.skip_param();
int read_length = _at.read_int();
_at.read_string((char *)buffer, read_length);
_at.resp_stop();
nsapi_error_t err = _at.get_last_error();
_at.unlock();
if (err == NSAPI_ERROR_OK && read_length) {
return read_length;
}
return NSAPI_ERROR_WOULD_BLOCK;
}
} // mbed namespace

View File

@ -0,0 +1,36 @@
/*
* 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.
*/
#ifndef QUECTEL_BG96_CONTROLPLANE_NETIF_H_
#define QUECTEL_BG96_CONTROLPLANE_NETIF_H_
#include "AT_ControlPlane_netif.h"
namespace mbed {
class QUECTEL_BG96_ControlPlane_netif: public AT_ControlPlane_netif {
public:
QUECTEL_BG96_ControlPlane_netif(ATHandler &at, int cid);
virtual ~QUECTEL_BG96_ControlPlane_netif() {};
// ControlPlane_netif
nsapi_size_or_error_t send(const void *data, nsapi_size_t size);
nsapi_size_or_error_t recv(void *buffer, nsapi_size_t size);
};
} /* namespace mbed */
#endif // QUECTEL_BG96_CONTROLPLANE_NETIF_H_

View File

@ -53,7 +53,7 @@ AT_CellularPower *QUECTEL_UG96::open_power_impl(ATHandler &at)
return new QUECTEL_UG96_CellularPower(at);
}
AT_CellularContext *QUECTEL_UG96::create_context_impl(ATHandler &at, const char *apn)
AT_CellularContext *QUECTEL_UG96::create_context_impl(ATHandler &at, const char *apn, bool cp_req, bool nonip_req)
{
return new QUECTEL_UG96_CellularContext(at, this, apn);
return new QUECTEL_UG96_CellularContext(at, this, apn, cp_req, nonip_req);
}

View File

@ -37,7 +37,7 @@ public:
protected: // AT_CellularDevice
virtual AT_CellularPower *open_power_impl(ATHandler &at);
virtual AT_CellularContext *create_context_impl(ATHandler &at, const char *apn);
virtual AT_CellularContext *create_context_impl(ATHandler &at, const char *apn, bool cp_req = false, bool nonip_req = false);
public: // NetworkInterface
void handle_urc(FileHandle *fh);

View File

@ -18,8 +18,8 @@
namespace mbed {
QUECTEL_UG96_CellularContext::QUECTEL_UG96_CellularContext(ATHandler &at, CellularDevice *device, const char *apn) :
AT_CellularContext(at, device, apn)
QUECTEL_UG96_CellularContext::QUECTEL_UG96_CellularContext(ATHandler &at, CellularDevice *device, const char *apn, bool cp_req, bool nonip_req) :
AT_CellularContext(at, device, apn, cp_req, nonip_req)
{
}

View File

@ -23,7 +23,7 @@ namespace mbed {
class QUECTEL_UG96_CellularContext: public AT_CellularContext {
public:
QUECTEL_UG96_CellularContext(ATHandler &at, CellularDevice *device, const char *apn);
QUECTEL_UG96_CellularContext(ATHandler &at, CellularDevice *device, const char *apn, bool cp_req = false, bool nonip_req = false);
virtual ~QUECTEL_UG96_CellularContext();
protected:

View File

@ -69,7 +69,7 @@ AT_CellularPower *UBLOX_AT::open_power_impl(ATHandler &at)
return new UBLOX_AT_CellularPower(at);
}
AT_CellularContext *UBLOX_AT::create_context_impl(ATHandler &at, const char *apn)
AT_CellularContext *UBLOX_AT::create_context_impl(ATHandler &at, const char *apn, bool cp_req, bool nonip_req)
{
return new UBLOX_AT_CellularContext(at, this, apn);
return new UBLOX_AT_CellularContext(at, this, apn, cp_req, nonip_req);
}

View File

@ -30,7 +30,7 @@ public:
protected: // AT_CellularDevice
virtual AT_CellularNetwork *open_network_impl(ATHandler &at);
virtual AT_CellularPower *open_power_impl(ATHandler &at);
virtual AT_CellularContext *create_context_impl(ATHandler &at, const char *apn);
virtual AT_CellularContext *create_context_impl(ATHandler &at, const char *apn, bool cp_req = false, bool nonip_req = false);
public: // NetworkInterface
void handle_urc(FileHandle *fh);
};

View File

@ -17,11 +17,12 @@
#include "UBLOX_AT_CellularContext.h"
#include "UBLOX_AT_CellularStack.h"
#include "APN_db.h"
#include "CellularLog.h"
namespace mbed {
UBLOX_AT_CellularContext::UBLOX_AT_CellularContext(ATHandler &at, CellularDevice *device, const char *apn) :
AT_CellularContext(at, device, apn)
UBLOX_AT_CellularContext::UBLOX_AT_CellularContext(ATHandler &at, CellularDevice *device, const char *apn, bool cp_req, bool nonip_req) :
AT_CellularContext(at, device, apn, cp_req, nonip_req)
{
// The authentication to use
_auth = NSAPI_SECURITY_UNKNOWN;
@ -33,9 +34,14 @@ UBLOX_AT_CellularContext::~UBLOX_AT_CellularContext()
NetworkStack *UBLOX_AT_CellularContext::get_stack()
{
if (!_stack) {
_stack = new UBLOX_AT_CellularStack(_at, _cid, _ip_stack_type);
if (_pdp_type == NON_IP_PDP_TYPE || _cp_in_use) {
tr_error("Requesting stack for NON-IP context! Should request control plane netif: get_cp_netif()");
return NULL;
}
if (!_stack) {
_stack = new UBLOX_AT_CellularStack(_at, _cid, (nsapi_ip_stack_t)_pdp_type);
}
return _stack;
}

View File

@ -23,7 +23,7 @@ namespace mbed {
class UBLOX_AT_CellularContext: public AT_CellularContext {
public:
UBLOX_AT_CellularContext(ATHandler &at, CellularDevice *device, const char *apn);
UBLOX_AT_CellularContext(ATHandler &at, CellularDevice *device, const char *apn, bool cp_req = false, bool nonip_req = false);
virtual ~UBLOX_AT_CellularContext();
virtual void do_connect();

View File

@ -0,0 +1,267 @@
/* CellularNonIPSocket
#include <CellularNonIPSocket.h>
* Copyright (c) 2015 ARM Limited
*
* 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 "platform/Callback.h"
#include "CellularNonIPSocket.h"
#include <stdio.h>
using namespace mbed;
CellularNonIPSocket::CellularNonIPSocket(CellularContext *cellular_context)
: _timeout(osWaitForever),
_readers(0), _writers(0), _pending(0),
_cp_netif(NULL),
_opened(false)
{
open(cellular_context);
}
nsapi_error_t CellularNonIPSocket::open(CellularContext *cellular_context)
{
return open(cellular_context->get_cp_netif());
}
CellularNonIPSocket::~CellularNonIPSocket()
{
close();
}
nsapi_error_t CellularNonIPSocket::open(ControlPlane_netif *cp_netif)
{
if (_opened) {
return NSAPI_ERROR_OK;
}
_lock.lock();
if (_cp_netif != NULL || cp_netif == NULL) {
_lock.unlock();
return NSAPI_ERROR_PARAMETER;
}
_cp_netif = cp_netif;
_event = callback(this, &CellularNonIPSocket::event);
_cp_netif->attach(Callback<void()>::thunk, &_event);
_opened = true;
_lock.unlock();
return NSAPI_ERROR_OK;
}
nsapi_error_t CellularNonIPSocket::close()
{
_lock.lock();
nsapi_error_t ret = NSAPI_ERROR_OK;
if (!_opened) {
return NSAPI_ERROR_NO_SOCKET;
}
// Just in case - tell the stack not to callback any more, then remove this socket.
_cp_netif->attach(0, 0);
_opened = false;
_cp_netif = 0; // Invalidate the cp_netif pointer - otherwise open() fails.
// Wakeup anything in a blocking operation
// on this socket
event();
// Wait until all readers and writers are gone
while (_readers || _writers) {
_lock.unlock();
_event_flag.wait_any(FINISHED_FLAG, osWaitForever);
_lock.lock();
}
_lock.unlock();
return ret;
}
nsapi_size_or_error_t CellularNonIPSocket::send(const void *data, nsapi_size_t size)
{
_lock.lock();
nsapi_size_or_error_t ret;
_writers++;
while (true) {
if (!_opened) {
ret = NSAPI_ERROR_NO_SOCKET;
break;
}
_pending = 0;
nsapi_size_or_error_t sent = _cp_netif->send(data, size);
if ((0 == _timeout) || (NSAPI_ERROR_WOULD_BLOCK != sent)) {
ret = sent;
break;
} else {
uint32_t flag;
// Release lock before blocking so other threads
// accessing this object aren't blocked
_lock.unlock();
flag = _event_flag.wait_any(WRITE_FLAG, _timeout);
_lock.lock();
if (flag & osFlagsError) {
// Timeout break
ret = NSAPI_ERROR_WOULD_BLOCK;
break;
}
}
}
_writers--;
if (!_opened || !_writers) {
_event_flag.set(FINISHED_FLAG);
}
_lock.unlock();
return ret;
}
nsapi_size_or_error_t CellularNonIPSocket::recv(void *buffer, nsapi_size_t size)
{
_lock.lock();
nsapi_size_or_error_t ret;
_readers++;
while (true) {
if (!_opened) {
ret = NSAPI_ERROR_NO_SOCKET;
break;
}
_pending = 0;
nsapi_size_or_error_t recv = _cp_netif->recv(buffer, size);
// Non-blocking sockets always return. Blocking only returns when success or errors other than WOULD_BLOCK
if ((0 == _timeout) || (NSAPI_ERROR_WOULD_BLOCK != recv)) {
ret = recv;
break;
} else {
uint32_t flag;
// Release lock before blocking so other threads
// accessing this object aren't blocked
_lock.unlock();
printf("\nWAITWAITWAIT\n");
flag = _event_flag.wait_any(READ_FLAG, _timeout);
_lock.lock();
if (flag & osFlagsError) {
// Timeout break
ret = NSAPI_ERROR_WOULD_BLOCK;
break;
}
}
}
_readers--;
if (!_opened || !_readers) {
_event_flag.set(FINISHED_FLAG);
}
_lock.unlock();
return ret;
}
void CellularNonIPSocket::set_blocking(bool blocking)
{
set_timeout(blocking ? -1 : 0);
}
void CellularNonIPSocket::set_timeout(int timeout)
{
_lock.lock();
if (timeout >= 0) {
_timeout = (uint32_t)timeout;
} else {
_timeout = osWaitForever;
}
_lock.unlock();
}
void CellularNonIPSocket::event()
{
_event_flag.set(READ_FLAG | WRITE_FLAG);
_pending += 1;
if (_callback && _pending == 1) {
_callback();
}
}
void CellularNonIPSocket::sigio(Callback<void()> callback)
{
_lock.lock();
_callback = callback;
_lock.unlock();
}
nsapi_error_t CellularNonIPSocket::connect(const SocketAddress &address)
{
return NSAPI_ERROR_UNSUPPORTED;
}
Socket *CellularNonIPSocket::accept(nsapi_error_t *error)
{
if (error) {
*error = NSAPI_ERROR_UNSUPPORTED;
}
return NULL;
}
nsapi_error_t CellularNonIPSocket::listen(int backlog)
{
return NSAPI_ERROR_UNSUPPORTED;
}
nsapi_size_or_error_t CellularNonIPSocket::sendto(const SocketAddress &address,
const void *data, nsapi_size_t size)
{
return NSAPI_ERROR_UNSUPPORTED;
}
nsapi_size_or_error_t CellularNonIPSocket::recvfrom(SocketAddress *address,
void *data, nsapi_size_t size)
{
return NSAPI_ERROR_UNSUPPORTED;
}
nsapi_error_t CellularNonIPSocket::setsockopt(int level, int optname, const void *optval, unsigned optlen)
{
return NSAPI_ERROR_UNSUPPORTED;
}
nsapi_error_t CellularNonIPSocket::getsockopt(int level, int optname, void *optval, unsigned *optlen)
{
return NSAPI_ERROR_UNSUPPORTED;
}
nsapi_error_t CellularNonIPSocket::getpeername(SocketAddress *address)
{
return NSAPI_ERROR_UNSUPPORTED;
}
nsapi_error_t CellularNonIPSocket::bind(const SocketAddress &address)
{
return NSAPI_ERROR_UNSUPPORTED;
}

View File

@ -0,0 +1,150 @@
/** \addtogroup netsocket */
/** @{*/
/* Socket
* Copyright (c) 2015 ARM Limited
*
* 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 CELLULARNONIPSOCKET_H
#define CELLULARNONIPSOCKET_H
#include "netsocket/Socket.h"
#include "rtos/Mutex.h"
#include "rtos/EventFlags.h"
#include "Callback.h"
#include "mbed_toolchain.h"
#include "ControlPlane_netif.h"
#include "CellularContext.h"
//Socket implementation for non ip datagrams over cellular control plane
class CellularNonIPSocket : public Socket {
public:
/** Destroy the socket.
*
* @note Closes socket if it's still open.
*/
virtual ~CellularNonIPSocket();
/** Creates and opens a socket on the given cellular context.
*
* @param cellular_context Cellular PDP context over which this socket
* is sending and receiving data.
*/
CellularNonIPSocket(mbed::CellularContext *cellular_context);
/** Opens a socket on the given cellular context.
*
* @param cellular_context Cellular PDP context over which this socket is sending and
* receiving data. The context has support for providing
* a control plane interface for data delivery.
* @return NSAPI_ERROR_OK on success
* NSAPI_ERROR_PARAMETER otherwise
*/
nsapi_error_t open(mbed::CellularContext *cellular_context);
/** Opens a socket that will use the given control plane interface for data delivery.
* Attaches the event as callback to the control plane interface.
*
* @param cp_netif Control plane interface for data delivery.
* @return NSAPI_ERROR_OK on success
* NSAPI_ERROR_PARAMETER otherwise
*
*/
nsapi_error_t open(mbed::ControlPlane_netif *cp_netif);
/** Closes socket
*
* @return NSAPI_ERROR_OK on success
* NSAPI_ERROR_NO_SOCKET otherwise
*/
virtual nsapi_error_t close();
/** Send data over a control plane cellular context.
*
* By default, send blocks until all data is sent. If socket is set to
* nonblocking or times out, a partial amount can be written.
* NSAPI_ERROR_WOULD_BLOCK is returned if no data was written.
*
* @param data Buffer of data to be sent.
* @param size Size of the buffer in bytes.
* @return Number of sent bytes on success, negative error
* code on failure.
*/
virtual nsapi_size_or_error_t send(const void *data, nsapi_size_t size);
/** Receive data from a socket.
*
* By default, recv blocks until some data is received. If socket is set to
* nonblocking or times out, NSAPI_ERROR_WOULD_BLOCK can be returned to
* indicate no data.
*
* @param data Pointer to buffer for received data.
* @param size Size of the buffer in bytes.
* @return Number of received bytes on success, negative error
* code on failure.
*/
virtual nsapi_size_or_error_t recv(void *data, nsapi_size_t size);
/** @copydoc Socket::set_blocking
*/
virtual void set_blocking(bool blocking);
/** @copydoc Socket::set_blocking
*/
virtual void set_timeout(int timeout);
/** @copydoc Socket::sigio
*/
virtual void sigio(mbed::Callback<void()> func);
// NOT SUPPORTED
virtual nsapi_error_t connect(const SocketAddress &address);
virtual Socket *accept(nsapi_error_t *error = NULL);
virtual nsapi_error_t listen(int backlog = 1);
virtual nsapi_error_t setsockopt(int level, int optname, const void *optval, unsigned optlen);
virtual nsapi_error_t getsockopt(int level, int optname, void *optval, unsigned *optlen);
virtual nsapi_error_t getpeername(SocketAddress *address);
virtual nsapi_size_or_error_t sendto(const SocketAddress &address,
const void *data, nsapi_size_t size);
virtual nsapi_size_or_error_t recvfrom(SocketAddress *address,
void *data, nsapi_size_t size);
virtual nsapi_error_t bind(const SocketAddress &address);
protected:
CellularNonIPSocket();
virtual void event();
uint32_t _timeout;
mbed::Callback<void()> _event;
mbed::Callback<void()> _callback;
rtos::EventFlags _event_flag;
rtos::Mutex _lock;
uint8_t _readers;
uint8_t _writers;
volatile unsigned _pending;
// Event flags
static const int READ_FLAG = 0x1u;
static const int WRITE_FLAG = 0x2u;
static const int FINISHED_FLAG = 0x3u;
mbed::ControlPlane_netif *_cp_netif;
bool _opened;
};
#endif // CELLULARNONIPSOCKET_H
/** @}*/

View File

@ -0,0 +1,78 @@
/*
* 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.
*/
#ifndef CONTROLPLANE_NETIF_H_
#define CONTROLPLANE_NETIF_H_
#include "nsapi_types.h"
namespace mbed {
/* Length of the buffer storing data received over control plane */
#define MAX_CP_DATA_RECV_LEN 2048
// TODO: need to make this l3ip compatible
class ControlPlane_netif {
public:
ControlPlane_netif() {}
virtual ~ControlPlane_netif() {}
/** Send data over cellular control plane
*
* @param cpdata Buffer of data to be sent over control plane connection
* @param cpdata_length Length of data in bytes
* @return Number of sent bytes on success, negative error
* code on failure.
*/
virtual nsapi_size_or_error_t send(const void *cpdata, nsapi_size_t cpdata_length) = 0;
/** Receive data over cellular control plane
*
* @param cpdata Destination buffer for data received from control plane connection
* @param cpdata_length Length of data in bytes
* @return Number of received bytes on success, negative error
* code on failure.
*/
virtual nsapi_size_or_error_t recv(void *cpdata, nsapi_size_t cpdata_length) = 0;
/** Register a callback on state change of the socket
*
* The specified callback will be called on state changes such as when
* the socket can recv/send successfully and on when an error
* occurs. The callback may also be called spuriously without reason.
*
* The callback may be called in an interrupt context and should not
* perform expensive operations such as recv/send calls.
*
* @param callback Function to call on state change
* @param data Argument to pass to callback
*/
virtual void attach(void (*callback)(void *), void *data) = 0;
/** Receives data from the control plane PDP context
*
* This function is called by cellular PDP context when data
* is received from network. It will invoke the callback set
* by the above attach.
*
* @param buffer Buffer containing received data
* @param size Size of data in bytes
*/
virtual void data_received() = 0;
};
} // mbed namespace
#endif