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 while ((count > 0) && success) { 00565 if (count < blk) { 00566 blk = count; 00567 } 00568 LOCK(); 00569 00570 if (_at->send("AT+USOWR=%d,%d", socket->modem_handle, blk) && _at->recv("@")) { 00571 wait_ms(50); 00572 if ((_at->write(buf, blk) < (int) blk) || 00573 !_at->recv("OK")) { 00574 success = false; 00575 } 00576 } else { 00577 success = false; 00578 } 00579 00580 UNLOCK(); 00581 buf += blk; 00582 count -= blk; 00583 } 00584 00585 if (success) { 00586 nsapi_error_size = size - count; 00587 if (_debug_trace_on) { 00588 tr_debug("socket_send: %d \"%*.*s\"", size, size, size, (char *) data); 00589 } 00590 } 00591 00592 return nsapi_error_size; 00593 } 00594 00595 // Send to an IP address. 00596 nsapi_size_or_error_t UbloxATCellularInterface::socket_sendto(nsapi_socket_t handle, 00597 const SocketAddress &address, 00598 const void *data, 00599 nsapi_size_t size) 00600 { 00601 nsapi_size_or_error_t nsapi_error_size = NSAPI_ERROR_DEVICE_ERROR; 00602 bool success = true; 00603 const char *buf = (const char *) data; 00604 nsapi_size_t blk = MAX_WRITE_SIZE; 00605 nsapi_size_t count = size; 00606 SockCtrl *socket = (SockCtrl *) handle; 00607 00608 tr_debug("socket_sendto(0x%8x, %s(:%d), 0x%08x, %d)", (unsigned int) handle, 00609 address.get_ip_address(), address.get_port(), (unsigned int) data, size); 00610 00611 MBED_ASSERT (check_socket(socket)); 00612 00613 if (size > MAX_WRITE_SIZE) { 00614 tr_warn("WARNING: packet length %d is too big for one UDP packet (max %d), will be fragmented.", size, MAX_WRITE_SIZE); 00615 } 00616 00617 while ((count > 0) && success) { 00618 if (count < blk) { 00619 blk = count; 00620 } 00621 LOCK(); 00622 00623 if (_at->send("AT+USOST=%d,\"%s\",%d,%d", socket->modem_handle, 00624 address.get_ip_address(), address.get_port(), blk) && 00625 _at->recv("@")) { 00626 wait_ms(50); 00627 if ((_at->write(buf, blk) >= (int) blk) && 00628 _at->recv("OK")) { 00629 } else { 00630 success = false; 00631 } 00632 } else { 00633 success = false; 00634 } 00635 00636 UNLOCK(); 00637 buf += blk; 00638 count -= blk; 00639 } 00640 00641 if (success) { 00642 nsapi_error_size = size - count; 00643 if (_debug_trace_on) { 00644 tr_debug("socket_sendto: %d \"%*.*s\"", size, size, size, (char *) data); 00645 } 00646 } 00647 00648 return nsapi_error_size; 00649 } 00650 00651 // Receive from a socket, TCP style. 00652 nsapi_size_or_error_t UbloxATCellularInterface::socket_recv(nsapi_socket_t handle, 00653 void *data, 00654 nsapi_size_t size) 00655 { 00656 nsapi_size_or_error_t nsapi_error_size = NSAPI_ERROR_DEVICE_ERROR; 00657 bool success = true; 00658 char *buf = (char *) data; 00659 nsapi_size_t read_blk; 00660 nsapi_size_t count = 0; 00661 unsigned int usord_sz; 00662 int read_sz; 00663 Timer timer; 00664 SockCtrl *socket = (SockCtrl *) handle; 00665 int at_timeout; 00666 00667 tr_debug("socket_recv(0x%08x, 0x%08x, %d)", 00668 (unsigned int) handle, (unsigned int) data, size); 00669 00670 MBED_ASSERT (check_socket(socket)); 00671 00672 timer.start(); 00673 00674 while (success && (size > 0)) { 00675 LOCK(); 00676 at_timeout = _at_timeout; 00677 at_set_timeout(1000); 00678 00679 read_blk = MAX_READ_SIZE; 00680 if (read_blk > size) { 00681 read_blk = size; 00682 } 00683 if (socket->pending > 0) { 00684 tr_debug("Socket 0x%08x: modem handle %d has %d byte(s) pending", 00685 (unsigned int) socket, socket->modem_handle, socket->pending); 00686 _at->debug_on(false); // ABSOLUTELY no time for debug here if you want to 00687 // be able to read packets of any size without 00688 // losing characters in UARTSerial 00689 if (_at->send("AT+USORD=%d,%d", socket->modem_handle, read_blk) && 00690 _at->recv("+USORD: %*d,%d,\"", &usord_sz)) { 00691 // Must use what +USORD returns here as it may be less or more than we asked for 00692 if (usord_sz > socket->pending) { 00693 socket->pending = 0; 00694 } else { 00695 socket->pending -= usord_sz; 00696 } 00697 // Note: insert no debug between _at->recv() and _at->read(), no time... 00698 if (usord_sz > size) { 00699 usord_sz = size; 00700 } 00701 read_sz = _at->read(buf, usord_sz); 00702 if (read_sz > 0) { 00703 tr_debug("...read %d byte(s) from modem handle %d...", read_sz, 00704 socket->modem_handle); 00705 if (_debug_trace_on) { 00706 tr_debug("Read returned %d, |%*.*s|", read_sz, read_sz, read_sz, buf); 00707 } 00708 count += read_sz; 00709 buf += read_sz; 00710 size -= read_sz; 00711 } else { 00712 // read() should not fail 00713 success = false; 00714 } 00715 tr_debug("Socket 0x%08x: modem handle %d now has only %d byte(s) pending", 00716 (unsigned int) socket, socket->modem_handle, socket->pending); 00717 // Wait for the "OK" before continuing 00718 _at->recv("OK"); 00719 } else { 00720 // Should never fail to do _at->send()/_at->recv() 00721 success = false; 00722 } 00723 _at->debug_on(_debug_trace_on); 00724 } else if (timer.read_ms() < SOCKET_TIMEOUT) { 00725 // Wait for URCs 00726 _at->recv(UNNATURAL_STRING); 00727 } else { 00728 if (count == 0) { 00729 // Timeout with nothing received 00730 nsapi_error_size = NSAPI_ERROR_WOULD_BLOCK; 00731 success = false; 00732 } 00733 size = 0; // This simply to cause an exit 00734 } 00735 00736 at_set_timeout(at_timeout); 00737 UNLOCK(); 00738 } 00739 timer.stop(); 00740 00741 if (success) { 00742 nsapi_error_size = count; 00743 } 00744 00745 if (_debug_trace_on) { 00746 tr_debug("socket_recv: %d \"%*.*s\"", count, count, count, buf - count); 00747 } else { 00748 tr_debug("socket_recv: received %d byte(s)", count); 00749 } 00750 00751 return nsapi_error_size; 00752 } 00753 00754 // Receive a packet over a UDP socket. 00755 nsapi_size_or_error_t UbloxATCellularInterface::socket_recvfrom(nsapi_socket_t handle, 00756 SocketAddress *address, 00757 void *data, 00758 nsapi_size_t size) 00759 { 00760 nsapi_size_or_error_t nsapi_error_size = NSAPI_ERROR_DEVICE_ERROR; 00761 bool success = true; 00762 char *buf = (char *) data; 00763 nsapi_size_t read_blk; 00764 nsapi_size_t count = 0; 00765 char ipAddress[NSAPI_IP_SIZE]; 00766 int port; 00767 unsigned int usorf_sz; 00768 int read_sz; 00769 Timer timer; 00770 SockCtrl *socket = (SockCtrl *) handle; 00771 int at_timeout; 00772 00773 tr_debug("socket_recvfrom(0x%08x, 0x%08x, %d)", 00774 (unsigned int) handle, (unsigned int) data, size); 00775 00776 MBED_ASSERT (check_socket(socket)); 00777 00778 timer.start(); 00779 00780 while (success && (size > 0)) { 00781 LOCK(); 00782 at_timeout = _at_timeout; 00783 at_set_timeout(1000); 00784 00785 read_blk = MAX_READ_SIZE; 00786 if (read_blk > size) { 00787 read_blk = size; 00788 } 00789 if (socket->pending > 0) { 00790 tr_debug("Socket 0x%08x: modem handle %d has %d byte(s) pending", 00791 (unsigned int) socket, socket->modem_handle, socket->pending); 00792 memset (ipAddress, 0, sizeof (ipAddress)); // Ensure terminator 00793 00794 // Note: the maximum length of UDP packet we can receive comes from 00795 // fitting all of the following into one buffer: 00796 // 00797 // +USORF: xx,"max.len.ip.address.ipv4.or.ipv6",yyyyy,wwww,"the_data"\r\n 00798 // 00799 // where xx is the handle, max.len.ip.address.ipv4.or.ipv6 is NSAPI_IP_SIZE, 00800 // yyyyy is the port number (max 65536), wwww is the length of the data and 00801 // the_data is binary data. I make that 29 + 48 + len(the_data), 00802 // so the overhead is 77 bytes. 00803 00804 _at->debug_on(false); // ABSOLUTELY no time for debug here if you want to 00805 // be able to read packets of any size without 00806 // losing characters in UARTSerial 00807 if (_at->send("AT+USORF=%d,%d", socket->modem_handle, read_blk) && 00808 _at->recv("+USORF: %*d,\"%" u_stringify(NSAPI_IP_SIZE) "[^\"]\",%d,%d,\"", 00809 ipAddress, &port, &usorf_sz)) { 00810 // Must use what +USORF returns here as it may be less or more than we asked for 00811 if (usorf_sz > socket->pending) { 00812 socket->pending = 0; 00813 } else { 00814 socket->pending -= usorf_sz; 00815 } 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 _run_event_thread = true; 00943 00944 // Initialise sockets storage 00945 memset(_sockets, 0, sizeof(_sockets)); 00946 for (unsigned int socket = 0; socket < sizeof(_sockets) / sizeof(_sockets[0]); socket++) { 00947 _sockets[socket].modem_handle = SOCKET_UNUSED; 00948 _sockets[socket].callback = NULL; 00949 _sockets[socket].data = NULL; 00950 } 00951 00952 // The authentication to use 00953 _auth = NSAPI_SECURITY_UNKNOWN; 00954 00955 // Nullify the temporary IP address storage 00956 _ip = NULL; 00957 00958 // Initialise the base class, which starts the AT parser 00959 baseClassInit(tx, rx, baud, debug_on); 00960 00961 // Start the event handler thread for Rx data 00962 event_thread.start(callback(this, &UbloxATCellularInterface::handle_event)); 00963 00964 // URC handlers for sockets 00965 _at->oob("+UUSORD", callback(this, &UbloxATCellularInterface::UUSORD_URC)); 00966 _at->oob("+UUSORF", callback(this, &UbloxATCellularInterface::UUSORF_URC)); 00967 _at->oob("+UUSOCL", callback(this, &UbloxATCellularInterface::UUSOCL_URC)); 00968 _at->oob("+UUPSDD", callback(this, &UbloxATCellularInterface::UUPSDD_URC)); 00969 } 00970 00971 // Destructor. 00972 UbloxATCellularInterface::~UbloxATCellularInterface() 00973 { 00974 _run_event_thread = false; 00975 event_thread.join(); 00976 00977 // Free _ip if it was ever allocated 00978 free(_ip); 00979 } 00980 00981 // Set the authentication scheme. 00982 void UbloxATCellularInterface::set_authentication(nsapi_security_t auth) 00983 { 00984 _auth = auth; 00985 } 00986 00987 // Set APN, user name and password. 00988 void UbloxATCellularInterface::set_credentials(const char *apn, 00989 const char *uname, 00990 const char *pwd) 00991 { 00992 _apn = apn; 00993 _uname = uname; 00994 _pwd = pwd; 00995 } 00996 00997 // Set PIN. 00998 void UbloxATCellularInterface::set_sim_pin(const char *pin) 00999 { 01000 set_pin(pin); 01001 } 01002 01003 // Get the IP address of a host. 01004 nsapi_error_t UbloxATCellularInterface::gethostbyname(const char *host, 01005 SocketAddress *address, 01006 nsapi_version_t version) 01007 { 01008 nsapi_error_t nsapi_error = NSAPI_ERROR_DEVICE_ERROR; 01009 int at_timeout; 01010 char ipAddress[NSAPI_IP_SIZE]; 01011 01012 if (address->set_ip_address(host)) { 01013 nsapi_error = NSAPI_ERROR_OK; 01014 } else { 01015 LOCK(); 01016 // This interrogation can sometimes take longer than the usual 8 seconds 01017 at_timeout = _at_timeout; 01018 at_set_timeout(60000); 01019 memset (ipAddress, 0, sizeof (ipAddress)); // Ensure terminator 01020 if (_at->send("AT+UDNSRN=0,\"%s\"", host) && 01021 _at->recv("+UDNSRN: \"%" u_stringify(NSAPI_IP_SIZE) "[^\"]\"", ipAddress) && 01022 _at->recv("OK")) { 01023 if (address->set_ip_address(ipAddress)) { 01024 nsapi_error = NSAPI_ERROR_OK; 01025 } 01026 } 01027 at_set_timeout(at_timeout); 01028 UNLOCK(); 01029 } 01030 01031 return nsapi_error; 01032 } 01033 01034 // Make a cellular connection 01035 nsapi_error_t UbloxATCellularInterface::connect(const char *sim_pin, 01036 const char *apn, 01037 const char *uname, 01038 const char *pwd) 01039 { 01040 nsapi_error_t nsapi_error; 01041 01042 if (sim_pin != NULL) { 01043 _pin = sim_pin; 01044 } 01045 01046 if (apn != NULL) { 01047 _apn = apn; 01048 } 01049 01050 if ((uname != NULL) && (pwd != NULL)) { 01051 _uname = uname; 01052 _pwd = pwd; 01053 } else { 01054 _uname = NULL; 01055 _pwd = NULL; 01056 } 01057 01058 nsapi_error = connect(); 01059 01060 return nsapi_error; 01061 } 01062 01063 // Make a cellular connection using the IP stack on board the cellular modem 01064 nsapi_error_t UbloxATCellularInterface::connect() 01065 { 01066 nsapi_error_t nsapi_error = NSAPI_ERROR_DEVICE_ERROR; 01067 bool registered = false; 01068 01069 // Set up modem and then register with the network 01070 if (init()) { 01071 nsapi_error = NSAPI_ERROR_NO_CONNECTION; 01072 // Perform any pending SIM actions 01073 if (_sim_pin_check_change_pending) { 01074 if (!sim_pin_check_enable(_sim_pin_check_change_pending_enabled_value)) { 01075 nsapi_error = NSAPI_ERROR_AUTH_FAILURE; 01076 } 01077 _sim_pin_check_change_pending = false; 01078 } 01079 if (_sim_pin_change_pending) { 01080 if (!change_sim_pin(_sim_pin_change_pending_new_pin_value)) { 01081 nsapi_error = NSAPI_ERROR_AUTH_FAILURE; 01082 } 01083 _sim_pin_change_pending = false; 01084 } 01085 01086 if (nsapi_error == NSAPI_ERROR_NO_CONNECTION) { 01087 for (int retries = 0; !registered && (retries < 3); retries++) { 01088 if (nwk_registration()) { 01089 registered = true;; 01090 } 01091 } 01092 } 01093 } 01094 01095 // Attempt to establish a connection 01096 #ifdef TARGET_UBLOX_C030_R410M 01097 if (registered) { 01098 #else 01099 if (registered && connect_modem_stack()) { 01100 #endif 01101 nsapi_error = NSAPI_ERROR_OK; 01102 } 01103 01104 return nsapi_error; 01105 } 01106 01107 // User initiated disconnect. 01108 nsapi_error_t UbloxATCellularInterface::disconnect() 01109 { 01110 nsapi_error_t nsapi_error = NSAPI_ERROR_DEVICE_ERROR; 01111 01112 if (disconnect_modem_stack() && nwk_deregistration()) { 01113 nsapi_error = NSAPI_ERROR_OK; 01114 } 01115 01116 return nsapi_error; 01117 } 01118 01119 // Enable or disable SIM PIN check lock. 01120 nsapi_error_t UbloxATCellularInterface::set_sim_pin_check(bool set, 01121 bool immediate, 01122 const char *sim_pin) 01123 { 01124 nsapi_error_t nsapi_error = NSAPI_ERROR_AUTH_FAILURE; 01125 01126 if (sim_pin != NULL) { 01127 _pin = sim_pin; 01128 } 01129 01130 if (immediate) { 01131 if (init()) { 01132 if (sim_pin_check_enable(set)) { 01133 nsapi_error = NSAPI_ERROR_OK; 01134 } 01135 } else { 01136 nsapi_error = NSAPI_ERROR_DEVICE_ERROR; 01137 } 01138 } else { 01139 nsapi_error = NSAPI_ERROR_OK; 01140 _sim_pin_check_change_pending = true; 01141 _sim_pin_check_change_pending_enabled_value = set; 01142 } 01143 01144 return nsapi_error; 01145 } 01146 01147 // Change the PIN code for the SIM card. 01148 nsapi_error_t UbloxATCellularInterface::set_new_sim_pin(const char *new_pin, 01149 bool immediate, 01150 const char *old_pin) 01151 { 01152 nsapi_error_t nsapi_error = NSAPI_ERROR_AUTH_FAILURE; 01153 01154 if (old_pin != NULL) { 01155 _pin = old_pin; 01156 } 01157 01158 if (immediate) { 01159 if (init()) { 01160 if (change_sim_pin(new_pin)) { 01161 nsapi_error = NSAPI_ERROR_OK; 01162 } 01163 } else { 01164 nsapi_error = NSAPI_ERROR_DEVICE_ERROR; 01165 } 01166 } else { 01167 nsapi_error = NSAPI_ERROR_OK; 01168 _sim_pin_change_pending = true; 01169 _sim_pin_change_pending_new_pin_value = new_pin; 01170 } 01171 01172 return nsapi_error; 01173 } 01174 01175 // Determine if the connection is up. 01176 bool UbloxATCellularInterface::is_connected() 01177 { 01178 return get_ip_address() != NULL; 01179 } 01180 01181 // Get the IP address of the on-board modem IP stack. 01182 const char * UbloxATCellularInterface::get_ip_address() 01183 { 01184 SocketAddress address; 01185 LOCK(); 01186 01187 if (_ip == NULL) { 01188 // Temporary storage for an IP address string with terminator 01189 _ip = (char *) malloc(NSAPI_IP_SIZE); 01190 } 01191 01192 if (_ip != NULL) { 01193 memset(_ip, 0, NSAPI_IP_SIZE); // Ensure a terminator 01194 // +UPSND=<profile_id>,<param_tag>[,<dynamic_param_val>] 01195 // If we get back a quoted "w.x.y.z" then we have an IP address, 01196 // otherwise we don't. 01197 if (!_at->send("AT+UPSND=" PROFILE ",0") || 01198 !_at->recv("+UPSND: " PROFILE ",0,\"%" u_stringify(NSAPI_IP_SIZE) "[^\"]\"", _ip) || 01199 !_at->recv("OK") || 01200 !address.set_ip_address(_ip) || // Return NULL if the address is not a valid one 01201 !address) { // Return null if the address is zero 01202 free (_ip); 01203 _ip = NULL; 01204 } 01205 } 01206 01207 UNLOCK(); 01208 return _ip; 01209 } 01210 01211 // Get the local network mask. 01212 const char *UbloxATCellularInterface::get_netmask() 01213 { 01214 // Not implemented. 01215 return NULL; 01216 } 01217 01218 // Get the local gateways. 01219 const char *UbloxATCellularInterface::get_gateway() 01220 { 01221 return get_ip_address(); 01222 } 01223 01224 // Callback in case the connection is lost. 01225 void UbloxATCellularInterface::connection_status_cb(Callback<void(nsapi_error_t)> cb) 01226 { 01227 _connection_status_cb = cb; 01228 } 01229 01230 // End of file 01231
Generated on Wed Jul 13 2022 04:35:37 by
1.7.2
