takashi kadono / Mbed OS Nucleo_446

Dependencies:   ssd1331

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers GEMALTO_CINTERION_CellularStack.cpp Source File

GEMALTO_CINTERION_CellularStack.cpp

00001 /*
00002  * Copyright (c) 2018, Arm Limited and affiliates.
00003  * SPDX-License-Identifier: Apache-2.0
00004  *
00005  * Licensed under the Apache License, Version 2.0 (the "License");
00006  * you may not use this file except in compliance with the License.
00007  * You may obtain a copy of the License at
00008  *
00009  *     http://www.apache.org/licenses/LICENSE-2.0
00010  *
00011  * Unless required by applicable law or agreed to in writing, software
00012  * distributed under the License is distributed on an "AS IS" BASIS,
00013  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00014  * See the License for the specific language governing permissions and
00015  * limitations under the License.
00016  */
00017 
00018 #include <cstdlib>
00019 #include "GEMALTO_CINTERION_CellularStack.h"
00020 #include "GEMALTO_CINTERION_Module.h"
00021 #include "CellularLog.h"
00022 
00023 // defines as per ELS61-E2_ATC_V01.000 and BGS2-W_ATC_V00.100
00024 #define SOCKET_MAX 10
00025 #define UDP_PACKET_SIZE 1460
00026 #define FAILURE_TIMEOUT (30*1000) // failure timeout in milliseconds on modem side
00027 
00028 /*
00029  * Use connection profile 0 and Internet service profiles starting from 0 for sockets.
00030  */
00031 #define CONNECTION_PROFILE_ID 0
00032 
00033 using namespace mbed;
00034 
00035 GEMALTO_CINTERION_CellularStack::GEMALTO_CINTERION_CellularStack(ATHandler &atHandler, const char *apn,
00036                                                                  int cid, nsapi_ip_stack_t stack_type) : AT_CellularStack(atHandler, cid, stack_type), _apn(apn)
00037 {
00038 }
00039 
00040 GEMALTO_CINTERION_CellularStack::~GEMALTO_CINTERION_CellularStack()
00041 {
00042 }
00043 
00044 GEMALTO_CINTERION_CellularStack::CellularSocket *GEMALTO_CINTERION_CellularStack::find_socket(int sock_id)
00045 {
00046     CellularSocket *sock = NULL;
00047     for (int i = 0; i < SOCKET_MAX; i++) {
00048         if (_socket[i] && _socket[i]->id == sock_id) {
00049             sock = _socket[i];
00050             break;
00051         }
00052     }
00053     if (!sock) {
00054         tr_error("Socket not found %d", sock_id);
00055     }
00056     return sock;
00057 }
00058 
00059 void GEMALTO_CINTERION_CellularStack::urc_sis()
00060 {
00061     int sock_id = _at.read_int();
00062     int urc_code = _at.read_int();
00063     CellularSocket *sock = find_socket(sock_id);
00064     if (sock) {
00065         if (urc_code == 5) { // data available
00066             if (sock->_cb) {
00067                 sock->started = true;
00068                 sock->tx_ready = true;
00069                 sock->_cb(sock->_data);
00070             }
00071         } else if (urc_code == 2) { // socket closed
00072             sock->created = false;
00073         }
00074     }
00075 }
00076 
00077 void GEMALTO_CINTERION_CellularStack::urc_sisw()
00078 {
00079     int sock_id = _at.read_int();
00080     int urc_code = _at.read_int();
00081     CellularSocket *sock = find_socket(sock_id);
00082     if (sock) {
00083         if (urc_code == 1) { // ready
00084             if (sock->_cb) {
00085                 sock->tx_ready = true;
00086                 if (GEMALTO_CINTERION_Module::get_model() == GEMALTO_CINTERION_Module::ModelBGS2) {
00087                     sock->started = true;
00088                 }
00089                 sock->_cb(sock->_data);
00090             }
00091         } else if (urc_code == 2) { // socket closed
00092             sock->created = false;
00093         }
00094     }
00095 }
00096 
00097 void GEMALTO_CINTERION_CellularStack::urc_sisr()
00098 {
00099     int sock_id = _at.read_int();
00100     int urc_code = _at.read_int();
00101     CellularSocket *sock = find_socket(sock_id);
00102     if (sock) {
00103         if (urc_code == 1) { // data available
00104             if (sock->_cb) {
00105                 sock->rx_avail = true;
00106                 sock->_cb(sock->_data);
00107             }
00108         } else if (urc_code == 2) { // socket closed
00109             sock->created = false;
00110         }
00111     }
00112 }
00113 
00114 nsapi_error_t GEMALTO_CINTERION_CellularStack::socket_stack_init()
00115 {
00116     _at.lock();
00117     if (create_connection_profile()) {
00118         _at.set_urc_handler("^SIS:", mbed::Callback<void()>(this, &GEMALTO_CINTERION_CellularStack::urc_sis));
00119         _at.set_urc_handler("^SISW:", mbed::Callback<void()>(this, &GEMALTO_CINTERION_CellularStack::urc_sisw));
00120         _at.set_urc_handler("^SISR:", mbed::Callback<void()>(this, &GEMALTO_CINTERION_CellularStack::urc_sisr));
00121     }
00122     return _at.unlock_return_error();
00123 }
00124 
00125 int GEMALTO_CINTERION_CellularStack::get_max_socket_count()
00126 {
00127     return SOCKET_MAX;
00128 }
00129 
00130 bool GEMALTO_CINTERION_CellularStack::is_protocol_supported(nsapi_protocol_t protocol)
00131 {
00132     return (protocol == NSAPI_UDP );
00133 }
00134 
00135 nsapi_error_t GEMALTO_CINTERION_CellularStack::socket_close_impl(int sock_id)
00136 {
00137     CellularSocket *socket = find_socket(sock_id);
00138     if (!socket) {
00139         return NSAPI_ERROR_NO_SOCKET ;
00140     }
00141     _at.set_at_timeout(FAILURE_TIMEOUT);
00142     _at.cmd_start("AT^SISC=");
00143     _at.write_int(sock_id);
00144     _at.cmd_stop();
00145     _at.resp_start();
00146     _at.resp_stop();
00147     _at.restore_at_timeout();
00148 
00149     socket->started = false;
00150     socket->created = false;
00151     socket->tx_ready = false;
00152     socket->rx_avail = false;
00153 
00154     tr_debug("Closed socket %d (err %d)", sock_id, _at.get_last_error());
00155     return _at.get_last_error();
00156 }
00157 
00158 nsapi_error_t GEMALTO_CINTERION_CellularStack::socket_open_defer(CellularSocket *socket, const SocketAddress *address)
00159 {
00160     // host address (IPv4) and local+remote port is needed only for BGS2 which does not support UDP server socket
00161     char sock_addr[sizeof("sockudp://") - 1 + NSAPI_IPv4_SIZE + sizeof(":12345;port=12345") - 1 + 1];
00162     if (GEMALTO_CINTERION_Module::get_model() != GEMALTO_CINTERION_Module::ModelBGS2) {
00163         std::sprintf(sock_addr, "sockudp://%s:%u", address ? address->get_ip_address() : "", socket->localAddress.get_port());
00164     } else {
00165         std::sprintf(sock_addr, "sockudp://%s:%u;port=%u", address->get_ip_address(), address->get_port(), socket->localAddress.get_port());
00166     }
00167 
00168     _at.cmd_start("AT^SISS=");
00169     _at.write_int(socket->id);
00170     _at.write_string("address", false);
00171     _at.write_string(sock_addr);
00172     _at.cmd_stop();
00173     _at.resp_start();
00174     _at.resp_stop();
00175 
00176     _at.cmd_start("AT^SISO=");
00177     _at.write_int(socket->id);
00178     _at.cmd_stop();
00179     _at.resp_start();
00180     _at.resp_stop();
00181     if (_at.get_last_error()) {
00182         tr_error("Socket %d open failed!", socket->id);
00183         _at.clear_error();
00184         socket_close_impl(socket->id); // socket may already be open on modem if app and modem are not in sync, as a recovery, try to close the socket so open succeeds the next time
00185         return NSAPI_ERROR_NO_SOCKET ;
00186     }
00187 
00188     socket->created = true;
00189     tr_debug("Socket %d created (err %d)", socket->id, _at.get_last_error());
00190 
00191     return _at.get_last_error();
00192 }
00193 
00194 // To open socket:
00195 // 1. Select URC mode or polling mode with AT^SCFG
00196 // 2. create a GPRS connection profile with AT^SICS (must have PDP)
00197 // 3. create service profile with AT^SISS and map connectionID to serviceID
00198 // 4. open internet session with AT^SISO (ELS61 tries to attach to a packet domain)
00199 nsapi_error_t GEMALTO_CINTERION_CellularStack::create_socket_impl(CellularSocket *socket)
00200 {
00201     int connection_profile_id = CONNECTION_PROFILE_ID;
00202 
00203     // setup internet session profile
00204     int internet_service_id = socket->id;
00205     bool foundSrvType = false;
00206     bool foundConIdType = false;
00207     _at.cmd_start("AT^SISS?");
00208     _at.cmd_stop();
00209     _at.resp_start("^SISS:");
00210     /*
00211      * Profile is a list of tag-value map:
00212      * ^SISS: <srvProfileId>, <srvParmTag>, <srvParmValue>
00213      * [^SISS: ...]
00214      */
00215     while (_at.info_resp()) {
00216         int id = _at.read_int();
00217         if (id == internet_service_id) {
00218             char paramTag[16];
00219             int paramTagLen = _at.read_string(paramTag, sizeof(paramTag));
00220             if (paramTagLen > 0) {
00221                 char paramValue[100 + 1]; // APN may be up to 100 chars
00222                 int paramValueLen = _at.read_string(paramValue, sizeof(paramValue));
00223                 if (paramValueLen >= 0) {
00224                     if (strcmp(paramTag, "srvType") == 0) {
00225                         if (strcmp(paramValue, "Socket") == 0) {
00226                             tr_debug("srvType %s", paramValue);
00227                             foundSrvType = true;
00228                         }
00229                     }
00230                     if (strcmp(paramTag, "address") == 0) {
00231                         if (strncmp(paramValue, "sock", sizeof("sock")) == 0) {
00232                             tr_debug("address %s", paramValue);
00233                             foundSrvType = true;
00234                         }
00235                     }
00236                     if (strcmp(paramTag, "conId") == 0) {
00237                         char buf[10];
00238                         std::sprintf(buf, "%d", connection_profile_id);
00239                         tr_debug("conId %s", paramValue);
00240                         if (strcmp(paramValue, buf) == 0) {
00241                             foundConIdType = true;
00242                         }
00243                     }
00244                 }
00245             }
00246         }
00247     }
00248     _at.resp_stop();
00249 
00250     if (!foundSrvType) {
00251         _at.cmd_start("AT^SISS=");
00252         _at.write_int(internet_service_id);
00253         _at.write_string("srvType");
00254         _at.write_string("Socket");
00255         _at.cmd_stop();
00256         _at.resp_start();
00257         _at.resp_stop();
00258     }
00259 
00260     if (!foundConIdType) {
00261         _at.cmd_start("AT^SISS=");
00262         _at.write_int(internet_service_id);
00263         _at.write_string("conId");
00264         _at.write_int(connection_profile_id);
00265         _at.cmd_stop();
00266         _at.resp_start();
00267         _at.resp_stop();
00268     }
00269 
00270     tr_debug("Internet service %d created (err %d)", internet_service_id, _at.get_last_error());
00271 
00272     if (GEMALTO_CINTERION_Module::get_model() != GEMALTO_CINTERION_Module::ModelBGS2) {
00273         return socket_open_defer(socket);
00274     }
00275     return _at.get_last_error();
00276 }
00277 
00278 nsapi_size_or_error_t GEMALTO_CINTERION_CellularStack::socket_sendto_impl(CellularSocket *socket,
00279                                                                           const SocketAddress &address, const void *data, nsapi_size_t size)
00280 {
00281     tr_debug("Socket %d, sendto %s, len %d", socket->id, address.get_ip_address(), size);
00282 
00283     int ip_version = address.get_ip_version();
00284     if ((ip_version == NSAPI_IPv4  && _stack_type != IPV4_STACK) ||
00285             (ip_version == NSAPI_IPv6  && _stack_type != IPV6_STACK)) {
00286         tr_warn("No IP route for %s", address.get_ip_address());
00287         return NSAPI_ERROR_NO_SOCKET ;
00288     }
00289     if (GEMALTO_CINTERION_Module::get_model() == GEMALTO_CINTERION_Module::ModelBGS2) {
00290         tr_error("Send addr %s, prev addr %s", address.get_ip_address(), socket->remoteAddress.get_ip_address());
00291         if (address != socket->remoteAddress) {
00292             if (socket->started) {
00293                 socket_close_impl(socket->id);
00294                 _at.clear_error();
00295             }
00296 
00297             if (socket_open_defer(socket, &address) != NSAPI_ERROR_OK ) {
00298                 tr_error("Failed to open socket %d", socket->id);
00299                 return NSAPI_ERROR_NO_SOCKET ;
00300             }
00301             socket->remoteAddress = address;
00302             _at.resp_start("^SISW:");
00303             int sock_id = _at.read_int();
00304             int urc_code = _at.read_int();
00305             tr_debug("TX ready: socket=%d, urc=%d (err=%d)", sock_id, urc_code, _at.get_last_error());
00306             (void)sock_id;
00307             (void)urc_code;
00308             socket->created = true;
00309             socket->started = true;
00310             socket->tx_ready = true;
00311         }
00312     }
00313     if (!socket->started || !socket->tx_ready) {
00314         tr_debug("Socket %d would block (started %d, tx %d)", socket->id, socket->started, socket->tx_ready);
00315         return NSAPI_ERROR_WOULD_BLOCK ;
00316     }
00317 
00318     if (size > UDP_PACKET_SIZE) {
00319         tr_warn("Sending UDP packet size %d (max %d)", size, UDP_PACKET_SIZE);
00320         size = UDP_PACKET_SIZE;
00321     }
00322 
00323     _at.set_at_timeout(FAILURE_TIMEOUT);
00324     _at.cmd_start("AT^SISW=");
00325     _at.write_int(socket->id);
00326     _at.write_int(size);
00327     if (GEMALTO_CINTERION_Module::get_model() != GEMALTO_CINTERION_Module::ModelBGS2) {
00328         _at.write_int(0);
00329         char socket_address[NSAPI_IPv6_SIZE + sizeof("[]:12345") - 1 + 1];
00330         if (address.get_ip_version() == NSAPI_IPv4 ) {
00331             std::sprintf(socket_address, "%s:%u", address.get_ip_address(), address.get_port());
00332         } else {
00333             std::sprintf(socket_address, "[%s]:%u", address.get_ip_address(), address.get_port());
00334         }
00335         _at.write_string(socket_address);
00336     }
00337     _at.cmd_stop();
00338 
00339 sisw_retry:
00340     _at.resp_start("^SISW:");
00341     if (!_at.info_resp()) {
00342         tr_error("Socket %d send failure", socket->id);
00343         _at.restore_at_timeout();
00344         return NSAPI_ERROR_DEVICE_ERROR ;
00345     }
00346     _at.restore_at_timeout();
00347     int socket_id = _at.read_int();
00348     if (socket_id != socket->id) {
00349         tr_error("Socket id %d != %d", socket_id, socket->id);
00350         return NSAPI_ERROR_DEVICE_ERROR ;
00351     }
00352     int accept_len = _at.read_int();
00353     if (accept_len == -1) {
00354         tr_error("Socket %d send failed", socket->id);
00355         return NSAPI_ERROR_DEVICE_ERROR ;
00356     }
00357     int unack_len = _at.read_int();
00358     if (unack_len != 0) {
00359         tr_warn("Socket %d unack_len %d", socket->id, unack_len);
00360         if (GEMALTO_CINTERION_Module::get_model() != GEMALTO_CINTERION_Module::ModelBGS2) {
00361             // assume that an URC was received when unackData is not received
00362             _at.resp_stop();
00363             goto sisw_retry;
00364         }
00365     }
00366 
00367     _at.write_bytes((uint8_t *)data, accept_len);
00368     _at.resp_stop();
00369 
00370     tr_debug("Socket %d sendto %s, %d bytes (err %d)", socket->id, address.get_ip_address(), accept_len, _at.get_last_error());
00371 
00372     if (_at.get_last_error() == NSAPI_ERROR_OK ) {
00373         socket->tx_ready = false;
00374     }
00375 
00376     return (_at.get_last_error() == NSAPI_ERROR_OK ) ? accept_len : NSAPI_ERROR_DEVICE_ERROR ;
00377 }
00378 
00379 nsapi_size_or_error_t GEMALTO_CINTERION_CellularStack::socket_recvfrom_impl(CellularSocket *socket, SocketAddress *address,
00380                                                                             void *buffer, nsapi_size_t size)
00381 {
00382     tr_debug("Socket %d recvfrom %d bytes", socket->id, size);
00383 
00384     if (size > UDP_PACKET_SIZE) {
00385         tr_debug("Socket recvfrom size %d > %d", size, UDP_PACKET_SIZE);
00386         size = UDP_PACKET_SIZE;
00387     }
00388 
00389     if (!socket->rx_avail) {
00390         _at.process_oob(); // check for ^SISR URC
00391         if (!socket->rx_avail) {
00392             tr_debug("Socket %d would block", socket->id);
00393             return NSAPI_ERROR_WOULD_BLOCK ;
00394         }
00395     }
00396 
00397     _at.cmd_start("AT^SISR=");
00398     _at.write_int(socket->id);
00399     _at.write_int(size);
00400     _at.cmd_stop();
00401 
00402     _at.resp_start("^SISR:");
00403     if (!_at.info_resp()) {
00404         tr_error("Socket %d not responding", socket->id);
00405         return NSAPI_ERROR_DEVICE_ERROR ;
00406     }
00407     int socket_id = _at.read_int();
00408     if (socket_id != socket->id) {
00409         tr_error("Socket recvfrom id %d != %d", socket_id, socket->id);
00410         return NSAPI_ERROR_DEVICE_ERROR ;
00411     }
00412     nsapi_size_or_error_t len = _at.read_int();
00413     if (len == 0) {
00414         tr_warn("Socket %d no data", socket->id);
00415         _at.resp_stop();
00416         return NSAPI_ERROR_WOULD_BLOCK ;
00417     }
00418     if (len == -1) {
00419         tr_error("Socket %d recvfrom failed!", socket->id);
00420         return NSAPI_ERROR_DEVICE_ERROR ;
00421     }
00422     socket->rx_avail = false;
00423     if (len >= (nsapi_size_or_error_t)size) {
00424         int remain_len = _at.read_int();
00425         if (remain_len > 0) {
00426             socket->rx_avail = true;
00427         }
00428     }
00429     if (GEMALTO_CINTERION_Module::get_model() != GEMALTO_CINTERION_Module::ModelBGS2) {
00430         char ip_address[NSAPI_IPv6_SIZE + sizeof("[]:12345") - 1 + 1];
00431         int ip_len = _at.read_string(ip_address, sizeof(ip_address));
00432         if (ip_len <= 0) {
00433             tr_error("Socket %d recvfrom addr!", socket->id);
00434             return NSAPI_ERROR_DEVICE_ERROR ;
00435         }
00436         if (address) {
00437             char *ip_start = ip_address;
00438             char *ip_stop;
00439             char *port_start;
00440             if (_stack_type == IPV6_STACK) {
00441                 ip_start++; // skip '['
00442                 ip_stop = strchr(ip_address, ']');
00443                 if (ip_stop) {
00444                     port_start = strchr(ip_stop, ':');
00445                 }
00446             } else {
00447                 ip_stop = strchr(ip_address, ':');
00448                 port_start = ip_stop;
00449             }
00450             if (ip_stop && port_start) {
00451                 char tmp_ch = *ip_stop;
00452                 *ip_stop = '\0'; // split IP and port
00453                 address->set_ip_address(ip_start);
00454                 port_start++; // skip ':'
00455                 int port = std::strtol(port_start, NULL, 10);
00456                 address->set_port(port);
00457                 tr_debug("IP address %s:%d", address->get_ip_address(), address->get_port());
00458                 *ip_stop = tmp_ch; // restore original IP string
00459             }
00460         }
00461     } else {
00462         if (address) {
00463             *address = socket->remoteAddress;
00464         }
00465     }
00466 
00467     nsapi_size_or_error_t recv_len = _at.read_bytes((uint8_t *)buffer, len);
00468 
00469     _at.resp_stop();
00470 
00471     tr_debug("Socket %d, recvfrom %s, %d bytes (err %d)", socket->id, address, len, _at.get_last_error());
00472 
00473     return (_at.get_last_error() == NSAPI_ERROR_OK ) ? recv_len : NSAPI_ERROR_DEVICE_ERROR ;
00474 }
00475 
00476 // setup internet connection profile for sockets
00477 bool GEMALTO_CINTERION_CellularStack::create_connection_profile()
00478 {
00479     if (GEMALTO_CINTERION_Module::get_model() == GEMALTO_CINTERION_Module::ModelEMS31) {
00480         // EMS31 connection has only DNS settings and there is no need to modify those here for now
00481         return true;
00482     }
00483 
00484     char conParamType[12];
00485     std::sprintf(conParamType, "GPRS%d", (_stack_type == IPV4_STACK) ? 0 : 6);
00486     _at.cmd_start("AT^SICS?");
00487     _at.cmd_stop();
00488     bool foundConnection = false;
00489     bool foundAPN = false;
00490     int connection_profile_id = CONNECTION_PROFILE_ID;
00491     _at.resp_start("^SICS:");
00492     while (_at.info_resp()) {
00493         int id = _at.read_int();
00494         tr_debug("SICS %d", id);
00495         if (id == connection_profile_id) {
00496             char paramTag[16];
00497             int paramTagLen = _at.read_string(paramTag, sizeof(paramTag));
00498             if (paramTagLen > 0) {
00499                 tr_debug("paramTag %s", paramTag);
00500                 char paramValue[100 + 1]; // APN may be up to 100 chars
00501                 int paramValueLen = _at.read_string(paramValue, sizeof(paramValue));
00502                 if (paramValueLen >= 0) {
00503                     tr_debug("paramValue %s", paramValue);
00504                     if (strcmp(paramTag, "conType") == 0) {
00505                         tr_debug("conType %s", paramValue);
00506                         if (strcmp(paramValue, conParamType) == 0) {
00507                             foundConnection = true;
00508                         }
00509                     }
00510                     if (strcmp(paramTag, "apn") == 0) {
00511                         tr_debug("apn %s", paramValue);
00512                         if (strcmp(paramValue, _apn ? _apn : "") == 0) {
00513                             foundAPN = true;
00514                         }
00515                     }
00516                 }
00517             }
00518         }
00519     }
00520     _at.resp_stop();
00521 
00522     if (!foundConnection) {
00523         tr_debug("Socket conType %s", conParamType);
00524         _at.cmd_start("AT^SICS=");
00525         _at.write_int(connection_profile_id);
00526         _at.write_string("conType");
00527         _at.write_string(conParamType);
00528         _at.cmd_stop();
00529         _at.resp_start();
00530         _at.resp_stop();
00531     }
00532 
00533     if (!foundAPN && _apn) {
00534         tr_debug("Socket APN %s", _apn ? _apn : "");
00535         _at.cmd_start("AT^SICS=");
00536         _at.write_int(connection_profile_id);
00537         _at.write_string("apn");
00538         _at.write_string(_apn);
00539         _at.cmd_stop();
00540         _at.resp_start();
00541         _at.resp_stop();
00542     }
00543 
00544     // use URC mode
00545     _at.cmd_start("AT^SCFG=\"Tcp/withURCs\",\"on\"");
00546     _at.cmd_stop();
00547     _at.resp_start();
00548     _at.resp_stop();
00549 
00550     tr_debug("Connection profile %d created, stack_type %d (err %d)", connection_profile_id, _stack_type, _at.get_last_error());
00551     return _at.get_last_error() == NSAPI_ERROR_OK ;
00552 }