Curt Black / ublox-at-cellular-interface

Dependents:  

Fork of ublox-at-cellular-interface by u-blox

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers UbloxATCellularInterface.cpp Source File

UbloxATCellularInterface.cpp

00001 /* Copyright (c) 2017 ublox Limited
00002  *
00003  * Licensed under the Apache License, Version 2.0 (the "License");
00004  * you may not use this file except in compliance with the License.
00005  * You may obtain a copy of the License at
00006  *
00007  *     http://www.apache.org/licenses/LICENSE-2.0
00008  *
00009  * Unless required by applicable law or agreed to in writing, software
00010  * distributed under the License is distributed on an "AS IS" BASIS,
00011  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00012  * See the License for the specific language governing permissions and
00013  * limitations under the License.
00014  */
00015 
00016 #include "UbloxATCellularInterface.h"
00017 #include "mbed_poll.h"
00018 #include "nsapi.h"
00019 #include "APN_db.h"
00020 #ifdef FEATURE_COMMON_PAL
00021 #include "mbed_trace.h"
00022 #define TRACE_GROUP "UACI"
00023 #else
00024 #define tr_debug(format, ...) debug_if(_debug_trace_on, format "\n", ## __VA_ARGS__)
00025 #define tr_info(format, ...)  debug_if(_debug_trace_on, format "\n", ## __VA_ARGS__)
00026 #define tr_warn(format, ...)  debug_if(_debug_trace_on, format "\n", ## __VA_ARGS__)
00027 #define tr_error(format, ...) debug_if(_debug_trace_on, format "\n", ## __VA_ARGS__)
00028 #endif
00029 
00030 /**********************************************************************
00031  * PRIVATE METHODS
00032  **********************************************************************/
00033 
00034 // Event thread for asynchronous received data handling.
00035 void UbloxATCellularInterface::handle_event(){
00036     pollfh fhs;
00037     int count;
00038     int at_timeout;
00039 
00040     fhs.fh = _fh;
00041     fhs.events = POLLIN;
00042 
00043     while (_run_event_thread) {
00044         count = poll(&fhs, 1, 1000);
00045         if (count > 0 && (fhs.revents & POLLIN)) {
00046             LOCK();
00047             at_timeout = _at_timeout;
00048             at_set_timeout(10); // Avoid blocking but also make sure we don't
00049             // time out if we get ahead of the serial port
00050             _at->debug_on(false); // Debug here screws with the test output
00051             // Let the URCs run
00052             _at->recv(UNNATURAL_STRING);
00053             _at->debug_on(_debug_trace_on);
00054             at_set_timeout(at_timeout);
00055             UNLOCK();
00056         }
00057     }
00058 }
00059 
00060 // Find or create a socket from the list.
00061 UbloxATCellularInterface::SockCtrl * UbloxATCellularInterface::find_socket(int modem_handle)
00062 {
00063     UbloxATCellularInterface::SockCtrl *socket = NULL;
00064 
00065     for (unsigned int x = 0; (socket == NULL) && (x < sizeof(_sockets) / sizeof(_sockets[0])); x++) {
00066         if (_sockets[x].modem_handle == modem_handle) {
00067             socket = &(_sockets[x]);
00068         }
00069     }
00070 
00071     return socket;
00072 }
00073 
00074 // Clear out the storage for a socket
00075 void UbloxATCellularInterface::clear_socket(UbloxATCellularInterface::SockCtrl * socket)
00076 {
00077     if (socket != NULL) {
00078         socket->modem_handle = SOCKET_UNUSED;
00079         socket->pending     = 0;
00080         socket->callback    = NULL;
00081         socket->data        = NULL;
00082     }
00083 }
00084 
00085 // Check that a socket pointer is valid
00086 bool UbloxATCellularInterface::check_socket(SockCtrl * socket)
00087 {
00088     bool success = false;
00089 
00090     if (socket != NULL) {
00091         for (unsigned int x = 0; !success && (x < sizeof(_sockets) / sizeof(_sockets[0])); x++) {
00092             if (socket == &(_sockets[x])) {
00093                 success = true;
00094             }
00095         }
00096     }
00097 
00098     return success;
00099 }
00100 
00101 // Convert nsapi_security_t to the modem security numbers
00102 int UbloxATCellularInterface::nsapi_security_to_modem_security(nsapi_security_t nsapi_security)
00103 {
00104     int modem_security = 3;
00105 
00106     switch (nsapi_security)
00107     {
00108     case NSAPI_SECURITY_NONE:
00109         modem_security = 0;
00110         break;
00111     case NSAPI_SECURITY_PAP:
00112         modem_security = 1;
00113         break;
00114     case NSAPI_SECURITY_CHAP:
00115         modem_security = 2;
00116         break;
00117     case NSAPI_SECURITY_UNKNOWN:
00118         modem_security = 3;
00119         break;
00120     default:
00121         modem_security = 3;
00122         break;
00123     }
00124 
00125     return modem_security;
00126 }
00127 
00128 // Callback for Socket Read URC.
00129 void UbloxATCellularInterface::UUSORD_URC()
00130 {
00131     int a;
00132     int b;
00133     char buf[32];
00134     SockCtrl *socket;
00135 
00136     // Note: not calling _at->recv() from here as we're
00137     // already in an _at->recv()
00138     // +UUSORD: <socket>,<length>
00139     if (read_at_to_char(buf, sizeof (buf), '\n') > 0) {
00140         if (sscanf(buf, ": %d,%d", &a, &b) == 2) {
00141             socket = find_socket(a);
00142             if (socket != NULL) {
00143                 socket->pending = b;
00144                 // No debug prints here as they can affect timing
00145                 // and cause data loss in UARTSerial
00146                 if (socket->callback != NULL) {
00147                     socket->callback(socket->data);
00148                 }
00149             }
00150         }
00151     }
00152 }
00153 
00154 // Callback for Socket Read From URC.
00155 void UbloxATCellularInterface::UUSORF_URC()
00156 {
00157     int a;
00158     int b;
00159     char buf[32];
00160     SockCtrl *socket;
00161 
00162     // Note: not calling _at->recv() from here as we're
00163     // already in an _at->recv()
00164     // +UUSORF: <socket>,<length>
00165     if (read_at_to_char(buf, sizeof (buf), '\n') > 0) {
00166         if (sscanf(buf, ": %d,%d", &a, &b) == 2) {
00167             socket = find_socket(a);
00168             if (socket != NULL) {
00169                 socket->pending = b;
00170                 // No debug prints here as they can affect timing
00171                 // and cause data loss in UARTSerial
00172                 if (socket->callback != NULL) {
00173                     socket->callback(socket->data);
00174                 }
00175             }
00176         }
00177     }
00178 }
00179 
00180 // Callback for Socket Close URC.
00181 void UbloxATCellularInterface::UUSOCL_URC()
00182 {
00183     int a;
00184     char buf[32];
00185     SockCtrl *socket;
00186 
00187     // Note: not calling _at->recv() from here as we're
00188     // already in an _at->recv()
00189     // +UUSOCL: <socket>
00190     if (read_at_to_char(buf, sizeof (buf), '\n') > 0) {
00191         if (sscanf(buf, ": %d", &a) == 1) {
00192             socket = find_socket(a);
00193             tr_debug("Socket 0x%08x: handle %d closed by remote host",
00194                     (unsigned int) socket, a);
00195             clear_socket(socket);
00196         }
00197     }
00198 }
00199 
00200 // Callback for UUPSDD.
00201 void UbloxATCellularInterface::UUPSDD_URC()
00202 {
00203     int a;
00204     char buf[32];
00205     SockCtrl *socket;
00206 
00207     // Note: not calling _at->recv() from here as we're
00208     // already in an _at->recv()
00209     // +UUPSDD: <socket>
00210     if (read_at_to_char(buf, sizeof (buf), '\n') > 0) {
00211         if (sscanf(buf, ": %d", &a) == 1) {
00212             socket = find_socket(a);
00213             tr_debug("Socket 0x%08x: handle %d connection lost",
00214                     (unsigned int) socket, a);
00215             clear_socket(socket);
00216             if (_connection_status_cb) {
00217                 _connection_status_cb(NSAPI_ERROR_CONNECTION_LOST);
00218             }
00219         }
00220     }
00221 }
00222 
00223 /**********************************************************************
00224  * PROTECTED METHODS: GENERAL
00225  **********************************************************************/
00226 
00227 // Get the next set of credentials, based on IMSI.
00228 void UbloxATCellularInterface::get_next_credentials(const char ** config)
00229 {
00230     if (*config) {
00231         _apn    = _APN_GET(*config);
00232         _uname  = _APN_GET(*config);
00233         _pwd    = _APN_GET(*config);
00234     }
00235 
00236     _apn    = _apn     ?  _apn    : "";
00237     _uname  = _uname   ?  _uname  : "";
00238     _pwd    = _pwd     ?  _pwd    : "";
00239 }
00240 
00241 // Active a connection profile on board the modem.
00242 // Note: the AT interface should be locked before this is called.
00243 bool UbloxATCellularInterface::activate_profile(const char* apn,
00244         const char* username,
00245         const char* password,
00246         nsapi_security_t auth)
00247 {
00248     bool activated = false;
00249     bool success = false;
00250     int at_timeout = _at_timeout;
00251     SocketAddress address;
00252 
00253     // Set up the APN
00254     if (*apn) {
00255         success = _at->send("AT+UPSD=" PROFILE ",1,\"%s\"", apn) && _at->recv("OK");
00256     }
00257     if (success && *username) {
00258         success = _at->send("AT+UPSD=" PROFILE ",2,\"%s\"", username) && _at->recv("OK");
00259     }
00260     if (success && *password) {
00261         success = _at->send("AT+UPSD=" PROFILE ",3,\"%s\"", password) && _at->recv("OK");
00262     }
00263 
00264     if (success) {
00265         // Set up dynamic IP address assignment.
00266         success = _at->send("AT+UPSD=" PROFILE ",7,\"0.0.0.0\"") && _at->recv("OK");
00267         // Set up the authentication protocol
00268         // 0 = none
00269         // 1 = PAP (Password Authentication Protocol)
00270         // 2 = CHAP (Challenge Handshake Authentication Protocol)
00271         for (int protocol = nsapi_security_to_modem_security(NSAPI_SECURITY_NONE);
00272                 success && (protocol <= nsapi_security_to_modem_security(NSAPI_SECURITY_CHAP)); protocol++) {
00273             if ((_auth == NSAPI_SECURITY_UNKNOWN) || (nsapi_security_to_modem_security(_auth) == protocol)) {
00274                 if (_at->send("AT+UPSD=" PROFILE ",6,%d", protocol) && _at->recv("OK")) {
00275                     // Activate, waiting 30 seconds for the connection to be made
00276                     at_set_timeout(30000);
00277                     activated = _at->send("AT+UPSDA=" PROFILE ",3") && _at->recv("OK");
00278                     at_set_timeout(at_timeout);
00279                 }
00280             }
00281         }
00282     }
00283 
00284     return activated;
00285 }
00286 
00287 // Activate a profile by reusing an external PDP context.
00288 // Note: the AT interface should be locked before this is called.
00289 bool UbloxATCellularInterface::activate_profile_reuse_external(void)
00290 {
00291     bool success = false;
00292     int cid = -1;
00293     char ip[NSAPI_IP_SIZE];
00294     SocketAddress address;
00295     int t;
00296     int at_timeout = _at_timeout;
00297 
00298     //+CGDCONT: <cid>,"IP","<apn name>","<ip adr>",0,0,0,0,0,0
00299     if (_at->send("AT+CGDCONT?")) {
00300         if (_at->recv("+CGDCONT: %d,\"IP\",\"%*[^\"]\",\"%" u_stringify(NSAPI_IP_SIZE) "[^\"]\",%*d,%*d,%*d,%*d,%*d,%*d",
00301                 &t, ip) &&
00302                 _at->recv("OK")) {
00303             // Check if the IP address is valid
00304             if (address.set_ip_address(ip)) {
00305                 cid = t;
00306             }
00307         }
00308     }
00309 
00310     // If a context has been found, use it
00311     if ((cid != -1) && (_at->send("AT+UPSD=" PROFILE ",100,%d", cid) && _at->recv("OK"))) {
00312         // Activate, waiting 30 seconds for the connection to be made
00313         at_set_timeout(30000);
00314         success = _at->send("AT+UPSDA=" PROFILE ",3") && _at->recv("OK");
00315         at_set_timeout(at_timeout);
00316     }
00317 
00318     return success;
00319 }
00320 
00321 // Activate a profile by context ID.
00322 // Note: the AT interface should be locked before this is called.
00323 bool UbloxATCellularInterface::activate_profile_by_cid(int cid,
00324         const char* apn,
00325         const char* username,
00326         const char* password,
00327         nsapi_security_t auth)
00328 {
00329     bool success = false;
00330     int at_timeout = _at_timeout;
00331 
00332     // Must be detached to change CGDCONT
00333     if (_at->send("AT+CGATT=0") && _at->recv("OK")) {
00334         if (_at->send("AT+CGDCONT=%d,\"IP\",\"%s\"", cid, apn) && _at->recv("OK") &&
00335                 _at->send("AT+UAUTHREQ=%d,%d,\"%s\",\"%s\"", cid, nsapi_security_to_modem_security(auth),
00336                         username, password) && _at->recv("OK") &&
00337                         _at->send("AT+UPSD=" PROFILE ",100,%d", cid) && _at->recv("OK")) {
00338 
00339             // Wait 30 seconds for the connection to be made
00340             at_set_timeout(30000);
00341             // Activate the protocol
00342             success = _at->send("AT+UPSDA=" PROFILE ",3") && _at->recv("OK");
00343             at_set_timeout(at_timeout);
00344         }
00345     }
00346 
00347     return success;
00348 }
00349 
00350 // Connect the on board IP stack of the modem.
00351 bool UbloxATCellularInterface::connect_modem_stack()
00352 {
00353     bool success = false;
00354     int active = 0;
00355     const char * config = NULL;
00356     LOCK();
00357 
00358     // Check the profile
00359     if (_at->send("AT+UPSND=" PROFILE ",8") && _at->recv("+UPSND: %*d,%*d,%d\n", &active) &&
00360             _at->recv("OK")) {
00361         if (active == 0) {
00362             // If the caller hasn't entered an APN, try to find it
00363             if (_apn == NULL) {
00364                 config = apnconfig(_dev_info.imsi);
00365             }
00366 
00367             // Attempt to connect
00368             do {
00369                 // Set up APN and IP protocol for PDP context
00370                 get_next_credentials(&config);
00371                 _auth = (*_uname && *_pwd) ? _auth : NSAPI_SECURITY_NONE;
00372                 // TOBY-L2 / MPCI-L2 / LARA-R2 / TOBY-R2 mapped to <cid> via param tag 100
00373                 if ((_dev_info.dev != DEV_TOBY_L2) && (_dev_info.dev != DEV_MPCI_L2) && (_dev_info.dev != DEV_LARA_R2)) {
00374                     success = activate_profile(_apn, _uname, _pwd, _auth);
00375                 } else {
00376                     success = activate_profile_reuse_external();
00377                     if (success) {
00378                         tr_debug("Reusing external context");
00379                     } else {
00380                         success = activate_profile_by_cid(1, _apn, _uname, _pwd, _auth);
00381                     }
00382                 }
00383             } while (!success && config && *config);
00384         } else {
00385             // If the profile is already active, we're good
00386             success = true;
00387         }
00388     }
00389 
00390     if (!success) {
00391         tr_error("Failed to connect, check your APN/username/password");
00392     }
00393 
00394     UNLOCK();
00395     return success;
00396 }
00397 
00398 // Disconnect the on board IP stack of the modem.
00399 bool UbloxATCellularInterface::disconnect_modem_stack()
00400 {
00401     bool success = false;
00402     LOCK();
00403 
00404     if (get_ip_address() != NULL) {
00405         if (_at->send("AT+UPSDA=" PROFILE ",4") && _at->recv("OK")) {
00406             success = true;
00407             if (_connection_status_cb) {
00408                 _connection_status_cb(NSAPI_ERROR_CONNECTION_LOST);
00409             }
00410         }
00411     }
00412 
00413     UNLOCK();
00414     return success;
00415 }
00416 
00417 /**********************************************************************
00418  * PROTECTED METHODS: NETWORK INTERFACE and SOCKETS
00419  **********************************************************************/
00420 
00421 // Gain access to us.
00422 NetworkStack *UbloxATCellularInterface::get_stack()
00423 {
00424     return this;
00425 }
00426 
00427 // Create a socket.
00428 nsapi_error_t UbloxATCellularInterface::socket_open(nsapi_socket_t *handle,
00429         nsapi_protocol_t proto)
00430 {
00431     nsapi_error_t nsapi_error = NSAPI_ERROR_DEVICE_ERROR;
00432     bool success = false;
00433     int modem_handle;
00434     SockCtrl *socket;
00435     LOCK();
00436 
00437     // Find a free socket
00438     socket = find_socket();
00439     tr_debug("socket_open(%d)", proto);
00440 
00441     if (socket != NULL) {
00442         if (proto == NSAPI_UDP) {
00443             success = _at->send("AT+USOCR=17");
00444         } else if (proto == NSAPI_TCP) {
00445             success = _at->send("AT+USOCR=6");
00446         } else  {
00447             nsapi_error = NSAPI_ERROR_UNSUPPORTED;
00448         }
00449 
00450         if (success) {
00451             nsapi_error = NSAPI_ERROR_NO_SOCKET;
00452             if (_at->recv("+USOCR: %d\n", &modem_handle) && (modem_handle != SOCKET_UNUSED) &&
00453                     _at->recv("OK")) {
00454                 tr_debug("Socket 0x%8x: handle %d was created", (unsigned int) socket, modem_handle);
00455                 clear_socket(socket);
00456                 socket->modem_handle         = modem_handle;
00457                 *handle = (nsapi_socket_t) socket;
00458                 nsapi_error = NSAPI_ERROR_OK;
00459             }
00460         }
00461     } else {
00462         nsapi_error = NSAPI_ERROR_NO_MEMORY;
00463     }
00464 
00465     UNLOCK();
00466     return nsapi_error;
00467 }
00468 
00469 // Close a socket.
00470 nsapi_error_t UbloxATCellularInterface::socket_close(nsapi_socket_t handle)
00471 {
00472     nsapi_error_t nsapi_error = NSAPI_ERROR_DEVICE_ERROR;
00473     SockCtrl *socket = (SockCtrl *) handle;
00474     LOCK();
00475 
00476     tr_debug("socket_close(0x%08x)", (unsigned int) handle);
00477 
00478     MBED_ASSERT (check_socket(socket));
00479 
00480     if (_at->send("AT+USOCL=%d", socket->modem_handle) &&
00481             _at->recv("OK")) {
00482         clear_socket(socket);
00483         nsapi_error = NSAPI_ERROR_OK;
00484     }
00485 
00486     UNLOCK();
00487     return nsapi_error;
00488 }
00489 
00490 // Bind a local port to a socket.
00491 nsapi_error_t UbloxATCellularInterface::socket_bind(nsapi_socket_t handle,
00492         const SocketAddress &address)
00493 {
00494     nsapi_error_t nsapi_error = NSAPI_ERROR_NO_SOCKET;
00495     int proto;
00496     int modem_handle;
00497     SockCtrl savedSocket;
00498     SockCtrl *socket = (SockCtrl *) handle;
00499     LOCK();
00500 
00501     tr_debug("socket_bind(0x%08x, :%d)", (unsigned int) handle, address.get_port());
00502 
00503     MBED_ASSERT (check_socket(socket));
00504 
00505     // Query the socket type
00506     if (_at->send("AT+USOCTL=%d,0", socket->modem_handle) &&
00507             _at->recv("+USOCTL: %*d,0,%d\n", &proto) &&
00508             _at->recv("OK")) {
00509         savedSocket = *socket;
00510         nsapi_error = NSAPI_ERROR_DEVICE_ERROR;
00511         // Now close the socket and re-open it with the binding given
00512         if (_at->send("AT+USOCL=%d", socket->modem_handle) &&
00513                 _at->recv("OK")) {
00514             clear_socket(socket);
00515             nsapi_error = NSAPI_ERROR_CONNECTION_LOST;
00516             if (_at->send("AT+USOCR=%d,%d", proto, address.get_port()) &&
00517                     _at->recv("+USOCR: %d\n", &modem_handle) && (modem_handle != SOCKET_UNUSED) &&
00518                     _at->recv("OK")) {
00519                 *socket = savedSocket;
00520                 nsapi_error = NSAPI_ERROR_OK;
00521             }
00522         }
00523     }
00524 
00525     UNLOCK();
00526     return nsapi_error;
00527 }
00528 
00529 // Connect to a socket
00530 nsapi_error_t UbloxATCellularInterface::socket_connect(nsapi_socket_t handle,
00531         const SocketAddress &address)
00532 {
00533     nsapi_error_t nsapi_error = NSAPI_ERROR_DEVICE_ERROR;
00534     SockCtrl *socket = (SockCtrl *) handle;
00535     LOCK();
00536 
00537     tr_debug("socket_connect(0x%08x, %s(:%d))", (unsigned int) handle,
00538             address.get_ip_address(), address.get_port());
00539 
00540     MBED_ASSERT (check_socket(socket));
00541 
00542     if (_at->send("AT+USOCO=%d,\"%s\",%d", socket->modem_handle,
00543             address.get_ip_address(), address.get_port()) &&
00544             _at->recv("OK")) {
00545         nsapi_error = NSAPI_ERROR_OK;
00546     }
00547 
00548     UNLOCK();
00549     return nsapi_error;
00550 }
00551 
00552 // Send to a socket.
00553 nsapi_size_or_error_t UbloxATCellularInterface::socket_send(nsapi_socket_t handle,
00554         const void *data,
00555         nsapi_size_t size)
00556 {
00557     nsapi_size_or_error_t nsapi_error_size = NSAPI_ERROR_DEVICE_ERROR;
00558     bool success = true;
00559     const char *buf = (const char *) data;
00560     nsapi_size_t blk = MAX_WRITE_SIZE;
00561     nsapi_size_t count = size;
00562     SockCtrl *socket = (SockCtrl *) handle;
00563 
00564     tr_debug("socket_send(0x%08x, 0x%08x, %d)", (unsigned int) handle, (unsigned int) data, size);
00565 
00566     MBED_ASSERT (check_socket(socket));
00567 
00568     if (socket->modem_handle == SOCKET_UNUSED) {
00569         tr_debug("socket_send: socket closed");
00570         return NSAPI_ERROR_NO_SOCKET;
00571     }
00572 
00573     while ((count > 0) && success) {
00574         if (count < blk) {
00575             blk = count;
00576         }
00577         LOCK();
00578 
00579         if (_at->send("AT+USOWR=%d,%d", socket->modem_handle, blk) && _at->recv("@")) {
00580             wait_ms(50);
00581             if ((_at->write(buf, blk) < (int) blk) ||
00582                     !_at->recv("OK")) {
00583                 success = false;
00584             }
00585         } else {
00586             success = false;
00587         }
00588 
00589         UNLOCK();
00590         buf += blk;
00591         count -= blk;
00592     }
00593 
00594     if (success) {
00595         nsapi_error_size = size - count;
00596         if (_debug_trace_on) {
00597             tr_debug("socket_send: %d \"%*.*s\"", size, size, size, (char *) data);
00598         }
00599     }
00600 
00601     return nsapi_error_size;
00602 }
00603 
00604 // Send to an IP address.
00605 nsapi_size_or_error_t UbloxATCellularInterface::socket_sendto(nsapi_socket_t handle,
00606         const SocketAddress &address,
00607         const void *data,
00608         nsapi_size_t size)
00609 {
00610     nsapi_size_or_error_t nsapi_error_size = NSAPI_ERROR_DEVICE_ERROR;
00611     bool success = true;
00612     const char *buf = (const char *) data;
00613     nsapi_size_t blk = MAX_WRITE_SIZE;
00614     nsapi_size_t count = size;
00615     SockCtrl *socket = (SockCtrl *) handle;
00616 
00617     tr_debug("socket_sendto(0x%8x, %s(:%d), 0x%08x, %d)", (unsigned int) handle,
00618             address.get_ip_address(), address.get_port(), (unsigned int) data, size);
00619 
00620     MBED_ASSERT (check_socket(socket));
00621 
00622     if (size > MAX_WRITE_SIZE) {
00623         tr_warn("WARNING: packet length %d is too big for one UDP packet (max %d), will be fragmented.", size, MAX_WRITE_SIZE);
00624     }
00625 
00626     while ((count > 0) && success) {
00627         if (count < blk) {
00628             blk = count;
00629         }
00630         LOCK();
00631 
00632         if (_at->send("AT+USOST=%d,\"%s\",%d,%d", socket->modem_handle,
00633                 address.get_ip_address(), address.get_port(), blk) &&
00634                 _at->recv("@")) {
00635             wait_ms(50);
00636             if ((_at->write(buf, blk) >= (int) blk) &&
00637                     _at->recv("OK")) {
00638             } else {
00639                 success = false;
00640             }
00641         } else {
00642             success = false;
00643         }
00644 
00645         UNLOCK();
00646         buf += blk;
00647         count -= blk;
00648     }
00649 
00650     if (success) {
00651         nsapi_error_size = size - count;
00652         if (_debug_trace_on) {
00653             tr_debug("socket_sendto: %d \"%*.*s\"", size, size, size, (char *) data);
00654         }
00655     }
00656 
00657     return nsapi_error_size;
00658 }
00659 
00660 // Receive from a socket, TCP style.
00661 nsapi_size_or_error_t UbloxATCellularInterface::socket_recv(nsapi_socket_t handle,
00662         void *data,
00663         nsapi_size_t size)
00664 {
00665     nsapi_size_or_error_t nsapi_error_size = NSAPI_ERROR_DEVICE_ERROR;
00666     bool success = true;
00667     char *buf = (char *) data;
00668     nsapi_size_t read_blk;
00669     nsapi_size_t count = 0;
00670     unsigned int usord_sz;
00671     int read_sz;
00672     Timer timer;
00673     SockCtrl *socket = (SockCtrl *) handle;
00674     int at_timeout;
00675 
00676     tr_debug("socket_recv(0x%08x, 0x%08x, %d)",
00677             (unsigned int) handle, (unsigned int) data, size);
00678 
00679     MBED_ASSERT (check_socket(socket));
00680 
00681     if (socket->modem_handle == SOCKET_UNUSED) {
00682         tr_debug("socket_recv: socket closed");
00683         return NSAPI_ERROR_NO_SOCKET;
00684     }
00685 
00686     timer.start();
00687 
00688     while (success && (size > 0)) {
00689         LOCK();
00690         at_timeout = _at_timeout;
00691         at_set_timeout(1000);
00692 
00693         do {
00694 
00695 #ifdef USE_HEX_MODE_USORx
00696             if (_at->send("AT+UDCONF=1,1") && _at->recv("OK")) {
00697             } else {
00698                 success = false;
00699                 break;
00700             }
00701 #endif
00702             read_blk = MAX_READ_SIZE;
00703             if (read_blk > size) {
00704                 read_blk = size;
00705             }
00706             if (socket->pending > 0) {
00707                 tr_debug("Socket 0x%08x: modem handle %d has %d byte(s) pending",
00708                         (unsigned int) socket, socket->modem_handle, socket->pending);
00709                 _at->debug_on(false); // ABSOLUTELY no time for debug here if you want to
00710                 // be able to read packets of any size without
00711                 // losing characters in UARTSerial
00712                 if (_at->send("AT+USORD=%d,%d", socket->modem_handle, read_blk) &&
00713                         _at->recv("+USORD: %*d,%d,\"", &usord_sz)) {
00714                     // Must use what +USORD returns here as it may be less or more than we asked for
00715                     if (usord_sz > socket->pending) {
00716                         socket->pending = 0;
00717                     } else {
00718                         socket->pending -= usord_sz;
00719                     }
00720                     // Note: insert no debug between _at->recv() and _at->read(), no time...
00721                     if (usord_sz > size) {
00722                         usord_sz = size;
00723                     }
00724 #ifdef USE_HEX_MODE_USORx
00725                     read_sz = 0;
00726                     char asciiBytes[2];
00727                     while (success && (read_sz < usord_sz*2)) {
00728                         // Read the 2 bytes of ascii text for this byte of data
00729                         if (2 == _at->read(asciiBytes, 2)) {
00730                             read_sz += 2;
00731                             // convert to binary data and store in read buffer
00732                             sscanf(asciiBytes, "%2hhx", buf+(read_sz/2));
00733                         } else {
00734                             success = false;
00735                         }
00736                     }
00737 #else
00738                     read_sz = _at->read(buf, usorf_sz);
00739 #endif
00740                     if (read_sz > 0) {
00741                         tr_debug("...read %d byte(s) from modem handle %d...", read_sz,
00742                                 socket->modem_handle);
00743                         if (_debug_trace_on) {
00744                             tr_debug("Read returned %d,  |%*.*s|", read_sz, read_sz, read_sz, buf);
00745                         }
00746                         count += read_sz;
00747                         buf += read_sz;
00748                         size -= read_sz;
00749                     } else {
00750                         // read() should not fail
00751                         success = false;
00752                     }
00753                     tr_debug("Socket 0x%08x: modem handle %d now has only %d byte(s) pending",
00754                             (unsigned int) socket, socket->modem_handle, socket->pending);
00755                     // Wait for the "OK" before continuing
00756                     _at->recv("OK");
00757                 } else {
00758                     // Should never fail to do _at->send()/_at->recv()
00759                     success = false;
00760                 }
00761                 _at->debug_on(_debug_trace_on);
00762             } else if (timer.read_ms() < SOCKET_TIMEOUT) {
00763                 // Wait for URCs
00764                 _at->recv(UNNATURAL_STRING);
00765             } else {
00766                 if (count == 0) {
00767                     // Timeout with nothing received
00768                     nsapi_error_size = NSAPI_ERROR_WOULD_BLOCK;
00769                     success = false;
00770                 }
00771                 size = 0; // This simply to cause an exit
00772             }
00773 
00774         } while (0);  // Any fails that set success=false can simply break, avoids even worse nested if's
00775 
00776 #ifdef USE_HEX_MODE_USORx
00777         // Set back to non-hex mode
00778         if (_at->send("AT+UDCONF=1,0") && _at->recv("OK")) {
00779         } else {
00780             success = false;
00781             break;
00782         }
00783 #endif
00784 
00785         at_set_timeout(at_timeout);
00786         UNLOCK();
00787     }
00788     timer.stop();
00789 
00790     if (success) {
00791         nsapi_error_size = count;
00792     }
00793 
00794     if (_debug_trace_on) {
00795         tr_debug("socket_recv: %d \"%*.*s\"", count, count, count, buf - count);
00796     } else {
00797         tr_debug("socket_recv: received %d byte(s)", count);
00798     }
00799 
00800     return nsapi_error_size;
00801 }
00802 
00803 // Receive a packet over a UDP socket.
00804 nsapi_size_or_error_t UbloxATCellularInterface::socket_recvfrom(nsapi_socket_t handle,
00805         SocketAddress *address,
00806         void *data,
00807         nsapi_size_t size)
00808 {
00809     nsapi_size_or_error_t nsapi_error_size = NSAPI_ERROR_DEVICE_ERROR;
00810     bool success = true;
00811     char *buf = (char *) data;
00812     nsapi_size_t read_blk;
00813     nsapi_size_t count = 0;
00814     char ipAddress[NSAPI_IP_SIZE];
00815     int port;
00816     unsigned int usorf_sz;
00817     int read_sz;
00818     Timer timer;
00819     SockCtrl *socket = (SockCtrl *) handle;
00820     int at_timeout;
00821 
00822     tr_debug("socket_recvfrom(0x%08x, 0x%08x, %d)",
00823             (unsigned int) handle, (unsigned int) data, size);
00824 
00825     MBED_ASSERT (check_socket(socket));
00826 
00827     timer.start();
00828 
00829     while (success && (size > 0)) {
00830         LOCK();
00831         at_timeout = _at_timeout;
00832         at_set_timeout(1000);
00833 
00834         do {
00835 
00836 #ifdef USE_HEX_MODE_USORx
00837             if (_at->send("AT+UDCONF=1,1") && _at->recv("OK")) {
00838             } else {
00839                 success = false;
00840                 break;
00841             }
00842 #endif
00843             read_blk = MAX_READ_SIZE;
00844             if (read_blk > size) {
00845                 read_blk = size;
00846             }
00847             if (socket->pending > 0) {
00848                 tr_debug("Socket 0x%08x: modem handle %d has %d byte(s) pending",
00849                         (unsigned int) socket, socket->modem_handle, socket->pending);
00850                 memset (ipAddress, 0, sizeof (ipAddress)); // Ensure terminator
00851 
00852                 // Note: the maximum length of UDP packet we can receive comes from
00853                 // fitting all of the following into one buffer:
00854                 //
00855                 // +USORF: xx,"max.len.ip.address.ipv4.or.ipv6",yyyyy,wwww,"the_data"\r\n
00856                 //
00857                 // where xx is the handle, max.len.ip.address.ipv4.or.ipv6 is NSAPI_IP_SIZE,
00858                 // yyyyy is the port number (max 65536), wwww is the length of the data and
00859                 // the_data is binary data. I make that 29 + 48 + len(the_data),
00860                 // so the overhead is 77 bytes.
00861 
00862                 //_at->debug_on(false); // ABSOLUTELY no time for debug here if you want to
00863                 // be able to read packets of any size without
00864                 // losing characters in UARTSerial
00865                 if (_at->send("AT+USORF=%d,%d", socket->modem_handle, read_blk) &&
00866                         _at->recv("+USORF: %*d,\"%" u_stringify(NSAPI_IP_SIZE) "[^\"]\",%d,%d,\"",
00867                                 ipAddress, &port, &usorf_sz)) {
00868                     // Must use what +USORF returns here as it may be less or more than we asked for
00869                     if (usorf_sz > socket->pending) {
00870                         socket->pending = 0;
00871                     } else {
00872                         socket->pending -= usorf_sz;
00873                     }
00874                     // Note: insert no debug between _at->recv() and _at->read(), no time...
00875                     if (usorf_sz > size) {
00876                         usorf_sz = size;
00877                     }
00878 #ifdef USE_HEX_MODE_USORx
00879                     read_sz = 0;
00880                     char asciiBytes[2];
00881                     while (success && (read_sz < usorf_sz*2)) {
00882                         // Read the 2 bytes of ascii text for this byte of data
00883                         if (2 == _at->read(asciiBytes, 2)) {
00884                             // convert to binary data and store in read buffer
00885                             sscanf(asciiBytes, "%2hhx", buf+(read_sz/2));
00886                             read_sz += 2;
00887                         } else {
00888                             success = false;
00889                         }
00890                     }
00891 #else
00892                     read_sz = _at->read(buf, usorf_sz);
00893 #endif
00894                     if (read_sz > 0) {
00895                         address->set_ip_address(ipAddress);
00896                         address->set_port(port);
00897                         tr_debug("...read %d byte(s) from modem handle %d...", read_sz,
00898                                 socket->modem_handle);
00899                         if (_debug_trace_on) {
00900                             tr_debug("Read returned %d,  |%*.*s|", read_sz, read_sz, read_sz, buf);
00901                         }
00902                         count += read_sz;
00903                         buf += read_sz;
00904                         size -= read_sz;
00905                         if ((usorf_sz < read_blk) || (usorf_sz == MAX_READ_SIZE)) {
00906                             size = 0; // If we've received less than we asked for, or
00907                             // the max size, then a whole UDP packet has arrived and
00908                             // this means DONE.
00909                         }
00910                     } else {
00911                         // read() should not fail
00912                         success = false;
00913                     }
00914                     tr_debug("Socket 0x%08x: modem handle %d now has only %d byte(s) pending",
00915                             (unsigned int) socket, socket->modem_handle, socket->pending);
00916                     // Wait for the "OK" before continuing
00917                     _at->recv("OK");
00918                 } else {
00919                     // Should never fail to do _at->send()/_at->recv()
00920                     success = false;
00921                 }
00922                 _at->debug_on(_debug_trace_on);
00923             } else if (timer.read_ms() < SOCKET_TIMEOUT) {
00924                 // Wait for URCs
00925                 _at->recv(UNNATURAL_STRING);
00926             } else {
00927                 if (count == 0) {
00928                     // Timeout with nothing received
00929                     nsapi_error_size = NSAPI_ERROR_WOULD_BLOCK;
00930                     success = false;
00931                 }
00932                 size = 0; // This simply to cause an exit
00933             }
00934 
00935         } while (0);  // Any fails that set success=false can simply break, avoids even worse nested if's
00936 
00937 #ifdef USE_HEX_MODE_USORx
00938         // Set back to non-hex mode
00939         if (_at->send("AT+UDCONF=1,0") && _at->recv("OK")) {
00940         } else {
00941             success = false;
00942             break;
00943         }
00944 #endif
00945         at_set_timeout(at_timeout);
00946         UNLOCK();
00947     }
00948     timer.stop();
00949 
00950     if (success) {
00951         nsapi_error_size = count;
00952     }
00953 
00954     if (_debug_trace_on) {
00955         tr_debug("socket_recvfrom: %d \"%*.*s\"", count, count, count, buf - count);
00956     } else {
00957         tr_debug("socket_recvfrom: received %d byte(s)", count);
00958     }
00959 
00960     return nsapi_error_size;
00961 }
00962 
00963 // Attach an event callback to a socket, required for asynchronous
00964 // data reception
00965 void UbloxATCellularInterface::socket_attach(nsapi_socket_t handle,
00966         void (*callback)(void *),
00967         void *data)
00968 {
00969     SockCtrl *socket = (SockCtrl *) handle;
00970 
00971     MBED_ASSERT (check_socket(socket));
00972 
00973     socket->callback = callback;
00974     socket->data = data;
00975 }
00976 
00977 // Unsupported TCP server functions.
00978 nsapi_error_t UbloxATCellularInterface::socket_listen(nsapi_socket_t handle,
00979         int backlog)
00980 {
00981     return NSAPI_ERROR_UNSUPPORTED;
00982 }
00983 nsapi_error_t UbloxATCellularInterface::socket_accept(nsapi_socket_t server,
00984         nsapi_socket_t *handle,
00985         SocketAddress *address)
00986 {
00987     return NSAPI_ERROR_UNSUPPORTED;
00988 }
00989 
00990 // Unsupported option functions.
00991 nsapi_error_t UbloxATCellularInterface::setsockopt(nsapi_socket_t handle,
00992         int level, int optname,
00993         const void *optval,
00994         unsigned optlen)
00995 {
00996     return NSAPI_ERROR_UNSUPPORTED;
00997 }
00998 nsapi_error_t UbloxATCellularInterface::getsockopt(nsapi_socket_t handle,
00999         int level, int optname,
01000         void *optval,
01001         unsigned *optlen)
01002 {
01003     return NSAPI_ERROR_UNSUPPORTED;
01004 }
01005 
01006 /**********************************************************************
01007  * PUBLIC METHODS
01008  **********************************************************************/
01009 
01010 // Constructor.
01011 UbloxATCellularInterface::UbloxATCellularInterface(PinName tx,
01012         PinName rx,
01013         int baud,
01014         bool debug_on)
01015 {
01016     _sim_pin_check_change_pending = false;
01017     _sim_pin_check_change_pending_enabled_value = false;
01018     _sim_pin_change_pending = false;
01019     _sim_pin_change_pending_new_pin_value = NULL;
01020     _run_event_thread = true;
01021     _apn = NULL;
01022     _uname = NULL;
01023     _pwd = NULL;
01024     _connection_status_cb = NULL;
01025 
01026     // Initialise sockets storage
01027     memset(_sockets, 0, sizeof(_sockets));
01028     for (unsigned int socket = 0; socket < sizeof(_sockets) / sizeof(_sockets[0]); socket++) {
01029         _sockets[socket].modem_handle = SOCKET_UNUSED;
01030         _sockets[socket].callback = NULL;
01031         _sockets[socket].data = NULL;
01032     }
01033 
01034     // The authentication to use
01035     _auth = NSAPI_SECURITY_UNKNOWN;
01036 
01037     // Nullify the temporary IP address storage
01038     _ip = NULL;
01039 
01040     // Initialise the base class, which starts the AT parser
01041     baseClassInit(tx, rx, baud, debug_on);
01042 
01043     // Start the event handler thread for Rx data
01044     event_thread.start(callback(this, &UbloxATCellularInterface::handle_event));
01045 
01046     // URC handlers for sockets
01047     _at->oob("+UUSORD", callback(this, &UbloxATCellularInterface::UUSORD_URC));
01048     _at->oob("+UUSORF", callback(this, &UbloxATCellularInterface::UUSORF_URC));
01049     _at->oob("+UUSOCL", callback(this, &UbloxATCellularInterface::UUSOCL_URC));
01050     _at->oob("+UUPSDD", callback(this, &UbloxATCellularInterface::UUPSDD_URC));
01051 }
01052 
01053 // Destructor.
01054 UbloxATCellularInterface::~UbloxATCellularInterface()
01055 {
01056     // Let the event thread shut down tidily
01057     _run_event_thread = false;
01058     event_thread.join();
01059 
01060     // Free _ip if it was ever allocated
01061     free(_ip);
01062 }
01063 
01064 // Set the authentication scheme.
01065 void UbloxATCellularInterface::set_authentication(nsapi_security_t auth)
01066 {
01067     _auth = auth;
01068 }
01069 
01070 // Set APN, user name and password.
01071 void  UbloxATCellularInterface::set_credentials(const char *apn,
01072         const char *uname,
01073         const char *pwd)
01074 {
01075     _apn = apn;
01076     _uname = uname;
01077     _pwd = pwd;
01078 }
01079 
01080 // Set PIN.
01081 void UbloxATCellularInterface::set_sim_pin(const char *pin)
01082 {
01083     set_pin(pin);
01084 }
01085 
01086 // Get the IP address of a host.
01087 nsapi_error_t UbloxATCellularInterface::gethostbyname(const char *host,
01088         SocketAddress *address,
01089         nsapi_version_t version)
01090 {
01091     nsapi_error_t nsapi_error = NSAPI_ERROR_DEVICE_ERROR;
01092     int at_timeout;
01093     char ipAddress[NSAPI_IP_SIZE];
01094 
01095     if (address->set_ip_address(host)) {
01096         nsapi_error = NSAPI_ERROR_OK;
01097     } else {
01098         LOCK();
01099         // This interrogation can sometimes take longer than the usual 8 seconds
01100         at_timeout = _at_timeout;
01101         at_set_timeout(60000);
01102         memset (ipAddress, 0, sizeof (ipAddress)); // Ensure terminator
01103         if (_at->send("AT+UDNSRN=0,\"%s\"", host) &&
01104                 _at->recv("+UDNSRN: \"%" u_stringify(NSAPI_IP_SIZE) "[^\"]\"", ipAddress) &&
01105                 _at->recv("OK")) {
01106             if (address->set_ip_address(ipAddress)) {
01107                 nsapi_error = NSAPI_ERROR_OK;
01108             }
01109         }
01110         at_set_timeout(at_timeout);
01111         UNLOCK();
01112     }
01113 
01114     return nsapi_error;
01115 }
01116 
01117 // Make a cellular connection
01118 nsapi_error_t UbloxATCellularInterface::connect(const char *sim_pin,
01119         const char *apn,
01120         const char *uname,
01121         const char *pwd)
01122 {
01123     nsapi_error_t nsapi_error;
01124 
01125     if (sim_pin != NULL) {
01126         _pin = sim_pin;
01127     }
01128 
01129     if (apn != NULL) {
01130         _apn = apn;
01131     }
01132 
01133     if ((uname != NULL) && (pwd != NULL)) {
01134         _uname = uname;
01135         _pwd = pwd;
01136     } else {
01137         _uname = NULL;
01138         _pwd = NULL;
01139     }
01140 
01141     nsapi_error = connect();
01142 
01143     return nsapi_error;
01144 }
01145 
01146 // Make a cellular connection using the IP stack on board the cellular modem
01147 nsapi_error_t UbloxATCellularInterface::connect()
01148 {
01149     nsapi_error_t nsapi_error = NSAPI_ERROR_DEVICE_ERROR;
01150     bool registered = false;
01151 
01152     // Set up modem and then register with the network
01153     if (init()) {
01154         nsapi_error = NSAPI_ERROR_NO_CONNECTION;
01155         // Perform any pending SIM actions
01156         if (_sim_pin_check_change_pending) {
01157             if (!sim_pin_check_enable(_sim_pin_check_change_pending_enabled_value)) {
01158                 nsapi_error = NSAPI_ERROR_AUTH_FAILURE;
01159             }
01160             _sim_pin_check_change_pending = false;
01161         }
01162         if (_sim_pin_change_pending) {
01163             if (!change_sim_pin(_sim_pin_change_pending_new_pin_value)) {
01164                 nsapi_error = NSAPI_ERROR_AUTH_FAILURE;
01165             }
01166             _sim_pin_change_pending = false;
01167         }
01168 
01169         if (nsapi_error == NSAPI_ERROR_NO_CONNECTION) {
01170             for (int retries = 0; !registered && (retries < 3); retries++) {
01171                 if (nwk_registration()) {
01172                     registered = true;;
01173                 }
01174             }
01175         }
01176     }
01177 
01178     // Attempt to establish a connection
01179 #if defined(TARGET_UBLOX_C030_R410M) || defined (TARGET_SR_L475RG_R410)
01180     if (registered) {
01181 #else
01182         if (registered && connect_modem_stack()) {
01183 #endif
01184             nsapi_error = NSAPI_ERROR_OK;
01185         }
01186 
01187         return nsapi_error;
01188     }
01189 
01190     // User initiated disconnect.
01191     nsapi_error_t UbloxATCellularInterface::disconnect()
01192     {
01193         nsapi_error_t nsapi_error = NSAPI_ERROR_DEVICE_ERROR;
01194 
01195         if (disconnect_modem_stack() && nwk_deregistration()) {
01196             nsapi_error = NSAPI_ERROR_OK;
01197         }
01198 
01199         return nsapi_error;
01200     }
01201 
01202     // Enable or disable SIM PIN check lock.
01203     nsapi_error_t UbloxATCellularInterface::set_sim_pin_check(bool set,
01204             bool immediate,
01205             const char *sim_pin)
01206     {
01207         nsapi_error_t nsapi_error = NSAPI_ERROR_AUTH_FAILURE;
01208 
01209         if (sim_pin != NULL) {
01210             _pin = sim_pin;
01211         }
01212 
01213         if (immediate) {
01214             if (init()) {
01215                 if (sim_pin_check_enable(set)) {
01216                     nsapi_error = NSAPI_ERROR_OK;
01217                 }
01218             } else {
01219                 nsapi_error = NSAPI_ERROR_DEVICE_ERROR;
01220             }
01221         } else {
01222             nsapi_error = NSAPI_ERROR_OK;
01223             _sim_pin_check_change_pending = true;
01224             _sim_pin_check_change_pending_enabled_value = set;
01225         }
01226 
01227         return nsapi_error;
01228     }
01229 
01230     // Change the PIN code for the SIM card.
01231     nsapi_error_t UbloxATCellularInterface::set_new_sim_pin(const char *new_pin,
01232             bool immediate,
01233             const char *old_pin)
01234     {
01235         nsapi_error_t nsapi_error = NSAPI_ERROR_AUTH_FAILURE;
01236 
01237         if (old_pin != NULL) {
01238             _pin = old_pin;
01239         }
01240 
01241         if (immediate) {
01242             if (init()) {
01243                 if (change_sim_pin(new_pin)) {
01244                     nsapi_error = NSAPI_ERROR_OK;
01245                 }
01246             } else {
01247                 nsapi_error = NSAPI_ERROR_DEVICE_ERROR;
01248             }
01249         } else {
01250             nsapi_error = NSAPI_ERROR_OK;
01251             _sim_pin_change_pending = true;
01252             _sim_pin_change_pending_new_pin_value = new_pin;
01253         }
01254 
01255         return nsapi_error;
01256     }
01257 
01258     // Determine if the connection is up.
01259     bool UbloxATCellularInterface::is_connected()
01260     {
01261         return get_ip_address() != NULL;
01262     }
01263 
01264     // Get the IP address of the on-board modem IP stack.
01265     const char * UbloxATCellularInterface::get_ip_address()
01266     {
01267         SocketAddress address;
01268         LOCK();
01269 
01270         if (_ip == NULL) {
01271             // Temporary storage for an IP address string with terminator
01272             _ip = (char *) malloc(NSAPI_IP_SIZE);
01273         }
01274 
01275         if (_ip != NULL) {
01276             memset(_ip, 0, NSAPI_IP_SIZE); // Ensure a terminator
01277             // +UPSND=<profile_id>,<param_tag>[,<dynamic_param_val>]
01278             // If we get back a quoted "w.x.y.z" then we have an IP address,
01279             // otherwise we don't.
01280             if (!_at->send("AT+UPSND=" PROFILE ",0") ||
01281                     !_at->recv("+UPSND: " PROFILE ",0,\"%" u_stringify(NSAPI_IP_SIZE) "[^\"]\"", _ip) ||
01282                     !_at->recv("OK") ||
01283                     !address.set_ip_address(_ip) || // Return NULL if the address is not a valid one
01284                     !address) { // Return null if the address is zero
01285                 free (_ip);
01286                 _ip = NULL;
01287             }
01288         }
01289 
01290         UNLOCK();
01291         return _ip;
01292     }
01293 
01294     // Get the local network mask.
01295     const char *UbloxATCellularInterface::get_netmask()
01296     {
01297         // Not implemented.
01298         return NULL;
01299     }
01300 
01301     // Get the local gateways.
01302     const char *UbloxATCellularInterface::get_gateway()
01303     {
01304         return get_ip_address();
01305     }
01306 
01307     // Callback in case the connection is lost.
01308     void UbloxATCellularInterface::connection_status_cb(Callback<void(nsapi_error_t)> cb)
01309     {
01310         _connection_status_cb = cb;
01311     }
01312 
01313     // End of file
01314