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