Retry

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 (true) {
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                     if(!activated)
00281                     {
00282                         // Activate, waiting 30 seconds for the connection to be made
00283                         at_set_timeout(30000);
00284                         activated = _at->send("AT+UPSDA=" PROFILE ",3") && _at->recv("OK");
00285                         at_set_timeout(at_timeout);
00286                     }
00287                 }
00288             }
00289         }
00290     }
00291 
00292     return activated;
00293 }
00294 
00295 // Activate a profile by reusing an external PDP context.
00296 // Note: the AT interface should be locked before this is called.
00297 bool UbloxATCellularInterface::activate_profile_reuse_external(void)
00298 {
00299     bool success = false;
00300     int cid = -1;
00301     char ip[NSAPI_IP_SIZE];
00302     SocketAddress address;
00303     int t;
00304     int at_timeout = _at_timeout;
00305 
00306     //+CGDCONT: <cid>,"IP","<apn name>","<ip adr>",0,0,0,0,0,0
00307     if (_at->send("AT+CGDCONT?")) {
00308         if (_at->recv("+CGDCONT: %d,\"IP\",\"%*[^\"]\",\"%" u_stringify(NSAPI_IP_SIZE) "[^\"]\",%*d,%*d,%*d,%*d,%*d,%*d",
00309                       &t, ip) &&
00310             _at->recv("OK")) {
00311             // Check if the IP address is valid
00312             if (address.set_ip_address(ip)) {
00313                 cid = t;
00314             }
00315         }
00316     }
00317 
00318     // If a context has been found, use it
00319     if ((cid != -1) && (_at->send("AT+UPSD=" PROFILE ",100,%d", cid) && _at->recv("OK"))) {
00320         // Activate, waiting 30 seconds for the connection to be made
00321         at_set_timeout(30000);
00322         success = _at->send("AT+UPSDA=" PROFILE ",3") && _at->recv("OK");
00323         at_set_timeout(at_timeout);
00324     }
00325 
00326     return success;
00327 }
00328 
00329 // Activate a profile by context ID.
00330 // Note: the AT interface should be locked before this is called.
00331 bool UbloxATCellularInterface::activate_profile_by_cid(int cid,
00332                                                        const char* apn,
00333                                                        const char* username,
00334                                                        const char* password,
00335                                                        nsapi_security_t auth)
00336 {
00337     bool success = false;
00338     int at_timeout = _at_timeout;
00339 
00340     if (_at->send("AT+CGDCONT=%d,\"IP\",\"%s\"", cid, apn) && _at->recv("OK") &&
00341         _at->send("AT+UAUTHREQ=%d,%d,\"%s\",\"%s\"", cid, nsapi_security_to_modem_security(auth),
00342                   username, password) && _at->recv("OK") &&
00343         _at->send("AT+UPSD=" PROFILE ",100,%d", cid) && _at->recv("OK")) {
00344 
00345         // Wait 30 seconds for the connection to be made
00346         at_set_timeout(30000);
00347         // Activate the protocol
00348         success = _at->send("AT+UPSDA=" PROFILE ",3") && _at->recv("OK");
00349         at_set_timeout(at_timeout);
00350     }
00351 
00352     return success;
00353 }
00354 
00355 // Connect the on board IP stack of the modem.
00356 bool UbloxATCellularInterface::connect_modem_stack()
00357 {
00358     bool success = false;
00359     int active = 0;
00360     const char * config = NULL;
00361     LOCK();
00362 
00363     // Check the profile
00364     if (_at->send("AT+UPSND=" PROFILE ",8") && _at->recv("+UPSND: %*d,%*d,%d\n", &active) &&
00365         _at->recv("OK")) {
00366         if (active == 0) {
00367             // If the caller hasn't entered an APN, try to find it
00368             if (_apn == NULL) {
00369                 config = apnconfig(_dev_info.imsi);
00370             }
00371 
00372             // Attempt to connect
00373             do {
00374                 // Set up APN and IP protocol for PDP context
00375                 get_next_credentials(&config);
00376                 _auth = (*_uname && *_pwd) ? _auth : NSAPI_SECURITY_NONE;
00377                 if ((_dev_info.dev != DEV_TOBY_L2) && (_dev_info.dev != DEV_MPCI_L2)) {
00378                     success = activate_profile(_apn, _uname, _pwd, _auth);
00379                 } else {
00380                     success = activate_profile_reuse_external();
00381                     if (success) {
00382                         tr_debug("Reusing external context");
00383                     } else {
00384                         success = activate_profile_by_cid(1, _apn, _uname, _pwd, _auth);
00385                     }
00386                 }
00387             } while (!success && config && *config);
00388         } else {
00389             // If the profile is already active, we're good
00390             success = true;
00391         }
00392     }
00393 
00394     if (!success) {
00395         tr_error("Failed to connect, check your APN/username/password");
00396     }
00397 
00398     UNLOCK();
00399     return success;
00400 }
00401 
00402 // Disconnect the on board IP stack of the modem.
00403 bool UbloxATCellularInterface::disconnect_modem_stack()
00404 {
00405     bool success = false;
00406     LOCK();
00407 
00408     if (get_ip_address() != NULL) {
00409         if (_at->send("AT+UPSDA=" PROFILE ",4") && _at->recv("OK")) {
00410             success = true;
00411             if (_connection_status_cb) {
00412                 _connection_status_cb(NSAPI_ERROR_CONNECTION_LOST);
00413             }
00414         }
00415     }
00416 
00417     UNLOCK();
00418     return success;
00419 }
00420 
00421 /**********************************************************************
00422  * PROTECTED METHODS: NETWORK INTERFACE and SOCKETS
00423  **********************************************************************/
00424 
00425 // Gain access to us.
00426 NetworkStack *UbloxATCellularInterface::get_stack()
00427 {
00428     return this;
00429 }
00430 
00431 // Create a socket.
00432 nsapi_error_t UbloxATCellularInterface::socket_open(nsapi_socket_t *handle,
00433                                                     nsapi_protocol_t proto)
00434 {
00435     nsapi_error_t nsapi_error = NSAPI_ERROR_DEVICE_ERROR;
00436     bool success = false;
00437     int modem_handle;
00438     SockCtrl *socket;
00439     LOCK();
00440 
00441     // Find a free socket
00442     socket = find_socket();
00443     tr_debug("socket_open(%d)", proto);
00444 
00445     if (socket != NULL) {
00446         if (proto == NSAPI_UDP) {
00447             success = _at->send("AT+USOCR=17");
00448         } else if (proto == NSAPI_TCP) {
00449             success = _at->send("AT+USOCR=6");
00450         } else  {
00451             nsapi_error = NSAPI_ERROR_UNSUPPORTED;
00452         }
00453 
00454         if (success) {
00455             nsapi_error = NSAPI_ERROR_NO_SOCKET;
00456             if (_at->recv("+USOCR: %d\n", &modem_handle) && (modem_handle != SOCKET_UNUSED) &&
00457                 _at->recv("OK")) {
00458                 tr_debug("Socket 0x%8x: handle %d was created", (unsigned int) socket, modem_handle);
00459                 clear_socket(socket);
00460                 socket->modem_handle         = modem_handle;
00461                 *handle = (nsapi_socket_t) socket;
00462                 nsapi_error = NSAPI_ERROR_OK;
00463             }
00464         }
00465     } else {
00466         nsapi_error = NSAPI_ERROR_NO_MEMORY;
00467     }
00468 
00469     UNLOCK();
00470     return nsapi_error;
00471 }
00472 
00473 // Close a socket.
00474 nsapi_error_t UbloxATCellularInterface::socket_close(nsapi_socket_t handle)
00475 {
00476     nsapi_error_t nsapi_error = NSAPI_ERROR_DEVICE_ERROR;
00477     SockCtrl *socket = (SockCtrl *) handle;
00478     LOCK();
00479 
00480     tr_debug("socket_close(0x%08x)", (unsigned int) handle);
00481 
00482     MBED_ASSERT (check_socket(socket));
00483 
00484     if (_at->send("AT+USOCL=%d", socket->modem_handle) &&
00485         _at->recv("OK")) {
00486         clear_socket(socket);
00487         nsapi_error = NSAPI_ERROR_OK;
00488     }
00489 
00490     UNLOCK();
00491     return nsapi_error;
00492 }
00493 
00494 // Bind a local port to a socket.
00495 nsapi_error_t UbloxATCellularInterface::socket_bind(nsapi_socket_t handle,
00496                                                     const SocketAddress &address)
00497 {
00498     nsapi_error_t nsapi_error = NSAPI_ERROR_NO_SOCKET;
00499     int proto;
00500     int modem_handle;
00501     SockCtrl savedSocket;
00502     SockCtrl *socket = (SockCtrl *) handle;
00503     LOCK();
00504 
00505     tr_debug("socket_bind(0x%08x, :%d)", (unsigned int) handle, address.get_port());
00506 
00507     MBED_ASSERT (check_socket(socket));
00508 
00509     // Query the socket type
00510     if (_at->send("AT+USOCTL=%d,0", socket->modem_handle) &&
00511         _at->recv("+USOCTL: %*d,0,%d\n", &proto) &&
00512         _at->recv("OK")) {
00513         savedSocket = *socket;
00514         nsapi_error = NSAPI_ERROR_DEVICE_ERROR;
00515         // Now close the socket and re-open it with the binding given
00516         if (_at->send("AT+USOCL=%d", socket->modem_handle) &&
00517             _at->recv("OK")) {
00518             clear_socket(socket);
00519             nsapi_error = NSAPI_ERROR_CONNECTION_LOST;
00520             if (_at->send("AT+USOCR=%d,%d", proto, address.get_port()) &&
00521                 _at->recv("+USOCR: %d\n", &modem_handle) && (modem_handle != SOCKET_UNUSED) &&
00522                 _at->recv("OK")) {
00523                 *socket = savedSocket;
00524                 nsapi_error = NSAPI_ERROR_OK;
00525             }
00526         }
00527     }
00528 
00529     UNLOCK();
00530     return nsapi_error;
00531 }
00532 
00533 // Connect to a socket
00534 nsapi_error_t UbloxATCellularInterface::socket_connect(nsapi_socket_t handle,
00535                                                        const SocketAddress &address)
00536 {
00537     nsapi_error_t nsapi_error = NSAPI_ERROR_DEVICE_ERROR;
00538     SockCtrl *socket = (SockCtrl *) handle;
00539     LOCK();
00540 
00541     tr_debug("socket_connect(0x%08x, %s(:%d))", (unsigned int) handle,
00542              address.get_ip_address(), address.get_port());
00543 
00544     MBED_ASSERT (check_socket(socket));
00545 
00546     if (_at->send("AT+USOCO=%d,\"%s\",%d", socket->modem_handle,
00547                   address.get_ip_address(), address.get_port()) &&
00548         _at->recv("OK")) {
00549         nsapi_error = NSAPI_ERROR_OK;
00550     }
00551 
00552     UNLOCK();
00553     return nsapi_error;
00554 }
00555 
00556 // Send to a socket.
00557 nsapi_size_or_error_t UbloxATCellularInterface::socket_send(nsapi_socket_t handle,
00558                                                             const void *data,
00559                                                             nsapi_size_t size)
00560 {
00561     nsapi_size_or_error_t nsapi_error_size = NSAPI_ERROR_DEVICE_ERROR;
00562     bool success = true;
00563     const char *buf = (const char *) data;
00564     nsapi_size_t blk = MAX_WRITE_SIZE;
00565     nsapi_size_t count = size;
00566     SockCtrl *socket = (SockCtrl *) handle;
00567 
00568     tr_debug("socket_send(0x%08x, 0x%08x, %d)", (unsigned int) handle, (unsigned int) data, size);
00569 
00570     MBED_ASSERT (check_socket(socket));
00571 
00572     while ((count > 0) && success) {
00573         if (count < blk) {
00574             blk = count;
00575         }
00576         LOCK();
00577 
00578         if (_at->send("AT+USOWR=%d,%d", socket->modem_handle, blk) && _at->recv("@")) {
00579             wait_ms(50);
00580             if ((_at->write(buf, blk) < (int) blk) ||
00581                  !_at->recv("OK")) {
00582                 success = false;
00583             }
00584         } else {
00585             success = false;
00586         }
00587 
00588         UNLOCK();
00589         buf += blk;
00590         count -= blk;
00591     }
00592 
00593     if (success) {
00594         nsapi_error_size = size - count;
00595         if (_debug_trace_on) {
00596             tr_debug("socket_send: %d \"%*.*s\"", size, size, size, (char *) data);
00597         }
00598     }
00599 
00600     return nsapi_error_size;
00601 }
00602 
00603 // Send to an IP address.
00604 nsapi_size_or_error_t UbloxATCellularInterface::socket_sendto(nsapi_socket_t handle,
00605                                                               const SocketAddress &address,
00606                                                               const void *data,
00607                                                               nsapi_size_t size)
00608 {
00609     nsapi_size_or_error_t nsapi_error_size = NSAPI_ERROR_DEVICE_ERROR;
00610     bool success = true;
00611     const char *buf = (const char *) data;
00612     nsapi_size_t blk = MAX_WRITE_SIZE;
00613     nsapi_size_t count = size;
00614     SockCtrl *socket = (SockCtrl *) handle;
00615 
00616     tr_debug("socket_sendto(0x%8x, %s(:%d), 0x%08x, %d)", (unsigned int) handle,
00617              address.get_ip_address(), address.get_port(), (unsigned int) data, size);
00618 
00619     MBED_ASSERT (check_socket(socket));
00620 
00621     if (size > MAX_WRITE_SIZE) {
00622         tr_warn("WARNING: packet length %d is too big for one UDP packet (max %d), will be fragmented.", size, MAX_WRITE_SIZE);
00623     }
00624 
00625     while ((count > 0) && success) {
00626         if (count < blk) {
00627             blk = count;
00628         }
00629         LOCK();
00630 
00631         if (_at->send("AT+USOST=%d,\"%s\",%d,%d", socket->modem_handle,
00632                       address.get_ip_address(), address.get_port(), blk) &&
00633             _at->recv("@")) {
00634             wait_ms(50);
00635             if ((_at->write(buf, blk) >= (int) blk) &&
00636                  _at->recv("OK")) {
00637             } else {
00638                 success = false;
00639             }
00640         } else {
00641             success = false;
00642         }
00643 
00644         UNLOCK();
00645         buf += blk;
00646         count -= blk;
00647     }
00648 
00649     if (success) {
00650         nsapi_error_size = size - count;
00651         if (_debug_trace_on) {
00652             tr_debug("socket_sendto: %d \"%*.*s\"", size, size, size, (char *) data);
00653         }
00654     }
00655 
00656     return nsapi_error_size;
00657 }
00658 
00659 // Receive from a socket, TCP style.
00660 nsapi_size_or_error_t UbloxATCellularInterface::socket_recv(nsapi_socket_t handle,
00661                                                             void *data,
00662                                                             nsapi_size_t size)
00663 {
00664     nsapi_size_or_error_t nsapi_error_size = NSAPI_ERROR_DEVICE_ERROR;
00665     bool success = true;
00666     char *buf = (char *) data;
00667     nsapi_size_t read_blk;
00668     nsapi_size_t count = 0;
00669     unsigned int usord_sz;
00670     int read_sz;
00671     Timer timer;
00672     SockCtrl *socket = (SockCtrl *) handle;
00673     int at_timeout;
00674 
00675     tr_debug("socket_recv(0x%08x, 0x%08x, %d)",
00676              (unsigned int) handle, (unsigned int) data, size);
00677 
00678     MBED_ASSERT (check_socket(socket));
00679 
00680     timer.start();
00681 
00682     while (success && (size > 0)) {
00683         LOCK();
00684         at_timeout = _at_timeout;
00685         at_set_timeout(1000);
00686 
00687         read_blk = MAX_READ_SIZE;
00688         if (read_blk > size) {
00689             read_blk = size;
00690         }
00691         if (socket->pending > 0) {
00692             tr_debug("Socket 0x%08x: modem handle %d has %d byte(s) pending",
00693                      (unsigned int) socket, socket->modem_handle, socket->pending);
00694             _at->debug_on(false); // ABSOLUTELY no time for debug here if you want to
00695                                   // be able to read packets of any size without
00696                                   // losing characters in UARTSerial
00697             if (_at->send("AT+USORD=%d,%d", socket->modem_handle, read_blk) &&
00698                 _at->recv("+USORD: %*d,%d,\"", &usord_sz)) {
00699                 socket->pending -= usord_sz; // Must use what +USORD returns here as it
00700                                              // may be less than we asked for
00701                 // Note: insert no debug between _at->recv() and _at->read(), no time...
00702                 if (usord_sz > size) {
00703                     usord_sz = size;
00704                 }
00705                 read_sz = _at->read(buf, usord_sz);
00706                 if (read_sz > 0) {
00707                     tr_debug("...read %d byte(s) from modem handle %d...", read_sz,
00708                              socket->modem_handle);
00709                     if (_debug_trace_on) {
00710                         tr_debug("Read returned %d,  |%*.*s|", read_sz, read_sz, read_sz, buf);
00711                     }
00712                     count += read_sz;
00713                     buf += read_sz;
00714                     size -= read_sz;
00715                 } else {
00716                     // read() should not fail
00717                     success = false;
00718                 }
00719                 tr_debug("Socket 0x%08x: modem handle %d now has only %d byte(s) pending",
00720                          (unsigned int) socket, socket->modem_handle, socket->pending);
00721                 // Wait for the "OK" before continuing
00722                 _at->recv("OK");
00723             } else {
00724                 // Should never fail to do _at->send()/_at->recv()
00725                 success = false;
00726             }
00727             _at->debug_on(_debug_trace_on);
00728         } else if (timer.read_ms() < SOCKET_TIMEOUT) {
00729             // Wait for URCs
00730             _at->recv(UNNATURAL_STRING);
00731         } else {
00732             if (count == 0) {
00733                 // Timeout with nothing received
00734                 nsapi_error_size = NSAPI_ERROR_WOULD_BLOCK;
00735                 success = false;
00736             }
00737             size = 0; // This simply to cause an exit
00738         }
00739 
00740         at_set_timeout(at_timeout);
00741         UNLOCK();
00742     }
00743     timer.stop();
00744 
00745     if (success) {
00746         nsapi_error_size = count;
00747     }
00748 
00749     if (_debug_trace_on) {
00750         tr_debug("socket_recv: %d \"%*.*s\"", count, count, count, buf - count);
00751     } else {
00752         tr_debug("socket_recv: received %d byte(s)", count);
00753     }
00754 
00755     return nsapi_error_size;
00756 }
00757 
00758 // Receive a packet over a UDP socket.
00759 nsapi_size_or_error_t UbloxATCellularInterface::socket_recvfrom(nsapi_socket_t handle,
00760                                                                 SocketAddress *address,
00761                                                                 void *data,
00762                                                                 nsapi_size_t size)
00763 {
00764     nsapi_size_or_error_t nsapi_error_size = NSAPI_ERROR_DEVICE_ERROR;
00765     bool success = true;
00766     char *buf = (char *) data;
00767     nsapi_size_t read_blk;
00768     nsapi_size_t count = 0;
00769     char ipAddress[NSAPI_IP_SIZE];
00770     int port;
00771     unsigned int usorf_sz;
00772     int read_sz;
00773     Timer timer;
00774     SockCtrl *socket = (SockCtrl *) handle;
00775     int at_timeout;
00776 
00777     tr_debug("socket_recvfrom(0x%08x, 0x%08x, %d)",
00778              (unsigned int) handle, (unsigned int) data, size);
00779 
00780     MBED_ASSERT (check_socket(socket));
00781 
00782     timer.start();
00783 
00784     while (success && (size > 0)) {
00785         LOCK();
00786         at_timeout = _at_timeout;
00787         at_set_timeout(1000);
00788 
00789         read_blk = MAX_READ_SIZE;
00790         if (read_blk > size) {
00791             read_blk = size;
00792         }
00793         if (socket->pending > 0) {
00794             tr_debug("Socket 0x%08x: modem handle %d has %d byte(s) pending",
00795                      (unsigned int) socket, socket->modem_handle, socket->pending);
00796             memset (ipAddress, 0, sizeof (ipAddress)); // Ensure terminator
00797 
00798             // Note: the maximum length of UDP packet we can receive comes from
00799             // fitting all of the following into one buffer:
00800             //
00801             // +USORF: xx,"max.len.ip.address.ipv4.or.ipv6",yyyyy,wwww,"the_data"\r\n
00802             //
00803             // where xx is the handle, max.len.ip.address.ipv4.or.ipv6 is NSAPI_IP_SIZE,
00804             // yyyyy is the port number (max 65536), wwww is the length of the data and
00805             // the_data is binary data. I make that 29 + 48 + len(the_data),
00806             // so the overhead is 77 bytes.
00807 
00808             _at->debug_on(false); // ABSOLUTELY no time for debug here if you want to
00809                                   // be able to read packets of any size without
00810                                   // losing characters in UARTSerial
00811             if (_at->send("AT+USORF=%d,%d", socket->modem_handle, read_blk) &&
00812                 _at->recv("+USORF: %*d,\"%" u_stringify(NSAPI_IP_SIZE) "[^\"]\",%d,%d,\"",
00813                           ipAddress, &port, &usorf_sz)) {
00814                 socket->pending -= usorf_sz; // Must use what +USORF returns here as it
00815                                              // may be less than we asked for
00816                 // Note: insert no debug between _at->recv() and _at->read(), no time...
00817                 if (usorf_sz > size) {
00818                     usorf_sz = size;
00819                 }
00820                 read_sz = _at->read(buf, usorf_sz);
00821                 if (read_sz > 0) {
00822                     address->set_ip_address(ipAddress);
00823                     address->set_port(port);
00824                     tr_debug("...read %d byte(s) from modem handle %d...", read_sz,
00825                              socket->modem_handle);
00826                     if (_debug_trace_on) {
00827                         tr_debug("Read returned %d,  |%*.*s|", read_sz, read_sz, read_sz, buf);
00828                     }
00829                     count += read_sz;
00830                     buf += read_sz;
00831                     size -= read_sz;
00832                     if ((usorf_sz < read_blk) || (usorf_sz == MAX_READ_SIZE)) {
00833                         size = 0; // If we've received less than we asked for, or
00834                                   // the max size, then a whole UDP packet has arrived and
00835                                   // this means DONE.
00836                     }
00837                 } else {
00838                     // read() should not fail
00839                     success = false;
00840                 }
00841                 tr_debug("Socket 0x%08x: modem handle %d now has only %d byte(s) pending",
00842                          (unsigned int) socket, socket->modem_handle, socket->pending);
00843                 // Wait for the "OK" before continuing
00844                 _at->recv("OK");
00845             } else {
00846                 // Should never fail to do _at->send()/_at->recv()
00847                 success = false;
00848             }
00849             _at->debug_on(_debug_trace_on);
00850         } else if (timer.read_ms() < SOCKET_TIMEOUT) {
00851             // Wait for URCs
00852             _at->recv(UNNATURAL_STRING);
00853         } else {
00854             if (count == 0) {
00855                 // Timeout with nothing received
00856                 nsapi_error_size = NSAPI_ERROR_WOULD_BLOCK;
00857                 success = false;
00858             }
00859             size = 0; // This simply to cause an exit
00860         }
00861 
00862         at_set_timeout(at_timeout);
00863         UNLOCK();
00864     }
00865     timer.stop();
00866 
00867     if (success) {
00868         nsapi_error_size = count;
00869     }
00870 
00871     if (_debug_trace_on) {
00872         tr_debug("socket_recvfrom: %d \"%*.*s\"", count, count, count, buf - count);
00873     } else {
00874         tr_debug("socket_recvfrom: received %d byte(s)", count);
00875     }
00876 
00877     return nsapi_error_size;
00878 }
00879 
00880 // Attach an event callback to a socket, required for asynchronous
00881 // data reception
00882 void UbloxATCellularInterface::socket_attach(nsapi_socket_t handle,
00883                                              void (*callback)(void *),
00884                                              void *data)
00885 {
00886     SockCtrl *socket = (SockCtrl *) handle;
00887 
00888     MBED_ASSERT (check_socket(socket));
00889 
00890     socket->callback = callback;
00891     socket->data = data;
00892 }
00893 
00894 // Unsupported TCP server functions.
00895 nsapi_error_t UbloxATCellularInterface::socket_listen(nsapi_socket_t handle,
00896                                                       int backlog)
00897 {
00898     return NSAPI_ERROR_UNSUPPORTED;
00899 }
00900 nsapi_error_t UbloxATCellularInterface::socket_accept(nsapi_socket_t server,
00901                                                       nsapi_socket_t *handle,
00902                                                       SocketAddress *address)
00903 {
00904     return NSAPI_ERROR_UNSUPPORTED;
00905 }
00906 
00907 // Unsupported option functions.
00908 nsapi_error_t UbloxATCellularInterface::setsockopt(nsapi_socket_t handle,
00909                                                    int level, int optname,
00910                                                    const void *optval,
00911                                                    unsigned optlen)
00912 {
00913     return NSAPI_ERROR_UNSUPPORTED;
00914 }
00915 nsapi_error_t UbloxATCellularInterface::getsockopt(nsapi_socket_t handle,
00916                                                    int level, int optname,
00917                                                    void *optval,
00918                                                    unsigned *optlen)
00919 {
00920     return NSAPI_ERROR_UNSUPPORTED;
00921 }
00922 
00923 /**********************************************************************
00924  * PUBLIC METHODS
00925  **********************************************************************/
00926 
00927 // Constructor.
00928 UbloxATCellularInterface::UbloxATCellularInterface(PinName tx,
00929                                                    PinName rx,
00930                                                    int baud,
00931                                                    bool debug_on)
00932 {
00933     _sim_pin_check_change_pending = false;
00934     _sim_pin_check_change_pending_enabled_value = false;
00935     _sim_pin_change_pending = false;
00936     _sim_pin_change_pending_new_pin_value = NULL;
00937     _apn = NULL;
00938     _uname = NULL;
00939     _pwd = NULL;
00940     _connection_status_cb = NULL;
00941 
00942     // Initialise sockets storage
00943     memset(_sockets, 0, sizeof(_sockets));
00944     for (unsigned int socket = 0; socket < sizeof(_sockets) / sizeof(_sockets[0]); socket++) {
00945         _sockets[socket].modem_handle = SOCKET_UNUSED;
00946         _sockets[socket].callback = NULL;
00947         _sockets[socket].data = NULL;
00948     }
00949 
00950     // The authentication to use
00951     _auth = NSAPI_SECURITY_UNKNOWN;
00952 
00953     // Nullify the temporary IP address storage
00954     _ip = NULL;
00955 
00956     // Initialise the base class, which starts the AT parser
00957     baseClassInit(tx, rx, baud, debug_on);
00958 
00959     // Start the event handler thread for Rx data
00960     event_thread.start(callback(this, &UbloxATCellularInterface::handle_event));
00961 
00962     // URC handlers for sockets
00963     _at->oob("+UUSORD", callback(this, &UbloxATCellularInterface::UUSORD_URC));
00964     _at->oob("+UUSORF", callback(this, &UbloxATCellularInterface::UUSORF_URC));
00965     _at->oob("+UUSOCL", callback(this, &UbloxATCellularInterface::UUSOCL_URC));
00966     _at->oob("+UUPSDD", callback(this, &UbloxATCellularInterface::UUPSDD_URC));
00967 }
00968 
00969 // Destructor.
00970 UbloxATCellularInterface::~UbloxATCellularInterface()
00971 {
00972     // Free _ip if it was ever allocated
00973     free(_ip);
00974 }
00975 
00976 // Set the authentication scheme.
00977 void UbloxATCellularInterface::set_authentication(nsapi_security_t auth)
00978 {
00979     _auth = auth;
00980 }
00981 
00982 // Set APN, user name and password.
00983 void  UbloxATCellularInterface::set_credentials(const char *apn,
00984                                                 const char *uname,
00985                                                 const char *pwd)
00986 {
00987     _apn = apn;
00988     _uname = uname;
00989     _pwd = pwd;
00990 }
00991 
00992 // Set PIN.
00993 void UbloxATCellularInterface::set_sim_pin(const char *pin) {
00994     set_pin(pin);
00995 }
00996 
00997 static int count = 0;
00998 
00999 // Get the IP address of a host.
01000 nsapi_error_t UbloxATCellularInterface::gethostbyname(const char *host,
01001                                                       SocketAddress *address,
01002                                                       nsapi_version_t version)
01003 {
01004     nsapi_error_t nsapi_error = NSAPI_ERROR_DEVICE_ERROR;
01005     int at_timeout;
01006     char ipAddress[NSAPI_IP_SIZE];
01007 
01008     count++;
01009     if (address->set_ip_address(host)) {
01010         nsapi_error = NSAPI_ERROR_OK;
01011     } else {
01012         LOCK();
01013         // This interrogation can sometimes take longer than the usual 8 seconds
01014         at_timeout = _at_timeout;
01015         at_set_timeout(60000);
01016         memset (ipAddress, 0, sizeof (ipAddress)); // Ensure terminator
01017         if (_at->send("AT+UDNSRN=0,\"%s\"", host) &&
01018             _at->recv("+UDNSRN: \"%" u_stringify(NSAPI_IP_SIZE) "[^\"]\"", ipAddress) &&
01019             _at->recv("OK")) {
01020             if (address->set_ip_address(ipAddress)) {
01021                 nsapi_error = NSAPI_ERROR_OK;
01022             }
01023         }
01024         at_set_timeout(at_timeout);
01025         UNLOCK();
01026     }
01027 
01028     return nsapi_error;
01029 }
01030 
01031 // Make a cellular connection
01032 nsapi_error_t UbloxATCellularInterface::connect(const char *sim_pin,
01033                                                 const char *apn,
01034                                                 const char *uname,
01035                                                 const char *pwd)
01036 {
01037     nsapi_error_t nsapi_error;
01038 
01039     if (sim_pin != NULL) {
01040         _pin = sim_pin;
01041     }
01042 
01043     if (apn != NULL) {
01044         _apn = apn;
01045     }
01046 
01047     if ((uname != NULL) && (pwd != NULL)) {
01048         _uname = uname;
01049         _pwd = pwd;
01050     } else {
01051         _uname = NULL;
01052         _pwd = NULL;
01053     }
01054 
01055     nsapi_error = connect();
01056 
01057     return nsapi_error;
01058 }
01059 
01060 // Make a cellular connection using the IP stack on board the cellular modem
01061 nsapi_error_t UbloxATCellularInterface::connect()
01062 {
01063     nsapi_error_t nsapi_error = NSAPI_ERROR_DEVICE_ERROR;
01064     bool registered = false;
01065 
01066     // Set up modem and then register with the network
01067     if (init()) {
01068         nsapi_error = NSAPI_ERROR_NO_CONNECTION;
01069         // Perform any pending SIM actions
01070         if (_sim_pin_check_change_pending) {
01071             if (!sim_pin_check_enable(_sim_pin_check_change_pending_enabled_value)) {
01072                 nsapi_error = NSAPI_ERROR_AUTH_FAILURE;
01073             }
01074             _sim_pin_check_change_pending = false;
01075         }
01076         if (_sim_pin_change_pending) {
01077             if (!change_sim_pin(_sim_pin_change_pending_new_pin_value)) {
01078                 nsapi_error = NSAPI_ERROR_AUTH_FAILURE;
01079             }
01080             _sim_pin_change_pending = false;
01081         }
01082 
01083         if (nsapi_error == NSAPI_ERROR_NO_CONNECTION) {
01084             for (int retries = 0; !registered && (retries < 3); retries++) {
01085                 if (nwk_registration()) {
01086                     registered = true;;
01087                 }
01088             }
01089         }
01090     }
01091 
01092     // Attempt to establish a connection
01093     if (registered && connect_modem_stack()) {
01094         nsapi_error = NSAPI_ERROR_OK;
01095     }
01096 
01097     return nsapi_error;
01098 }
01099 
01100 // User initiated disconnect.
01101 nsapi_error_t UbloxATCellularInterface::disconnect()
01102 {
01103     nsapi_error_t nsapi_error = NSAPI_ERROR_DEVICE_ERROR;
01104 
01105     if (disconnect_modem_stack() && nwk_deregistration()) {
01106         nsapi_error = NSAPI_ERROR_OK;
01107     }
01108 
01109     return nsapi_error;
01110 }
01111 
01112 // Enable or disable SIM PIN check lock.
01113 nsapi_error_t UbloxATCellularInterface::set_sim_pin_check(bool set,
01114                                                           bool immediate,
01115                                                           const char *sim_pin)
01116 {
01117     nsapi_error_t nsapi_error = NSAPI_ERROR_AUTH_FAILURE;
01118 
01119     if (sim_pin != NULL) {
01120         _pin = sim_pin;
01121     }
01122 
01123     if (immediate) {
01124         if (init()) {
01125             if (sim_pin_check_enable(set)) {
01126                 nsapi_error = NSAPI_ERROR_OK;
01127             }
01128         } else {
01129             nsapi_error = NSAPI_ERROR_DEVICE_ERROR;
01130         }
01131     } else {
01132         nsapi_error = NSAPI_ERROR_OK;
01133         _sim_pin_check_change_pending = true;
01134         _sim_pin_check_change_pending_enabled_value = set;
01135     }
01136 
01137     return nsapi_error;
01138 }
01139 
01140 // Change the PIN code for the SIM card.
01141 nsapi_error_t UbloxATCellularInterface::set_new_sim_pin(const char *new_pin,
01142                                                         bool immediate,
01143                                                         const char *old_pin)
01144 {
01145     nsapi_error_t nsapi_error = NSAPI_ERROR_AUTH_FAILURE;
01146 
01147     if (old_pin != NULL) {
01148         _pin = old_pin;
01149     }
01150 
01151     if (immediate) {
01152         if (init()) {
01153             if (change_sim_pin(new_pin)) {
01154                 nsapi_error = NSAPI_ERROR_OK;
01155             }
01156         } else {
01157             nsapi_error = NSAPI_ERROR_DEVICE_ERROR;
01158         }
01159     } else {
01160         nsapi_error = NSAPI_ERROR_OK;
01161         _sim_pin_change_pending = true;
01162         _sim_pin_change_pending_new_pin_value = new_pin;
01163     }
01164 
01165     return nsapi_error;
01166 }
01167 
01168 // Determine if the connection is up.
01169 bool UbloxATCellularInterface::is_connected()
01170 {
01171     return get_ip_address() != NULL;
01172 }
01173 
01174 // Get the IP address of the on-board modem IP stack.
01175 const char * UbloxATCellularInterface::get_ip_address()
01176 {
01177     SocketAddress address;
01178     LOCK();
01179 
01180     if (_ip == NULL) {
01181         // Temporary storage for an IP address string with terminator
01182         _ip = (char *) malloc(NSAPI_IP_SIZE);
01183     }
01184 
01185     if (_ip != NULL) {
01186         memset(_ip, 0, NSAPI_IP_SIZE); // Ensure a terminator
01187         // +UPSND=<profile_id>,<param_tag>[,<dynamic_param_val>]
01188         // If we get back a quoted "w.x.y.z" then we have an IP address,
01189         // otherwise we don't.
01190         if (!_at->send("AT+UPSND=" PROFILE ",0") ||
01191             !_at->recv("+UPSND: " PROFILE ",0,\"%" u_stringify(NSAPI_IP_SIZE) "[^\"]\"", _ip) ||
01192             !_at->recv("OK") ||
01193             !address.set_ip_address(_ip) || // Return NULL if the address is not a valid one
01194             !address) { // Return null if the address is zero
01195             free (_ip);
01196             _ip = NULL;
01197         }
01198     }
01199 
01200     UNLOCK();
01201     return _ip;
01202 }
01203 
01204 // Get the local network mask.
01205 const char *UbloxATCellularInterface::get_netmask()
01206 {
01207     // Not implemented.
01208     return NULL;
01209 }
01210 
01211 // Get the local gateways.
01212 const char *UbloxATCellularInterface::get_gateway()
01213 {
01214     return get_ip_address();
01215 }
01216 
01217 // Callback in case the connection is lost.
01218 void UbloxATCellularInterface::connection_status_cb(Callback<void(nsapi_error_t)> cb)
01219 {
01220     _connection_status_cb = cb;
01221 }
01222 
01223 // End of file
01224