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 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 00452 clear_socket(socket); 00453 socket->modem_handle = modem_handle; 00454 *handle = (nsapi_socket_t) socket; 00455 nsapi_error = NSAPI_ERROR_OK; 00456 } 00457 } 00458 } else { 00459 nsapi_error = NSAPI_ERROR_NO_MEMORY; 00460 } 00461 00462 UNLOCK(); 00463 return nsapi_error; 00464 } 00465 00466 // Close a socket. 00467 nsapi_error_t UbloxATCellularInterface::socket_close(nsapi_socket_t handle) 00468 { 00469 nsapi_error_t nsapi_error = NSAPI_ERROR_DEVICE_ERROR; 00470 SockCtrl *socket = (SockCtrl *) handle; 00471 LOCK(); 00472 00473 tr_debug("socket_close(0x%08x)", (unsigned int) handle); 00474 00475 MBED_ASSERT (check_socket(socket)); 00476 00477 if (_at->send("AT+USOCL=%d", socket->modem_handle) && 00478 _at->recv("OK")) { 00479 clear_socket(socket); 00480 nsapi_error = NSAPI_ERROR_OK; 00481 } 00482 00483 UNLOCK(); 00484 return nsapi_error; 00485 } 00486 00487 // Bind a local port to a socket. 00488 nsapi_error_t UbloxATCellularInterface::socket_bind(nsapi_socket_t handle, 00489 const SocketAddress &address) 00490 { 00491 nsapi_error_t nsapi_error = NSAPI_ERROR_NO_SOCKET; 00492 int proto; 00493 int modem_handle; 00494 SockCtrl savedSocket; 00495 SockCtrl *socket = (SockCtrl *) handle; 00496 LOCK(); 00497 00498 tr_debug("socket_bind(0x%08x, :%d)", (unsigned int) handle, address.get_port()); 00499 00500 MBED_ASSERT (check_socket(socket)); 00501 00502 // Query the socket type 00503 if (_at->send("AT+USOCTL=%d,0", socket->modem_handle) && 00504 _at->recv("+USOCTL: %*d,0,%d\n", &proto) && 00505 _at->recv("OK")) { 00506 savedSocket = *socket; 00507 nsapi_error = NSAPI_ERROR_DEVICE_ERROR; 00508 // Now close the socket and re-open it with the binding given 00509 if (_at->send("AT+USOCL=%d", socket->modem_handle) && 00510 _at->recv("OK")) { 00511 clear_socket(socket); 00512 nsapi_error = NSAPI_ERROR_CONNECTION_LOST; 00513 if (_at->send("AT+USOCR=%d,%d", proto, address.get_port()) && 00514 _at->recv("+USOCR: %d\n", &modem_handle) && (modem_handle != SOCKET_UNUSED) && 00515 _at->recv("OK")) { 00516 *socket = savedSocket; 00517 nsapi_error = NSAPI_ERROR_OK; 00518 } 00519 } 00520 } 00521 00522 UNLOCK(); 00523 return nsapi_error; 00524 } 00525 00526 // Connect to a socket 00527 nsapi_error_t UbloxATCellularInterface::socket_connect(nsapi_socket_t handle, 00528 const SocketAddress &address) 00529 { 00530 nsapi_error_t nsapi_error = NSAPI_ERROR_DEVICE_ERROR; 00531 SockCtrl *socket = (SockCtrl *) handle; 00532 LOCK(); 00533 00534 tr_debug("socket_connect(0x%08x, %s(:%d))", (unsigned int) handle, 00535 address.get_ip_address(), address.get_port()); 00536 00537 MBED_ASSERT (check_socket(socket)); 00538 00539 if (_at->send("AT+USOCO=%d,\"%s\",%d", socket->modem_handle, 00540 address.get_ip_address(), address.get_port()) && 00541 _at->recv("OK")) { 00542 nsapi_error = NSAPI_ERROR_OK; 00543 } 00544 00545 UNLOCK(); 00546 return nsapi_error; 00547 } 00548 00549 // Send to a socket. 00550 nsapi_size_or_error_t UbloxATCellularInterface::socket_send(nsapi_socket_t handle, 00551 const void *data, 00552 nsapi_size_t size) 00553 { 00554 nsapi_size_or_error_t nsapi_error_size = NSAPI_ERROR_DEVICE_ERROR; 00555 bool success = true; 00556 const char *buf = (const char *) data; 00557 nsapi_size_t blk = MAX_WRITE_SIZE; 00558 nsapi_size_t count = size; 00559 SockCtrl *socket = (SockCtrl *) handle; 00560 00561 tr_debug("socket_send(0x%08x, 0x%08x, %d)", (unsigned int) handle, (unsigned int) data, size); 00562 00563 MBED_ASSERT (check_socket(socket)); 00564 00565 if (socket->modem_handle == SOCKET_UNUSED) { 00566 tr_debug("socket_send: socket closed"); 00567 return NSAPI_ERROR_NO_SOCKET; 00568 } 00569 00570 while ((count > 0) && success) { 00571 if (count < blk) { 00572 blk = count; 00573 } 00574 LOCK(); 00575 00576 if (_at->send("AT+USOWR=%d,%d", socket->modem_handle, blk) && _at->recv("@")) { 00577 wait_ms(50); 00578 if ((_at->write(buf, blk) < (int) blk) || 00579 !_at->recv("OK")) { 00580 success = false; 00581 } 00582 } else { 00583 success = false; 00584 } 00585 00586 UNLOCK(); 00587 buf += blk; 00588 count -= blk; 00589 } 00590 00591 if (success) { 00592 nsapi_error_size = size - count; 00593 if (_debug_trace_on) { 00594 tr_debug("socket_send: %d \"%*.*s\"", size, size, size, (char *) data); 00595 } 00596 } 00597 00598 return nsapi_error_size; 00599 } 00600 00601 // Send to an IP address. 00602 nsapi_size_or_error_t UbloxATCellularInterface::socket_sendto(nsapi_socket_t handle, 00603 const SocketAddress &address, 00604 const void *data, 00605 nsapi_size_t size) 00606 { 00607 nsapi_size_or_error_t nsapi_error_size = NSAPI_ERROR_DEVICE_ERROR; 00608 bool success = true; 00609 const char *buf = (const char *) data; 00610 nsapi_size_t blk = MAX_WRITE_SIZE; 00611 nsapi_size_t count = size; 00612 SockCtrl *socket = (SockCtrl *) handle; 00613 00614 tr_debug("socket_sendto(0x%8x, %s(:%d), 0x%08x, %d)", (unsigned int) handle, 00615 address.get_ip_address(), address.get_port(), (unsigned int) data, size); 00616 00617 MBED_ASSERT (check_socket(socket)); 00618 00619 if (size > MAX_WRITE_SIZE) { 00620 tr_warn("WARNING: packet length %d is too big for one UDP packet (max %d), will be fragmented.", size, MAX_WRITE_SIZE); 00621 } 00622 00623 while ((count > 0) && success) { 00624 if (count < blk) { 00625 blk = count; 00626 } 00627 LOCK(); 00628 00629 if (_at->send("AT+USOST=%d,\"%s\",%d,%d", socket->modem_handle, 00630 address.get_ip_address(), address.get_port(), blk) && 00631 _at->recv("@")) { 00632 wait_ms(50); 00633 if ((_at->write(buf, blk) >= (int) blk) && 00634 _at->recv("OK")) { 00635 } else { 00636 success = false; 00637 } 00638 } else { 00639 success = false; 00640 } 00641 00642 UNLOCK(); 00643 buf += blk; 00644 count -= blk; 00645 } 00646 00647 if (success) { 00648 nsapi_error_size = size - count; 00649 if (_debug_trace_on) { 00650 tr_debug("socket_sendto: %d \"%*.*s\"", size, size, size, (char *) data); 00651 } 00652 } 00653 00654 return nsapi_error_size; 00655 } 00656 00657 // Receive from a socket, TCP style. 00658 nsapi_size_or_error_t UbloxATCellularInterface::socket_recv(nsapi_socket_t handle, 00659 void *data, 00660 nsapi_size_t size) 00661 { 00662 nsapi_size_or_error_t nsapi_error_size = NSAPI_ERROR_DEVICE_ERROR; 00663 bool success = true; 00664 char *buf = (char *) data; 00665 nsapi_size_t read_blk; 00666 nsapi_size_t count = 0; 00667 unsigned int usord_sz; 00668 int read_sz; 00669 Timer timer; 00670 SockCtrl *socket = (SockCtrl *) handle; 00671 int at_timeout; 00672 00673 tr_debug("socket_recv(0x%08x, 0x%08x, %d)", 00674 (unsigned int) handle, (unsigned int) data, size); 00675 00676 MBED_ASSERT (check_socket(socket)); 00677 00678 if (socket->modem_handle == SOCKET_UNUSED) { 00679 tr_debug("socket_recv: socket closed"); 00680 return NSAPI_ERROR_NO_SOCKET; 00681 } 00682 00683 timer.start(); 00684 00685 while (success && (size > 0)) { 00686 LOCK(); 00687 at_timeout = _at_timeout; 00688 at_set_timeout(1000); 00689 00690 read_blk = MAX_READ_SIZE; 00691 if (read_blk > size) { 00692 read_blk = size; 00693 } 00694 if (socket->pending > 0) { 00695 tr_debug("Socket 0x%08x: modem handle %d has %d byte(s) pending", 00696 (unsigned int) socket, socket->modem_handle, socket->pending); 00697 _at->debug_on(false); // ABSOLUTELY no time for debug here if you want to 00698 // be able to read packets of any size without 00699 // losing characters in UARTSerial 00700 if (_at->send("AT+USORD=%d,%d", socket->modem_handle, read_blk) && 00701 _at->recv("+USORD: %*d,%d,\"", &usord_sz)) { 00702 // Must use what +USORD returns here as it may be less or more than we asked for 00703 if (usord_sz > socket->pending) { 00704 socket->pending = 0; 00705 } else { 00706 socket->pending -= usord_sz; 00707 } 00708 // Note: insert no debug between _at->recv() and _at->read(), no time... 00709 if (usord_sz > size) { 00710 usord_sz = size; 00711 } 00712 read_sz = _at->read(buf, usord_sz); 00713 if (read_sz > 0) { 00714 tr_debug("...read %d byte(s) from modem handle %d...", read_sz, 00715 socket->modem_handle); 00716 if (_debug_trace_on) { 00717 tr_debug("Read returned %d, |%*.*s|", read_sz, read_sz, read_sz, buf); 00718 } 00719 count += read_sz; 00720 buf += read_sz; 00721 size -= read_sz; 00722 } else { 00723 // read() should not fail 00724 success = false; 00725 } 00726 tr_debug("Socket 0x%08x: modem handle %d now has only %d byte(s) pending", 00727 (unsigned int) socket, socket->modem_handle, socket->pending); 00728 // Wait for the "OK" before continuing 00729 _at->recv("OK"); 00730 } else { 00731 // Should never fail to do _at->send()/_at->recv() 00732 success = false; 00733 } 00734 _at->debug_on(_debug_trace_on); 00735 } else if (timer.read_ms() < SOCKET_TIMEOUT) { 00736 // Wait for URCs 00737 _at->recv(UNNATURAL_STRING); 00738 } else { 00739 if (count == 0) { 00740 // Timeout with nothing received 00741 nsapi_error_size = NSAPI_ERROR_WOULD_BLOCK; 00742 success = false; 00743 } 00744 size = 0; // This simply to cause an exit 00745 } 00746 00747 at_set_timeout(at_timeout); 00748 UNLOCK(); 00749 } 00750 timer.stop(); 00751 00752 if (success) { 00753 nsapi_error_size = count; 00754 } 00755 00756 if (_debug_trace_on) { 00757 tr_debug("socket_recv: %d \"%*.*s\"", count, count, count, buf - count); 00758 } else { 00759 tr_debug("socket_recv: received %d byte(s)", count); 00760 } 00761 00762 return nsapi_error_size; 00763 } 00764 00765 // Receive a packet over a UDP socket. 00766 nsapi_size_or_error_t UbloxATCellularInterface::socket_recvfrom(nsapi_socket_t handle, 00767 SocketAddress *address, 00768 void *data, 00769 nsapi_size_t size) 00770 { 00771 nsapi_size_or_error_t nsapi_error_size = NSAPI_ERROR_DEVICE_ERROR; 00772 bool success = true; 00773 char *buf = (char *) data; 00774 nsapi_size_t read_blk; 00775 nsapi_size_t count = 0; 00776 char ipAddress[NSAPI_IP_SIZE]; 00777 int port; 00778 unsigned int usorf_sz; 00779 int read_sz; 00780 Timer timer; 00781 SockCtrl *socket = (SockCtrl *) handle; 00782 int at_timeout; 00783 00784 tr_debug("socket_recvfrom(0x%08x, 0x%08x, %d)", 00785 (unsigned int) handle, (unsigned int) data, size); 00786 00787 MBED_ASSERT (check_socket(socket)); 00788 00789 timer.start(); 00790 00791 while (success && (size > 0)) { 00792 LOCK(); 00793 at_timeout = _at_timeout; 00794 at_set_timeout(1000); 00795 00796 read_blk = MAX_READ_SIZE; 00797 if (read_blk > size) { 00798 read_blk = size; 00799 } 00800 if (socket->pending > 0) { 00801 tr_debug("Socket 0x%08x: modem handle %d has %d byte(s) pending", 00802 (unsigned int) socket, socket->modem_handle, socket->pending); 00803 memset (ipAddress, 0, sizeof (ipAddress)); // Ensure terminator 00804 00805 // Note: the maximum length of UDP packet we can receive comes from 00806 // fitting all of the following into one buffer: 00807 // 00808 // +USORF: xx,"max.len.ip.address.ipv4.or.ipv6",yyyyy,wwww,"the_data"\r\n 00809 // 00810 // where xx is the handle, max.len.ip.address.ipv4.or.ipv6 is NSAPI_IP_SIZE, 00811 // yyyyy is the port number (max 65536), wwww is the length of the data and 00812 // the_data is binary data. I make that 29 + 48 + len(the_data), 00813 // so the overhead is 77 bytes. 00814 00815 _at->debug_on(false); // ABSOLUTELY no time for debug here if you want to 00816 // be able to read packets of any size without 00817 // losing characters in UARTSerial 00818 if (_at->send("AT+USORF=%d,%d", socket->modem_handle, read_blk) && 00819 _at->recv("+USORF: %*d,\"%" u_stringify(NSAPI_IP_SIZE) "[^\"]\",%d,%d,\"", 00820 ipAddress, &port, &usorf_sz)) { 00821 // Must use what +USORF returns here as it may be less or more than we asked for 00822 if (usorf_sz > socket->pending) { 00823 socket->pending = 0; 00824 } else { 00825 socket->pending -= usorf_sz; 00826 } 00827 // Note: insert no debug between _at->recv() and _at->read(), no time... 00828 if (usorf_sz > size) { 00829 usorf_sz = size; 00830 } 00831 read_sz = _at->read(buf, usorf_sz); 00832 if (read_sz > 0) { 00833 address->set_ip_address(ipAddress); 00834 address->set_port(port); 00835 tr_debug("...read %d byte(s) from modem handle %d...", read_sz, 00836 socket->modem_handle); 00837 if (_debug_trace_on) { 00838 tr_debug("Read returned %d, |%*.*s|", read_sz, read_sz, read_sz, buf); 00839 } 00840 count += read_sz; 00841 buf += read_sz; 00842 size -= read_sz; 00843 if ((usorf_sz < read_blk) || (usorf_sz == MAX_READ_SIZE)) { 00844 size = 0; // If we've received less than we asked for, or 00845 // the max size, then a whole UDP packet has arrived and 00846 // this means DONE. 00847 } 00848 } else { 00849 // read() should not fail 00850 success = false; 00851 } 00852 tr_debug("Socket 0x%08x: modem handle %d now has only %d byte(s) pending", 00853 (unsigned int) socket, socket->modem_handle, socket->pending); 00854 // Wait for the "OK" before continuing 00855 _at->recv("OK"); 00856 } else { 00857 // Should never fail to do _at->send()/_at->recv() 00858 success = false; 00859 } 00860 _at->debug_on(_debug_trace_on); 00861 } else if (timer.read_ms() < SOCKET_TIMEOUT) { 00862 // Wait for URCs 00863 _at->recv(UNNATURAL_STRING); 00864 } else { 00865 if (count == 0) { 00866 // Timeout with nothing received 00867 nsapi_error_size = NSAPI_ERROR_WOULD_BLOCK; 00868 success = false; 00869 } 00870 size = 0; // This simply to cause an exit 00871 } 00872 00873 at_set_timeout(at_timeout); 00874 UNLOCK(); 00875 } 00876 timer.stop(); 00877 00878 if (success) { 00879 nsapi_error_size = count; 00880 } 00881 00882 if (_debug_trace_on) { 00883 tr_debug("socket_recvfrom: %d \"%*.*s\"", count, count, count, buf - count); 00884 } else { 00885 tr_debug("socket_recvfrom: received %d byte(s)", count); 00886 } 00887 00888 return nsapi_error_size; 00889 } 00890 00891 // Attach an event callback to a socket, required for asynchronous 00892 // data reception 00893 void UbloxATCellularInterface::socket_attach(nsapi_socket_t handle, 00894 void (*callback)(void *), 00895 void *data) 00896 { 00897 SockCtrl *socket = (SockCtrl *) handle; 00898 00899 MBED_ASSERT (check_socket(socket)); 00900 00901 socket->callback = callback; 00902 socket->data = data; 00903 } 00904 00905 // Unsupported TCP server functions. 00906 nsapi_error_t UbloxATCellularInterface::socket_listen(nsapi_socket_t handle, 00907 int backlog) 00908 { 00909 return NSAPI_ERROR_UNSUPPORTED; 00910 } 00911 nsapi_error_t UbloxATCellularInterface::socket_accept(nsapi_socket_t server, 00912 nsapi_socket_t *handle, 00913 SocketAddress *address) 00914 { 00915 return NSAPI_ERROR_UNSUPPORTED; 00916 } 00917 00918 // Unsupported option functions. 00919 nsapi_error_t UbloxATCellularInterface::setsockopt(nsapi_socket_t handle, 00920 int level, int optname, 00921 const void *optval, 00922 unsigned optlen) 00923 { 00924 return NSAPI_ERROR_UNSUPPORTED; 00925 } 00926 nsapi_error_t UbloxATCellularInterface::getsockopt(nsapi_socket_t handle, 00927 int level, int optname, 00928 void *optval, 00929 unsigned *optlen) 00930 { 00931 return NSAPI_ERROR_UNSUPPORTED; 00932 } 00933 00934 /********************************************************************** 00935 * PUBLIC METHODS 00936 **********************************************************************/ 00937 00938 // Constructor. 00939 UbloxATCellularInterface::UbloxATCellularInterface(PinName tx, 00940 PinName rx, 00941 int baud, 00942 bool debug_on, 00943 osPriority priority) 00944 { 00945 _sim_pin_check_change_pending = false; 00946 _sim_pin_check_change_pending_enabled_value = false; 00947 _sim_pin_change_pending = false; 00948 _sim_pin_change_pending_new_pin_value = NULL; 00949 _run_event_thread = true; 00950 _apn = NULL; 00951 _uname = NULL; 00952 _pwd = NULL; 00953 _connection_status_cb = NULL; 00954 00955 // Initialise sockets storage 00956 memset(_sockets, 0, sizeof(_sockets)); 00957 for (unsigned int socket = 0; socket < sizeof(_sockets) / sizeof(_sockets[0]); socket++) { 00958 _sockets[socket].modem_handle = SOCKET_UNUSED; 00959 _sockets[socket].callback = NULL; 00960 _sockets[socket].data = NULL; 00961 } 00962 00963 // The authentication to use 00964 _auth = NSAPI_SECURITY_UNKNOWN; 00965 00966 // Nullify the temporary IP address storage 00967 _ip = NULL; 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 int at_timeout; 01023 char ipAddress[NSAPI_IP_SIZE]; 01024 01025 if (address->set_ip_address(host)) { 01026 nsapi_error = NSAPI_ERROR_OK; 01027 } else { 01028 LOCK(); 01029 // This interrogation can sometimes take longer than the usual 8 seconds 01030 at_timeout = _at_timeout; 01031 at_set_timeout(60000); 01032 memset (ipAddress, 0, sizeof (ipAddress)); // Ensure terminator 01033 if (_at->send("AT+UDNSRN=0,\"%s\"", host) && 01034 _at->recv("+UDNSRN: \"%" u_stringify(NSAPI_IP_SIZE) "[^\"]\"", ipAddress) && 01035 _at->recv("OK")) { 01036 if (address->set_ip_address(ipAddress)) { 01037 nsapi_error = NSAPI_ERROR_OK; 01038 } 01039 } 01040 at_set_timeout(at_timeout); 01041 UNLOCK(); 01042 } 01043 01044 return nsapi_error; 01045 } 01046 01047 // Make a cellular connection 01048 nsapi_error_t UbloxATCellularInterface::connect(const char *sim_pin, 01049 const char *apn, 01050 const char *uname, 01051 const char *pwd) 01052 { 01053 nsapi_error_t nsapi_error; 01054 01055 if (sim_pin != NULL) { 01056 _pin = sim_pin; 01057 } 01058 01059 if (apn != NULL) { 01060 _apn = apn; 01061 } 01062 01063 if ((uname != NULL) && (pwd != NULL)) { 01064 _uname = uname; 01065 _pwd = pwd; 01066 } else { 01067 _uname = NULL; 01068 _pwd = NULL; 01069 } 01070 01071 nsapi_error = connect(); 01072 01073 return nsapi_error; 01074 } 01075 01076 // Make a cellular connection using the IP stack on board the cellular modem 01077 nsapi_error_t UbloxATCellularInterface::connect() 01078 { 01079 nsapi_error_t nsapi_error = NSAPI_ERROR_DEVICE_ERROR; 01080 bool registered = false; 01081 01082 // Set up modem and then register with the network 01083 if (init()) { 01084 nsapi_error = NSAPI_ERROR_NO_CONNECTION; 01085 // Perform any pending SIM actions 01086 if (_sim_pin_check_change_pending) { 01087 if (!sim_pin_check_enable(_sim_pin_check_change_pending_enabled_value)) { 01088 nsapi_error = NSAPI_ERROR_AUTH_FAILURE; 01089 } 01090 _sim_pin_check_change_pending = false; 01091 } 01092 if (_sim_pin_change_pending) { 01093 if (!change_sim_pin(_sim_pin_change_pending_new_pin_value)) { 01094 nsapi_error = NSAPI_ERROR_AUTH_FAILURE; 01095 } 01096 _sim_pin_change_pending = false; 01097 } 01098 01099 if (nsapi_error == NSAPI_ERROR_NO_CONNECTION) { 01100 for (int retries = 0; !registered && (retries < 3); retries++) { 01101 if (nwk_registration()) { 01102 registered = true;; 01103 } 01104 } 01105 } 01106 } 01107 01108 // Attempt to establish a connection 01109 #ifdef TARGET_UBLOX_C030_R41XM 01110 if (registered) { 01111 #else 01112 if (registered && connect_modem_stack()) { 01113 #endif 01114 nsapi_error = NSAPI_ERROR_OK; 01115 } 01116 01117 return nsapi_error; 01118 } 01119 01120 // User initiated disconnect. 01121 nsapi_error_t UbloxATCellularInterface::disconnect() 01122 { 01123 nsapi_error_t nsapi_error = NSAPI_ERROR_DEVICE_ERROR; 01124 01125 if (disconnect_modem_stack() && nwk_deregistration()) { 01126 nsapi_error = NSAPI_ERROR_OK; 01127 } 01128 01129 return nsapi_error; 01130 } 01131 01132 // Enable or disable SIM PIN check lock. 01133 nsapi_error_t UbloxATCellularInterface::set_sim_pin_check(bool set, 01134 bool immediate, 01135 const char *sim_pin) 01136 { 01137 nsapi_error_t nsapi_error = NSAPI_ERROR_AUTH_FAILURE; 01138 01139 if (sim_pin != NULL) { 01140 _pin = sim_pin; 01141 } 01142 01143 if (immediate) { 01144 if (init()) { 01145 if (sim_pin_check_enable(set)) { 01146 nsapi_error = NSAPI_ERROR_OK; 01147 } 01148 } else { 01149 nsapi_error = NSAPI_ERROR_DEVICE_ERROR; 01150 } 01151 } else { 01152 nsapi_error = NSAPI_ERROR_OK; 01153 _sim_pin_check_change_pending = true; 01154 _sim_pin_check_change_pending_enabled_value = set; 01155 } 01156 01157 return nsapi_error; 01158 } 01159 01160 // Change the PIN code for the SIM card. 01161 nsapi_error_t UbloxATCellularInterface::set_new_sim_pin(const char *new_pin, 01162 bool immediate, 01163 const char *old_pin) 01164 { 01165 nsapi_error_t nsapi_error = NSAPI_ERROR_AUTH_FAILURE; 01166 01167 if (old_pin != NULL) { 01168 _pin = old_pin; 01169 } 01170 01171 if (immediate) { 01172 if (init()) { 01173 if (change_sim_pin(new_pin)) { 01174 nsapi_error = NSAPI_ERROR_OK; 01175 } 01176 } else { 01177 nsapi_error = NSAPI_ERROR_DEVICE_ERROR; 01178 } 01179 } else { 01180 nsapi_error = NSAPI_ERROR_OK; 01181 _sim_pin_change_pending = true; 01182 _sim_pin_change_pending_new_pin_value = new_pin; 01183 } 01184 01185 return nsapi_error; 01186 } 01187 01188 // Determine if the connection is up. 01189 bool UbloxATCellularInterface::is_connected() 01190 { 01191 return get_ip_address() != NULL; 01192 } 01193 01194 // Get the IP address of the on-board modem IP stack. 01195 const char * UbloxATCellularInterface::get_ip_address() 01196 { 01197 SocketAddress address; 01198 LOCK(); 01199 01200 if (_ip == NULL) { 01201 // Temporary storage for an IP address string with terminator 01202 _ip = (char *) malloc(NSAPI_IP_SIZE); 01203 } 01204 01205 if (_ip != NULL) { 01206 memset(_ip, 0, NSAPI_IP_SIZE); // Ensure a terminator 01207 // +UPSND=<profile_id>,<param_tag>[,<dynamic_param_val>] 01208 // If we get back a quoted "w.x.y.z" then we have an IP address, 01209 // otherwise we don't. 01210 if (!_at->send("AT+UPSND=" PROFILE ",0") || 01211 !_at->recv("+UPSND: " PROFILE ",0,\"%" u_stringify(NSAPI_IP_SIZE) "[^\"]\"", _ip) || 01212 !_at->recv("OK") || 01213 !address.set_ip_address(_ip) || // Return NULL if the address is not a valid one 01214 !address) { // Return null if the address is zero 01215 free (_ip); 01216 _ip = NULL; 01217 } 01218 } 01219 01220 UNLOCK(); 01221 return _ip; 01222 } 01223 01224 // Get the local network mask. 01225 const char *UbloxATCellularInterface::get_netmask() 01226 { 01227 // Not implemented. 01228 return NULL; 01229 } 01230 01231 // Get the local gateways. 01232 const char *UbloxATCellularInterface::get_gateway() 01233 { 01234 return get_ip_address(); 01235 } 01236 01237 // Callback in case the connection is lost. 01238 void UbloxATCellularInterface::connection_status_cb(Callback<void(nsapi_error_t)> cb) 01239 { 01240 _connection_status_cb = cb; 01241 } 01242 01243 void UbloxATCellularInterface::set_plmn(const char *plmn) { 01244 01245 } 01246 01247 // End of file 01248
Generated on Wed Jul 20 2022 02:26:16 by
1.7.2