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