Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
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 }
Generated on Tue Aug 9 2022 00:37:08 by
1.7.2