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