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.
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
Generated on Wed Jul 20 2022 19:24:53 by
1.7.2