diff --git a/connectivity/cellular/include/cellular/framework/API/ATHandler.h b/connectivity/cellular/include/cellular/framework/API/ATHandler.h index 02499118ccc..6f8228def30 100644 --- a/connectivity/cellular/include/cellular/framework/API/ATHandler.h +++ b/connectivity/cellular/include/cellular/framework/API/ATHandler.h @@ -363,6 +363,12 @@ class ATHandler { */ void write_hex_string(const char *str, size_t size, bool quote_string = true); + /** Get the error detected during read_int() + * + * @return the latest negative integer error got from read_int(). + */ + int32_t get_last_read_error() const; + /** Reads as string and converts result to integer. Supports only non-negative integers. * * @return the non-negative integer or -1 in case of error. @@ -599,6 +605,7 @@ class ATHandler { nsapi_error_t _last_err; int _last_3gpp_error; device_err_t _last_at_err; + int32_t _last_read_error{}; uint16_t _oob_string_max_length; char *_output_delimiter; diff --git a/connectivity/cellular/include/cellular/framework/API/CellularContext.h b/connectivity/cellular/include/cellular/framework/API/CellularContext.h index e9c22f95119..ffc597b0b57 100644 --- a/connectivity/cellular/include/cellular/framework/API/CellularContext.h +++ b/connectivity/cellular/include/cellular/framework/API/CellularContext.h @@ -41,6 +41,12 @@ namespace mbed { * @{ */ +/// Radio Access Technology type +enum RadioAccessTechnologyType { + CATM1, ///< LTE CAT-M or LTE-M + CATNB ///< NB-IoT (Narrowband IoT) +}; + /// CellularContext is CellularInterface/NetworkInterface with extensions for cellular connectivity class CellularContext : public CellularInterface { @@ -158,6 +164,7 @@ class CellularContext : public CellularInterface { virtual nsapi_error_t connect(const char *sim_pin, const char *apn = 0, const char *uname = 0, const char *pwd = 0) = 0; virtual void set_credentials(const char *apn, const char *uname = 0, const char *pwd = 0) = 0; + virtual void set_access_technology(RadioAccessTechnologyType rat = CATM1) = 0; virtual bool is_connected() = 0; /** Same as NetworkInterface::get_default_instance() diff --git a/connectivity/cellular/include/cellular/framework/API/CellularDevice.h b/connectivity/cellular/include/cellular/framework/API/CellularDevice.h index 7d0adbf1cc2..d1c9dbe4d51 100644 --- a/connectivity/cellular/include/cellular/framework/API/CellularDevice.h +++ b/connectivity/cellular/include/cellular/framework/API/CellularDevice.h @@ -424,6 +424,14 @@ class CellularDevice { */ void set_retry_timeout_array(const uint16_t timeout[], int array_len); + /** + * @brief Enable serial multiplexing according to 3GPP TS 27.010, if implemented + * on this modem. + * + * @return Error code or success + */ + virtual nsapi_error_t enable_cmux() { return NSAPI_ERROR_UNSUPPORTED;}; + protected: //Common functions friend class AT_CellularNetwork; friend class AT_CellularContext; @@ -453,6 +461,8 @@ class CellularDevice { CellularStateMachine *_state_machine; Callback _status_cb; + bool _cmux_enabled = false; + private: //Member variables CellularNetwork *_nw; char _sim_pin[MAX_PIN_SIZE + 1]; diff --git a/connectivity/cellular/include/cellular/framework/AT/AT_CellularContext.h b/connectivity/cellular/include/cellular/framework/AT/AT_CellularContext.h index d30709954b7..2db191c4dad 100644 --- a/connectivity/cellular/include/cellular/framework/AT/AT_CellularContext.h +++ b/connectivity/cellular/include/cellular/framework/AT/AT_CellularContext.h @@ -22,6 +22,7 @@ #include "rtos/Semaphore.h" #include "AT_CellularDevice.h" +#include const int MAX_APN_LENGTH = 63 + 1; @@ -54,6 +55,7 @@ class AT_CellularContext : public CellularContext { virtual nsapi_error_t connect(const char *sim_pin, const char *apn = 0, const char *uname = 0, const char *pwd = 0); virtual void set_credentials(const char *apn, const char *uname = 0, const char *pwd = 0); + virtual void set_access_technology(RadioAccessTechnologyType rat = CATM1); // from CellularContext virtual nsapi_error_t get_pdpcontext_params(pdpContextList_t ¶ms_list); @@ -115,6 +117,13 @@ class AT_CellularContext : public CellularContext { */ virtual const char *get_nonip_context_type_str(); + /** + * @brief Set the cellular technology and band based on the \c _rat and \c _band class variables. + * + * Modems which support this functionality should override this in their CellularContext implementations. + */ + virtual void enable_access_technology() {} + private: #if NSAPI_PPP_AVAILABLE nsapi_error_t open_data_channel(); @@ -130,6 +139,8 @@ class AT_CellularContext : public CellularContext { nsapi_error_t check_operation(nsapi_error_t err, ContextOperation op); void ciot_opt_cb(mbed::CellularNetwork::CIoT_Supported_Opt ciot_opt); virtual void do_connect_with_retry(); + +protected: void set_cid(int cid); private: @@ -146,6 +157,7 @@ class AT_CellularContext : public CellularContext { bool _cp_req; bool _is_connected; ATHandler &_at; + std::optional _rat; }; /** diff --git a/connectivity/cellular/include/cellular/framework/AT/AT_CellularDevice.h b/connectivity/cellular/include/cellular/framework/AT/AT_CellularDevice.h index 5fc37e791dd..95a0447a02f 100755 --- a/connectivity/cellular/include/cellular/framework/AT/AT_CellularDevice.h +++ b/connectivity/cellular/include/cellular/framework/AT/AT_CellularDevice.h @@ -130,6 +130,8 @@ class AT_CellularDevice : public CellularDevice { virtual nsapi_error_t set_baud_rate(int baud_rate); + nsapi_error_t enable_cmux() override; + #if MBED_CONF_CELLULAR_USE_SMS virtual CellularSMS *open_sms(); diff --git a/connectivity/cellular/source/framework/AT/AT_CellularContext.cpp b/connectivity/cellular/source/framework/AT/AT_CellularContext.cpp index f2b1d632b95..27601a3472c 100644 --- a/connectivity/cellular/source/framework/AT/AT_CellularContext.cpp +++ b/connectivity/cellular/source/framework/AT/AT_CellularContext.cpp @@ -109,6 +109,19 @@ nsapi_error_t AT_CellularContext::connect() } call_network_cb(NSAPI_STATUS_CONNECTING); + set_device_ready(); + + _at.lock(); + bool valid_context = get_context(); + _at.unlock(); + if(!valid_context) { + set_new_context(_cid); + } + + do_user_authentication(); + + enable_access_technology(); + nsapi_error_t err = _device->attach_to_network(); _cb_data.error = check_operation(err, OP_CONNECT); _retry_count = 0; @@ -278,6 +291,11 @@ void AT_CellularContext::set_credentials(const char *apn, const char *uname, con _pwd = pwd; } +void AT_CellularContext::set_access_technology(RadioAccessTechnologyType rat) +{ + _rat = rat; +} + // PDP Context handling void AT_CellularContext::delete_current_context() { diff --git a/connectivity/cellular/source/framework/AT/AT_CellularDevice.cpp b/connectivity/cellular/source/framework/AT/AT_CellularDevice.cpp index 7a2d79f07a4..b751d0e2cbc 100644 --- a/connectivity/cellular/source/framework/AT/AT_CellularDevice.cpp +++ b/connectivity/cellular/source/framework/AT/AT_CellularDevice.cpp @@ -414,7 +414,8 @@ nsapi_error_t AT_CellularDevice::init() _at.flush(); _at.at_cmd_discard("E0", ""); if (_at.get_last_error() == NSAPI_ERROR_OK) { - _at.at_cmd_discard("+CMEE", "=1"); + // Enable verbose error messages + _at.at_cmd_discard("+CMEE", "=2"); _at.at_cmd_discard("+CFUN", "=1"); if (_at.get_last_error() == NSAPI_ERROR_OK) { break; @@ -623,6 +624,29 @@ nsapi_error_t AT_CellularDevice::set_baud_rate(int baud_rate) return error; } +nsapi_error_t AT_CellularDevice::enable_cmux() +{ + setup_at_handler(); + + _at.lock(); + for (int retry = 1; retry <= 3; retry++) { + _at.clear_error(); + _at.flush(); + _at.at_cmd_discard("E0", ""); + if (_at.get_last_error() == NSAPI_ERROR_OK) { + _at.at_cmd_discard("+CMUX", "=0"); + if (_at.get_last_error() == NSAPI_ERROR_OK) { + _cmux_enabled = true; + break; + } + } + tr_debug("Wait 100ms to init modem"); + rtos::ThisThread::sleep_for(100ms); // let modem have time to get ready + } + + return _at.unlock_return_error(); +} + nsapi_error_t AT_CellularDevice::set_baud_rate_impl(int baud_rate) { return _at.at_cmd_discard("+IPR", "=", "%d", baud_rate); diff --git a/connectivity/cellular/source/framework/AT/AT_CellularNetwork.cpp b/connectivity/cellular/source/framework/AT/AT_CellularNetwork.cpp index 0afbf88e4a1..d780e459e28 100644 --- a/connectivity/cellular/source/framework/AT/AT_CellularNetwork.cpp +++ b/connectivity/cellular/source/framework/AT/AT_CellularNetwork.cpp @@ -219,6 +219,7 @@ nsapi_error_t AT_CellularNetwork::set_registration(const char *plmn) return NSAPI_ERROR_DEVICE_ERROR; } if (mode != NWModeAutomatic) { + // Force operator registration return _at.at_cmd_discard("+COPS", "=0"); } return NSAPI_ERROR_OK; diff --git a/connectivity/cellular/source/framework/AT/AT_CellularStack.cpp b/connectivity/cellular/source/framework/AT/AT_CellularStack.cpp index 961d101c9b5..478905e5f75 100644 --- a/connectivity/cellular/source/framework/AT/AT_CellularStack.cpp +++ b/connectivity/cellular/source/framework/AT/AT_CellularStack.cpp @@ -332,7 +332,7 @@ nsapi_size_or_error_t AT_CellularStack::socket_recvfrom(nsapi_socket_t handle, S if (socket->closed) { tr_info("recvfrom socket %d closed", socket->id); - return 0; + return NSAPI_ERROR_NO_CONNECTION; } nsapi_size_or_error_t ret_val = NSAPI_ERROR_OK; diff --git a/connectivity/cellular/source/framework/device/ATHandler.cpp b/connectivity/cellular/source/framework/device/ATHandler.cpp index 7388bd4eb4a..7f0337a3d79 100644 --- a/connectivity/cellular/source/framework/device/ATHandler.cpp +++ b/connectivity/cellular/source/framework/device/ATHandler.cpp @@ -46,7 +46,7 @@ using namespace std::chrono_literals; #define PROCESS_URC_TIME 20ms // Suppress logging of very big packet payloads, maxlen is approximate due to write/read are cached -#define DEBUG_MAXLEN 60 +#define DEBUG_MAXLEN 120 #define DEBUG_END_MARK "..\r" const char *mbed::OK = "OK\r\n"; @@ -697,6 +697,11 @@ ssize_t ATHandler::read_hex_string(char *buf, size_t size) return buf_idx; } +int32_t ATHandler::get_last_read_error() const +{ + return _last_read_error; +} + int32_t ATHandler::read_int() { if (!ok_to_proceed() || !_stop_tag || _stop_tag->found) { @@ -711,9 +716,11 @@ int32_t ATHandler::read_int() errno = 0; long result = std::strtol(buff, NULL, 10); if ((result == LONG_MIN || result == LONG_MAX) && errno == ERANGE) { + _last_read_error = result; return -1; // overflow/underflow } if (result < 0) { + _last_read_error = result; return -1; // negative values are unsupported } if (*buff == '\0') { diff --git a/connectivity/cellular/source/framework/device/CellularStateMachine.cpp b/connectivity/cellular/source/framework/device/CellularStateMachine.cpp index 44d67b729a8..0a452eacb3a 100644 --- a/connectivity/cellular/source/framework/device/CellularStateMachine.cpp +++ b/connectivity/cellular/source/framework/device/CellularStateMachine.cpp @@ -28,7 +28,7 @@ using namespace std::chrono_literals; // timeout to wait for AT responses #define TIMEOUT_POWER_ON 1s -#define TIMEOUT_SIM_PIN 1s +#define TIMEOUT_SIM_PIN 10s #define TIMEOUT_NETWORK 10s /** CellularStateMachine does connecting up to packet service attach, and * after that it's up to CellularContext::connect() to connect to PDN. diff --git a/connectivity/drivers/cellular/GEMALTO/CMakeLists.txt b/connectivity/drivers/cellular/GEMALTO/CMakeLists.txt index d7bf4f24272..076c1fd212c 100644 --- a/connectivity/drivers/cellular/GEMALTO/CMakeLists.txt +++ b/connectivity/drivers/cellular/GEMALTO/CMakeLists.txt @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 if("COMPONENT_GEMALTO_CINTERION=1" IN_LIST MBED_TARGET_DEFINITIONS) - add_subdirectory(CINTERION) + add_subdirectory(COMPONENT_GEMALTO_CINTERION) endif() \ No newline at end of file diff --git a/connectivity/drivers/cellular/GEMALTO/COMPONENT_GEMALTO_CINTERION/GEMALTO_CINTERION.cpp b/connectivity/drivers/cellular/GEMALTO/COMPONENT_GEMALTO_CINTERION/GEMALTO_CINTERION.cpp index 4a72a93f8a1..2b73e91ba0d 100644 --- a/connectivity/drivers/cellular/GEMALTO/COMPONENT_GEMALTO_CINTERION/GEMALTO_CINTERION.cpp +++ b/connectivity/drivers/cellular/GEMALTO/COMPONENT_GEMALTO_CINTERION/GEMALTO_CINTERION.cpp @@ -17,6 +17,7 @@ #include "GEMALTO_CINTERION_CellularContext.h" #include "GEMALTO_CINTERION_CellularInformation.h" +#include "GEMALTO_CINTERION_CellularNetwork.h" #include "GEMALTO_CINTERION.h" #include "AT_CellularNetwork.h" #include "CellularLog.h" @@ -30,6 +31,79 @@ GEMALTO_CINTERION::GEMALTO_CINTERION(FileHandle *fh) : AT_CellularDevice(fh) { } +time_t GEMALTO_CINTERION::get_time() +{ + tr_info("GEMALTO_CINTERION::get_time\n"); + + _at.lock(); + + //"+CCLK: \"%y/%m/%d,%H:%M:%S+ZZ" + _at.cmd_start_stop("+CCLK", "?"); + _at.resp_start("+CCLK:"); + + char time_str[50]; + time_t now = 0; + while (_at.info_resp()) { + int date_len = _at.read_string(time_str, sizeof(time_str)); + tr_debug("Read %d bytes for the date\n", date_len); + if (date_len > 0) { + now = parse_time(time_str); + } + } + _at.resp_stop(); + + _at.unlock(); + + // adjust for timezone offset which is +/- in 15 minute increments + time_t delta = ((time_str[18] - '0') * 10 + (time_str[19] - '0')) * (15 * 60); + + if (time_str[17] == '-') { + now = now + delta; + } else if (time_str[17] == '+') { + now = now - delta; + } + + return now; +} + +time_t GEMALTO_CINTERION::get_local_time() +{ + tr_info("GEMALTO_CINTERION::get_local_time\n"); + + _at.lock(); + + //"+CCLK: \"%y/%m/%d,%H:%M:%S" + _at.cmd_start_stop("+CCLK", "?"); + _at.resp_start("+CCLK:"); + + char time_str[50]; + time_t now; + while (_at.info_resp()) { + int date_len = _at.read_string(time_str, sizeof(time_str)); + tr_debug("Read %d bytes for the date\n", date_len); + if (date_len > 0) { + now = parse_time(time_str); + } + } + _at.resp_stop(); + + _at.unlock(); + + return now; +} + +void GEMALTO_CINTERION::set_time(time_t const epoch, int const timezone) +{ + char time_buf[21]; + strftime(time_buf, sizeof(time_buf), "%g/%m/%d,%H:%M:%S", gmtime(&epoch)); + snprintf(time_buf + 17, 4, "%+03d", timezone); + + _at.lock(); + _at.at_cmd_discard("+CCLK", "=", "%s", time_buf); + _at.unlock(); +} + + 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, cp_req, nonip_req); @@ -43,6 +117,11 @@ AT_CellularInformation *GEMALTO_CINTERION::open_information_impl(ATHandler &at) return AT_CellularDevice::open_information_impl(at); } +AT_CellularNetwork *GEMALTO_CINTERION::open_network_impl(ATHandler &at) +{ + return new GEMALTO_CINTERION_CellularNetwork(at, *this, _module); +} + nsapi_error_t GEMALTO_CINTERION::init() { nsapi_error_t err = AT_CellularDevice::init(); @@ -70,6 +149,8 @@ nsapi_error_t GEMALTO_CINTERION::init() init_module_ems31(); } else if (memcmp(model, "EHS5-E", sizeof("EHS5-E") - 1) == 0) { init_module_ehs5e(); + } else if (memcmp(model, "TX62", sizeof("TX62") - 1) == 0) { + init_module_tx62(); } else { tr_error("Cinterion model unsupported %s", model); return NSAPI_ERROR_UNSUPPORTED; @@ -202,6 +283,58 @@ void GEMALTO_CINTERION::init_module_ehs5e() _module = ModuleEHS5E; } +void GEMALTO_CINTERION::init_module_tx62() +{ + // TX-62 + static const intptr_t cellular_properties[AT_CellularDevice::PROPERTY_MAX] = { + AT_CellularNetwork::RegistrationModeLAC,// C_EREG + AT_CellularNetwork::RegistrationModeDisable, // C_GREG + AT_CellularNetwork::RegistrationModeDisable, // 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 + 0, // PROPERTY_IPV6_STACK + 0, // PROPERTY_IPV4V6_STACK + 0, // PROPERTY_NON_IP_PDP_TYPE + 1, // PROPERTY_AT_CGEREP + 1, // PROPERTY_AT_COPS_FALLBACK_AUTO + 7, // PROPERTY_SOCKET_COUNT + 1, // PROPERTY_IP_TCP + 1, // PROPERTY_IP_UDP + 0, // PROPERTY_AT_SEND_DELAY + }; + set_cellular_properties(cellular_properties); + _module = ModuleTX62; + + // Enable network time zone updates + _at.at_cmd_discard("+CTZU", "=", "%d", 1); +} + +time_t GEMALTO_CINTERION::parse_time(char const *time_str) { + struct tm now; + + now.tm_year = std::strtol(time_str, NULL, 10) + 100; // mktime starts from 1900 + time_str += 3; // Skip '/' + now.tm_mon = std::strtol(time_str, NULL, 10); + time_str += 3; // Skip '/' + now.tm_mday = std::strtol(time_str, NULL, 10); + time_str += 3; // Skip ',' + now.tm_hour = std::strtol(time_str, NULL, 10); + time_str += 3; // Skip ':' + now.tm_min = std::strtol(time_str, NULL, 10); + time_str += 3; + now.tm_sec = std::strtol(time_str, NULL, 10); + + tr_debug("Year: %d, month: %d, day:%d, hour:%d, minute:%d, second:%d\n", now.tm_year, now.tm_mon, now.tm_mday, now.tm_hour, now.tm_min, now.tm_sec); + + return mktime(&now); +} + #if MBED_CONF_GEMALTO_CINTERION_PROVIDE_DEFAULT #include "drivers/BufferedSerial.h" CellularDevice *CellularDevice::get_default_instance() diff --git a/connectivity/drivers/cellular/GEMALTO/COMPONENT_GEMALTO_CINTERION/GEMALTO_CINTERION.h b/connectivity/drivers/cellular/GEMALTO/COMPONENT_GEMALTO_CINTERION/GEMALTO_CINTERION.h index ca2d6f542ab..e5c2e59f0bc 100644 --- a/connectivity/drivers/cellular/GEMALTO/COMPONENT_GEMALTO_CINTERION/GEMALTO_CINTERION.h +++ b/connectivity/drivers/cellular/GEMALTO/COMPONENT_GEMALTO_CINTERION/GEMALTO_CINTERION.h @@ -44,15 +44,38 @@ class GEMALTO_CINTERION : public AT_CellularDevice { ModuleBGS2, ModuleEMS31, ModuleEHS5E, + ModuleTX62, }; static Module get_module(); + /** + * @brief Get the RTC time from the Cinterion, as a UNIX seconds timestamp in UTC + */ + time_t get_time(); + + /** + * @brief Get the RTC time from the Cinterion, as a UNIX seconds timestamp in the local time zone + */ + time_t get_local_time(); + + /** + * @brief Set the RTC time on the Cinterion. + * + * Note that any time set will be overwritten once the modem can get time from a cellular network + * + * @param timestamp UNIX timestamp to set, in the local time zone + * @param timezone Local time zone, as a signed number of 15-minute increments from UTC time. Example: + * passing -8 would indicate UTC-2. + */ + virtual void set_time(time_t timestamp, int const timezone = 0); + protected: // AT_CellularDevice 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); + AT_CellularNetwork *open_network_impl(ATHandler &at) override; -protected: - virtual nsapi_error_t init(); +public: + nsapi_error_t init() override; private: static Module _module; @@ -60,6 +83,10 @@ class GEMALTO_CINTERION : public AT_CellularDevice { void init_module_els61(); void init_module_ems31(); void init_module_ehs5e(); + void init_module_tx62(); + + /// Convert time & date string returned by the modem into time_t + time_t parse_time(char const * time_str); }; } // namespace mbed diff --git a/connectivity/drivers/cellular/GEMALTO/COMPONENT_GEMALTO_CINTERION/GEMALTO_CINTERION_CellularContext.cpp b/connectivity/drivers/cellular/GEMALTO/COMPONENT_GEMALTO_CINTERION/GEMALTO_CINTERION_CellularContext.cpp index 38a5ba48196..28faa47e616 100644 --- a/connectivity/drivers/cellular/GEMALTO/COMPONENT_GEMALTO_CINTERION/GEMALTO_CINTERION_CellularContext.cpp +++ b/connectivity/drivers/cellular/GEMALTO/COMPONENT_GEMALTO_CINTERION/GEMALTO_CINTERION_CellularContext.cpp @@ -49,4 +49,108 @@ NetworkStack *GEMALTO_CINTERION_CellularContext::get_stack() } #endif // NSAPI_PPP_AVAILABLE +nsapi_error_t GEMALTO_CINTERION_CellularContext::do_user_authentication() +{ + // if user has defined user name and password we need to call CGAUTH before activating or modifying context + if (_pwd && _uname && (strcmp(_pwd, "") != 0) && (strcmp(_uname, "") != 0)) { + if (!get_device()->get_property(AT_CellularDevice::PROPERTY_AT_CGAUTH)) { + return NSAPI_ERROR_UNSUPPORTED; + } + + _at.at_cmd_discard("^SGAUTH", "=", "%d%d%s%s", _cid, _authentication_type, _uname, _pwd); + + if (_at.get_last_error() != NSAPI_ERROR_OK) { + return NSAPI_ERROR_AUTH_FAILURE; + } + } + else { + tr_info("Empty pwd and username fields: no need for authentication\n"); + } + + return NSAPI_ERROR_OK; +} + +void GEMALTO_CINTERION_CellularContext::enable_access_technology() +{ + if (!_rat.has_value()) { + return; + } + + switch (*_rat) + { + case CATM1: + _at.at_cmd_discard("^SXRAT", "=","%d", 7); // 7 = CAT.M1 + + // Ensure all bands are enabled by setting ^SCFG to the bitmask of all valid bands + _at.cmd_start_stop("^SCFG", "=","%s%d", "Radio/Band/CatM", "F0E189F"); + _at.resp_start("^SCFG"); + break; + + case CATNB: + _at.at_cmd_discard("^SXRAT", "=","%d", 8); // 8 = CAT.NB1 + + // Ensure all bands are enabled by setting ^SCFG to the bitmask of all valid bands + _at.cmd_start_stop("^SCFG", "=","%s%s", "Radio/Band/CatNB", "10000200000000"); + _at.resp_start("^SCFG"); + break; + + default: + break; + } +} + +bool GEMALTO_CINTERION_CellularContext::get_context() +{ + _at.cmd_start_stop("+CGDCONT", "?"); + _at.resp_start("+CGDCONT:"); + set_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 && (strcmp(apn, _apn) == 0)) { + // APN matched -> Check PDP type + pdp_type_t pdp_type = string_to_pdp_type(pdp_type_from_context); + tr_debug("CID %d APN \"%s\" pdp_type %u", cid, apn, pdp_type); + + // Accept exact matching PDP context type or dual PDP context for modems that support both IPv4 and IPv6 stacks + if (get_device()->get_property(pdp_type_t_to_cellular_property(pdp_type)) || + ((pdp_type == IPV4V6_PDP_TYPE && (get_device()->get_property(AT_CellularDevice::PROPERTY_IPV4_PDP_TYPE) && + get_device()->get_property(AT_CellularDevice::PROPERTY_IPV6_PDP_TYPE))) && !_nonip_req)) { + _pdp_type = pdp_type; + set_cid(cid); + } + } + else { + cid_max = 0; + } + } + } + + _at.resp_stop(); + if (_cid == -1) { // no suitable context was found so create a new one + if (!set_new_context(cid_max + 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 */ diff --git a/connectivity/drivers/cellular/GEMALTO/COMPONENT_GEMALTO_CINTERION/GEMALTO_CINTERION_CellularContext.h b/connectivity/drivers/cellular/GEMALTO/COMPONENT_GEMALTO_CINTERION/GEMALTO_CINTERION_CellularContext.h index 86209eda599..987d116aefa 100644 --- a/connectivity/drivers/cellular/GEMALTO/COMPONENT_GEMALTO_CINTERION/GEMALTO_CINTERION_CellularContext.h +++ b/connectivity/drivers/cellular/GEMALTO/COMPONENT_GEMALTO_CINTERION/GEMALTO_CINTERION_CellularContext.h @@ -30,6 +30,9 @@ class GEMALTO_CINTERION_CellularContext: public AT_CellularContext { #if !NSAPI_PPP_AVAILABLE virtual NetworkStack *get_stack(); #endif // NSAPI_PPP_AVAILABLE + nsapi_error_t do_user_authentication() override; + void enable_access_technology() override; + bool get_context() override; }; } /* namespace mbed */ diff --git a/connectivity/drivers/cellular/GEMALTO/COMPONENT_GEMALTO_CINTERION/GEMALTO_CINTERION_CellularNetwork.h b/connectivity/drivers/cellular/GEMALTO/COMPONENT_GEMALTO_CINTERION/GEMALTO_CINTERION_CellularNetwork.h new file mode 100644 index 00000000000..421b8e7e0e1 --- /dev/null +++ b/connectivity/drivers/cellular/GEMALTO/COMPONENT_GEMALTO_CINTERION/GEMALTO_CINTERION_CellularNetwork.h @@ -0,0 +1,46 @@ +/* + * 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 GEMALTO_CINTERION_CELLULARNETWORK_H_ +#define GEMALTO_CINTERION_CELLULARNETWORK_H_ + +#include "AT_CellularNetwork.h" +#include "GEMALTO_CINTERION.h" + +namespace mbed { + +class GEMALTO_CINTERION_CellularNetwork: public AT_CellularNetwork { + const GEMALTO_CINTERION::Module _module; +public: + GEMALTO_CINTERION_CellularNetwork(ATHandler &at, AT_CellularDevice &device, GEMALTO_CINTERION::Module module): + AT_CellularNetwork(at, device), + _module(module) + {} + + virtual nsapi_error_t set_attach() + { + if (_module == GEMALTO_CINTERION::ModuleTX62) { + return NSAPI_ERROR_OK; + } + return AT_CellularNetwork::set_attach(); + } + +protected: +}; + +} /* namespace mbed */ + +#endif // GEMALTO_CINTERION_CELLULARNETWORK_H_ \ No newline at end of file diff --git a/connectivity/drivers/cellular/GEMALTO/COMPONENT_GEMALTO_CINTERION/GEMALTO_CINTERION_CellularStack.cpp b/connectivity/drivers/cellular/GEMALTO/COMPONENT_GEMALTO_CINTERION/GEMALTO_CINTERION_CellularStack.cpp index a78e89a062b..dbca19afaa2 100644 --- a/connectivity/drivers/cellular/GEMALTO/COMPONENT_GEMALTO_CINTERION/GEMALTO_CINTERION_CellularStack.cpp +++ b/connectivity/drivers/cellular/GEMALTO/COMPONENT_GEMALTO_CINTERION/GEMALTO_CINTERION_CellularStack.cpp @@ -100,19 +100,111 @@ void GEMALTO_CINTERION_CellularStack::urc_sisr() sisr_urc_handler(sock_id, urc_code); } +void GEMALTO_CINTERION_CellularStack::urc_sysstart() +{ + // close sockets if open + _at.lock(); + for (int i = 0; i < _device.get_property(AT_CellularDevice::PROPERTY_SOCKET_COUNT); i++) { + _at.clear_error(); + socket_close_impl(i); + } + _at.clear_error(); + _at.unlock(); +} + void GEMALTO_CINTERION_CellularStack::sisr_urc_handler(int sock_id, int urc_code) { CellularSocket *sock = find_socket(sock_id); if (sock) { - if (urc_code == 1) { // data available + if (urc_code > 0) { // data available if (sock->_cb) { - sock->pending_bytes = 1; + sock->pending_bytes = urc_code; sock->_cb(sock->_data); } } } } +void GEMALTO_CINTERION_CellularStack::urc_gnss() { + char gnss_string[100] = {'$', 'G'}; + if (_gnss_cb) { + _at.set_delimiter('\n'); + _at.read_string(&gnss_string[2], 98); + _at.set_default_delimiter(); + _gnss_cb(gnss_string); + } +} + +void GEMALTO_CINTERION_CellularStack::beginGNSS(mbed::Callback gnss_cb) { + _at.lock(); + _gnss_cb = gnss_cb; + _at.at_cmd_discard("^SGPSC", "=", "%s%d", "Engine/StartMode", 0); + _at.at_cmd_discard("^SGPSC", "=", "%s%d", "Engine", 0); + _at.at_cmd_discard("^SGPSC", "=", "%s%s", "Nmea/Urc", "off"); + + // Set up LNA_ENABLE GPIO + _at.at_cmd_discard("^SPIO", "=", "%d", 1); + _at.at_cmd_discard("^SCPIN", "=", "%d%d%d%d", 1, 7, 1, 0); + _at.clear_error(); + _at.unlock(); +} + +void GEMALTO_CINTERION_CellularStack::endGNSS() { + _at.lock(); + _at.at_cmd_discard("^SSIO", "=", "%d%d", 7, 0); + _gnss_cb = nullptr; + _at.clear_error(); + _at.unlock(); +} + +void GEMALTO_CINTERION_CellularStack::enableCmux() +{ + _at.at_cmd_discard("+CMUX", "=0"); +} + +int GEMALTO_CINTERION_CellularStack::startGNSS() { + _at.lock(); + _engine = false; + _at.at_cmd_discard("^SSIO", "=", "%d%d", 7, 1); + _at.cmd_start_stop("^SGPSC", "=", "%s%d", "Engine", 3); + _at.resp_start("^SGPSC: \"Engine\","); + + char respEng[2]; + int resp_len = _at.read_string(respEng, sizeof(respEng)); + if (strcmp(respEng, "3") != 0) { + _engine = false; + _at.at_cmd_discard("^SGPSC", "=", "%s%d", "Engine", 0); + _at.at_cmd_discard("^SGPSC", "=", "%s%s", "Nmea/Urc", "off"); + return 0; + } + _engine = true; + _at.at_cmd_discard("^SGPSC", "=", "%s%s", "Nmea/Urc", "on"); + _at.clear_error(); + _at.unlock(); + + return 1; +} + +void GEMALTO_CINTERION_CellularStack::stopGNSS() { + if(_engine) { + _at.lock(); + _at.at_cmd_discard("^SGPSC", "=", "%s%s", "Nmea/Urc", "off"); + _at.at_cmd_discard("^SGPSC", "=", "%s%d", "Engine", 0); + _at.clear_error(); + _at.unlock(); + _engine = false; + } +} + +void GEMALTO_CINTERION_CellularStack::setGNSS_PSM(bool const enable) { + if(_engine) { + _at.lock(); + _at.at_cmd_discard("^SGPSC", "=", "%s%d", "Power/Psm", enable); + _at.clear_error(); + _at.unlock(); + } +} + nsapi_error_t GEMALTO_CINTERION_CellularStack::socket_stack_init() { _at.lock(); @@ -121,6 +213,9 @@ nsapi_error_t GEMALTO_CINTERION_CellularStack::socket_stack_init() _at.set_urc_handler("^SIS:", mbed::Callback(this, &GEMALTO_CINTERION_CellularStack::urc_sis)); _at.set_urc_handler("^SISW:", mbed::Callback(this, &GEMALTO_CINTERION_CellularStack::urc_sisw)); _at.set_urc_handler("^SISR:", mbed::Callback(this, &GEMALTO_CINTERION_CellularStack::urc_sisr)); + _at.set_urc_handler("^SYSSTART", mbed::Callback(this, &GEMALTO_CINTERION_CellularStack::urc_sysstart)); + _at.set_urc_handler("^SGPSE", mbed::Callback(this, &GEMALTO_CINTERION_CellularStack::urc_gnss)); + _at.set_urc_handler("$G", mbed::Callback(this, &GEMALTO_CINTERION_CellularStack::urc_gnss)); } else { // recovery cleanup // close all Internet and connection profiles for (int i = 0; i < _device.get_property(AT_CellularDevice::PROPERTY_SOCKET_COUNT); i++) { @@ -151,6 +246,46 @@ nsapi_error_t GEMALTO_CINTERION_CellularStack::socket_close_impl(int sock_id) return _at.get_last_error(); } +#ifdef MBED_CONF_CELLULAR_OFFLOAD_DNS_QUERIES +nsapi_error_t GEMALTO_CINTERION_CellularStack::gethostbyname(const char *host, SocketAddress *address, + nsapi_version_t version, const char *interface_name) +{ + (void) interface_name; + MBED_ASSERT(host); + MBED_ASSERT(address); + + _at.lock(); + + if (_dns_callback) { + _at.unlock(); + return NSAPI_ERROR_BUSY; + } + + if (!address->set_ip_address(host)) { + //_at.set_at_timeout(1min); + _at.cmd_start_stop("^SISX" , "=" , "%s%d%s", "HostByName" , _cid, host); + _at.resp_start("^SISX: \"HostByName\","); + char ipAddress[NSAPI_IP_SIZE]; + int size = _at.read_string(ipAddress, sizeof(ipAddress)); + if (size > 0) { + //Valid string received + tr_info("Read %d bytes. Valid string: %s\n", size, ipAddress); + _at.restore_at_timeout(); + if (!address->set_ip_address(ipAddress)) { + _at.unlock(); + return NSAPI_ERROR_DNS_FAILURE; + } + } else { + //Null string received + tr_info("Read %d bytes. Null string\n", size); + return NSAPI_ERROR_NO_ADDRESS; + } + } + + return _at.unlock_return_error(); +} +#endif + nsapi_error_t GEMALTO_CINTERION_CellularStack::socket_open_defer(CellularSocket *socket, const SocketAddress *address) { int retry_open = 1; @@ -159,6 +294,7 @@ nsapi_error_t GEMALTO_CINTERION_CellularStack::socket_open_defer(CellularSocket int internet_service_id = find_socket_index(socket); bool foundSrvType = false; bool foundConIdType = false; + _at.cmd_start_stop("^SISS", "?"); _at.resp_start("^SISS:"); /* @@ -393,6 +529,7 @@ nsapi_size_or_error_t GEMALTO_CINTERION_CellularStack::socket_recvfrom_impl(Cell size = UDP_PACKET_SIZE; } + tr_info("requesting %d bytes\n", size); _at.cmd_start_stop("^SISR", "=", "%d%d", socket->id, size); sisr_retry: @@ -421,26 +558,63 @@ nsapi_size_or_error_t GEMALTO_CINTERION_CellularStack::socket_recvfrom_impl(Cell return NSAPI_ERROR_WOULD_BLOCK; } if (len == -1) { + if (GEMALTO_CINTERION::get_module() == GEMALTO_CINTERION::ModuleTX62 && _at.get_last_read_error() == -2) { + _at.process_oob(); + tr_error("Socket %d recvfrom finished!", socket->id); + socket->pending_bytes = 0; + return NSAPI_ERROR_OK; + } tr_error("Socket %d recvfrom failed!", socket->id); return NSAPI_ERROR_DEVICE_ERROR; } - socket->pending_bytes = 0; if (len >= (nsapi_size_or_error_t)size) { len = (nsapi_size_or_error_t)size; - int remain_len = _at.read_int(); - if (remain_len > 0) { - socket->pending_bytes = 1; - } } // UDP Udp_RemClient if (socket->proto == NSAPI_UDP && GEMALTO_CINTERION::get_module() != GEMALTO_CINTERION::ModuleBGS2) { - char ip_address[NSAPI_IPv6_SIZE + sizeof("[]:12345") - 1 + 1]; - int ip_len = _at.read_string(ip_address, sizeof(ip_address)); - if (ip_len <= 0) { - tr_error("Socket %d recvfrom addr (len %d)", socket->id, ip_len); - return NSAPI_ERROR_DEVICE_ERROR; - } + size_t ip_address_len = NSAPI_IPv6_SIZE + sizeof("[]:12345") - 1 + 1; + char ip_address[ip_address_len]; + + if (GEMALTO_CINTERION::get_module() == GEMALTO_CINTERION::ModuleTX62) { + // Local buffer for parsing Udp_RemClient for TX62 + uint8_t at_buf[ip_address_len]; + size_t ip_len = 0; + + // Skip + nsapi_size_or_error_t rem_len = _at.read_int(); + + // Wait for full in the _at buffer + do { + int len = _at.read_bytes(at_buf + ip_len, 1); + if (len <= 0) { + tr_error("Socket %d recvfrom addr (len %d)", socket->id, ip_len); + return NSAPI_ERROR_DEVICE_ERROR; + } + ip_len += len; + } while (ip_len < ip_address_len && at_buf[ip_len - 2] != '\r' && at_buf[ip_len - 1] != '\n'); + + // if (ip_len < sizeof("0.0.0.0:0")) { + if (ip_len < sizeof("[]:0")) { + tr_error("Socket %d has no address", socket->id); + goto sisr_retry; + } + + // at_buf contains remote client IP information + // in the format ":"\r\n. + + // Terminate the C string at the closing quotation mark + at_buf[ip_len - 3] = '\0'; + // Skip the opening quotation mark + memcpy(ip_address, at_buf + 1, ip_len - 4); + tr_info("ip_address %s (%d)", ip_address, ip_len - 4); + } else { + int ip_len = _at.read_string(ip_address, sizeof(ip_address)); + if (ip_len <= 0) { + tr_error("Socket %d recvfrom addr (len %d)", socket->id, ip_len); + return NSAPI_ERROR_DEVICE_ERROR; + } + } if (address) { char *ip_start = ip_address; char *ip_stop; @@ -473,6 +647,10 @@ nsapi_size_or_error_t GEMALTO_CINTERION_CellularStack::socket_recvfrom_impl(Cell nsapi_size_or_error_t recv_len = _at.read_bytes((uint8_t *)buffer, len); + if (recv_len < len) { + goto sisr_retry; + } + _at.resp_stop(); return (_at.get_last_error() == NSAPI_ERROR_OK) ? (recv_len ? recv_len : NSAPI_ERROR_WOULD_BLOCK) : NSAPI_ERROR_DEVICE_ERROR; diff --git a/connectivity/drivers/cellular/GEMALTO/COMPONENT_GEMALTO_CINTERION/GEMALTO_CINTERION_CellularStack.h b/connectivity/drivers/cellular/GEMALTO/COMPONENT_GEMALTO_CINTERION/GEMALTO_CINTERION_CellularStack.h index 01c5b71c652..31eee6b945f 100644 --- a/connectivity/drivers/cellular/GEMALTO/COMPONENT_GEMALTO_CINTERION/GEMALTO_CINTERION_CellularStack.h +++ b/connectivity/drivers/cellular/GEMALTO/COMPONENT_GEMALTO_CINTERION/GEMALTO_CINTERION_CellularStack.h @@ -34,6 +34,21 @@ class GEMALTO_CINTERION_CellularStack : public AT_CellularStack { */ nsapi_error_t socket_stack_init(); + /** + * @brief Enable GNSS output, if supported by the Cinterion module + * + * @param gnss_cb Callback which will be called when a new GNSS sentence is received + */ + void beginGNSS(mbed::Callback gnss_cb); + + void enableCmux(); + void endGNSS(); + int startGNSS(); + void stopGNSS(); + + /// Set whether Power Save Mode is enabled for the GNSS in the Cinterion module + void setGNSS_PSM(bool enable); + protected: virtual nsapi_error_t socket_close_impl(int sock_id); @@ -48,13 +63,19 @@ class GEMALTO_CINTERION_CellularStack : public AT_CellularStack { virtual nsapi_error_t socket_connect(nsapi_socket_t handle, const SocketAddress &address); +#ifdef MBED_CONF_CELLULAR_OFFLOAD_DNS_QUERIES + virtual nsapi_error_t gethostbyname(const char *host, SocketAddress *address, nsapi_version_t version, const char *interface_name); +#endif + private: // socket URC handlers as per Cinterion AT manuals void urc_sis(); void urc_sisw(); + void urc_sysstart(); void sisw_urc_handler(int sock_id, int urc_code); void urc_sisr(); void sisr_urc_handler(int sock_id, int urc_code); + void urc_gnss(); // sockets need a connection profile, one profile is enough to support single stack sockets nsapi_error_t create_connection_profile(int connection_profile_id); @@ -67,6 +88,14 @@ class GEMALTO_CINTERION_CellularStack : public AT_CellularStack { const char *_apn; const char *_user; const char *_password; + bool _engine; + + mbed::Callback _gnss_cb; + +#ifdef MBED_CONF_CELLULAR_OFFLOAD_DNS_QUERIES + hostbyname_cb_t _dns_callback; + nsapi_version_t _dns_version; +#endif }; } // namespace mbed diff --git a/targets/drivers.json5 b/targets/drivers.json5 index fe8a148ea5e..bde03a69640 100644 --- a/targets/drivers.json5 +++ b/targets/drivers.json5 @@ -74,8 +74,8 @@ "friendly_name": "STMod Cellular Modules" }, "COMPONENT_GEMALTO_CINTERION": { - "description": "Gemalto Cinterion family of cellular modules (ELS61, BGS2, EMS31, and EHS5-E are supported)", - "friendly_name": "Gemalto Cinterion" + "description": "Telit Cinterion (formerly Thales Cinterion, formerly Gemalto Cinterion) family of cellular modules (TX62, ELS61, BGS2, EMS31, and EHS5-E are supported)", + "friendly_name": "Telit/Thales/Gemalto Cinterion" }, "COMPONENT_GENERIC_AT3GPP": { "description": "Generic cellular module supporting 3GPP AT command set",