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