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.
Dependents: TYBLE16_simple_data_logger TYBLE16_MP3_Air
QUECTEL_M26_CellularStack.cpp
00001 /* 00002 * Copyright (c) 2018, Arm Limited and affiliates. 00003 * SPDX-License-Identifier: Apache-2.0 00004 * 00005 * Licensed under the Apache License, Version 2.0 (the "License"); 00006 * you may not use this file except in compliance with the License. 00007 * You may obtain a copy of the License at 00008 * 00009 * http://www.apache.org/licenses/LICENSE-2.0 00010 * 00011 * Unless required by applicable law or agreed to in writing, software 00012 * distributed under the License is distributed on an "AS IS" BASIS, 00013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00014 * See the License for the specific language governing permissions and 00015 * limitations under the License. 00016 */ 00017 00018 #include "rtos/Kernel.h" 00019 #include "QUECTEL/M26/QUECTEL_M26_CellularStack.h" 00020 #include "CellularLog.h" 00021 00022 #define SOCKET_SEND_READY_TIMEOUT (30*1000) 00023 #define SOCKET_READ_TIMEOUT 1000 00024 00025 using namespace mbed; 00026 00027 QUECTEL_M26_CellularStack::QUECTEL_M26_CellularStack(ATHandler &atHandler, int cid, nsapi_ip_stack_t stack_type, AT_CellularDevice &device) : 00028 AT_CellularStack(atHandler, cid, stack_type, device) 00029 { 00030 _at.set_urc_handler("+QIRDI:", Callback<void()>(this, &QUECTEL_M26_CellularStack::urc_qiurc)); 00031 00032 _at.set_urc_handler("0, CLOSED", Callback<void()>(this, &QUECTEL_M26_CellularStack::socket_closed_0)); 00033 _at.set_urc_handler("1, CLOSED", Callback<void()>(this, &QUECTEL_M26_CellularStack::socket_closed_1)); 00034 _at.set_urc_handler("2, CLOSED", Callback<void()>(this, &QUECTEL_M26_CellularStack::socket_closed_2)); 00035 _at.set_urc_handler("3, CLOSED", Callback<void()>(this, &QUECTEL_M26_CellularStack::socket_closed_3)); 00036 _at.set_urc_handler("4, CLOSED", Callback<void()>(this, &QUECTEL_M26_CellularStack::socket_closed_4)); 00037 _at.set_urc_handler("5, CLOSED", Callback<void()>(this, &QUECTEL_M26_CellularStack::socket_closed_5)); 00038 } 00039 00040 QUECTEL_M26_CellularStack::~QUECTEL_M26_CellularStack() 00041 { 00042 _at.set_urc_handler("5, CLOSED", NULL); 00043 _at.set_urc_handler("4, CLOSED", NULL); 00044 _at.set_urc_handler("3, CLOSED", NULL); 00045 _at.set_urc_handler("2, CLOSED", NULL); 00046 _at.set_urc_handler("1, CLOSED", NULL); 00047 _at.set_urc_handler("0, CLOSED", NULL); 00048 00049 _at.set_urc_handler("+QIRDI:", NULL); 00050 } 00051 00052 nsapi_error_t QUECTEL_M26_CellularStack::socket_listen(nsapi_socket_t handle, int backlog) 00053 { 00054 return NSAPI_ERROR_UNSUPPORTED ; 00055 } 00056 00057 nsapi_error_t QUECTEL_M26_CellularStack::socket_accept(void *server, void **socket, SocketAddress *addr) 00058 { 00059 return NSAPI_ERROR_UNSUPPORTED ; 00060 } 00061 00062 nsapi_error_t QUECTEL_M26_CellularStack::socket_bind(nsapi_socket_t handle, const SocketAddress &addr) 00063 { 00064 return NSAPI_ERROR_UNSUPPORTED ; 00065 } 00066 00067 void QUECTEL_M26_CellularStack::socket_closed(int sock_id) 00068 { 00069 CellularSocket *sock = find_socket(sock_id); 00070 if (sock) { 00071 tr_info("Socket closed %d", sock_id); 00072 sock->closed = true; 00073 } 00074 } 00075 00076 void QUECTEL_M26_CellularStack::socket_closed_0() 00077 { 00078 socket_closed(0); 00079 } 00080 00081 void QUECTEL_M26_CellularStack::socket_closed_1() 00082 { 00083 socket_closed(1); 00084 } 00085 00086 void QUECTEL_M26_CellularStack::socket_closed_2() 00087 { 00088 socket_closed(2); 00089 } 00090 00091 void QUECTEL_M26_CellularStack::socket_closed_3() 00092 { 00093 socket_closed(3); 00094 } 00095 00096 void QUECTEL_M26_CellularStack::socket_closed_4() 00097 { 00098 socket_closed(4); 00099 } 00100 00101 void QUECTEL_M26_CellularStack::socket_closed_5() 00102 { 00103 socket_closed(5); 00104 } 00105 00106 void QUECTEL_M26_CellularStack::urc_qiurc() 00107 { 00108 int sock_id = 0; 00109 00110 _at.lock(); 00111 (void) _at.skip_param(); /*<id> AT+QIFGCNT*/ 00112 (void) _at.skip_param(); /*<sc> 1 Client, 2 Server*/ 00113 sock_id = _at.read_int(); 00114 (void) _at.skip_param(); /*<num>*/ 00115 (void) _at.skip_param(); /*<len>*/ 00116 (void) _at.skip_param(); /*<tlen>*/ 00117 _at.unlock(); 00118 00119 for (int i = 0; i < get_max_socket_count(); i++) { 00120 CellularSocket *sock = _socket[i]; 00121 if (sock && sock->id == sock_id) { 00122 if (sock->_cb) { 00123 sock->_cb(sock->_data); 00124 } 00125 break; 00126 } 00127 } 00128 } 00129 00130 nsapi_error_t QUECTEL_M26_CellularStack::socket_stack_init() 00131 { 00132 int tcpip_mode = 1; 00133 int mux_mode = 0; 00134 int cache_mode = 0; 00135 nsapi_error_t ret_val; 00136 00137 tr_debug("QUECTEL_M26_CellularStack:%s:%u: START ", __FUNCTION__, __LINE__); 00138 _at.lock(); 00139 00140 /*AT+QIFGCNT=0*/ 00141 _at.at_cmd_discard("+QIFGCNT", "=", "%d", 0); 00142 00143 ret_val = _at.get_last_error(); 00144 if (ret_val != NSAPI_ERROR_OK ) { 00145 _at.unlock(); 00146 return NSAPI_ERROR_DEVICE_ERROR ; 00147 } 00148 #if 0 00149 if (_pwd && _uname) { 00150 _at.at_cmd_discard("+QICSGP", "=", "%d%s%s%s", 1, _apn, _uname, _pwd); 00151 } else { 00152 _at.at_cmd_discard("+QICSGP", "=", "%d%s", 1, _apn); 00153 } 00154 00155 #endif 00156 ret_val = _at.get_last_error(); 00157 if (ret_val != NSAPI_ERROR_OK ) { 00158 _at.unlock(); 00159 return NSAPI_ERROR_DEVICE_ERROR ; 00160 } 00161 00162 /*AT+QIMODE=0 Set transparent mode*/ 00163 _at.cmd_start_stop("+QIMODE", "?"); 00164 00165 _at.resp_start("+QIMODE:"); 00166 if (_at.info_resp()) { 00167 tcpip_mode = _at.read_int(); 00168 } 00169 _at.resp_stop(); 00170 if (tcpip_mode) { 00171 _at.at_cmd_discard("+QIMODE", "=", "%d", 0); 00172 } 00173 ret_val = _at.get_last_error(); 00174 if (ret_val != NSAPI_ERROR_OK ) { 00175 _at.unlock(); 00176 return NSAPI_ERROR_DEVICE_ERROR ; 00177 } 00178 00179 /*AT+QIMUX=1*/ 00180 _at.cmd_start_stop("+QIMUX", "?"); 00181 00182 _at.resp_start("+QIMUX:"); 00183 if (_at.info_resp()) { 00184 mux_mode = _at.read_int(); 00185 } 00186 _at.resp_stop(); 00187 if (!mux_mode) { 00188 _at.at_cmd_discard("+QIMUX", "=", "%d", 1); 00189 } 00190 ret_val = _at.get_last_error(); 00191 if (ret_val != NSAPI_ERROR_OK ) { 00192 _at.unlock(); 00193 return NSAPI_ERROR_DEVICE_ERROR ; 00194 } 00195 00196 /*AT+QINDI=2*/ 00197 _at.cmd_start_stop("+QINDI", "?"); 00198 00199 _at.resp_start(); 00200 if (_at.info_resp()) { 00201 cache_mode = _at.read_int(); 00202 } 00203 _at.resp_stop(); 00204 if (cache_mode != 2) { 00205 _at.at_cmd_discard("+QINDI", "=", "%d", 2); 00206 } 00207 00208 ret_val = _at.get_last_error(); 00209 if (ret_val != NSAPI_ERROR_OK ) { 00210 _at.unlock(); 00211 return NSAPI_ERROR_DEVICE_ERROR ; 00212 } 00213 00214 _at.unlock(); 00215 00216 tr_debug("QUECTEL_M26_CellularStack:%s:%u: SUCCESS ", __FUNCTION__, __LINE__); 00217 return NSAPI_ERROR_OK ; 00218 } 00219 00220 int QUECTEL_M26_CellularStack::get_max_socket_count() 00221 { 00222 return M26_SOCKET_MAX; 00223 } 00224 00225 bool QUECTEL_M26_CellularStack::is_protocol_supported(nsapi_protocol_t protocol) 00226 { 00227 return (protocol == NSAPI_UDP || protocol == NSAPI_TCP ); 00228 } 00229 00230 nsapi_error_t QUECTEL_M26_CellularStack::socket_close_impl(int sock_id) 00231 { 00232 tr_debug("QUECTEL_M26_CellularStack:%s:%u:", __FUNCTION__, __LINE__); 00233 00234 return _at.at_cmd_discard("+QICLOSE", "=", "%d", sock_id); 00235 } 00236 00237 void QUECTEL_M26_CellularStack::handle_open_socket_response(int &modem_connect_id, int &err) 00238 { 00239 char status[15]; 00240 tr_debug("QUECTEL_M26_CellularStack:%s:%u: START", __FUNCTION__, __LINE__); 00241 00242 _at.resp_start("ALREADY CONNECT"); 00243 if (_at.info_resp()) { 00244 /* ALREADY CONNECT: The request socket already connected */ 00245 err = 0; 00246 return; 00247 } 00248 _at.resp_stop(); 00249 if (_at.get_last_error() != NSAPI_ERROR_OK ) { 00250 /* ERROR: The command format error */ 00251 err = 1; 00252 return; 00253 } 00254 00255 tr_debug("QUECTEL_M26_CellularStack:%s:%u: OK", __FUNCTION__, __LINE__); 00256 00257 _at.set_at_timeout(M26_CREATE_SOCKET_TIMEOUT); 00258 _at.resp_start(); 00259 _at.set_stop_tag("\r\n"); 00260 modem_connect_id = _at.read_int(); 00261 _at.read_string(status, sizeof(status), true); 00262 _at.resp_stop(); 00263 _at.restore_at_timeout(); 00264 00265 if ((!strcmp(status, "CONNECT FAIL")) || (_at.get_last_error() != NSAPI_ERROR_OK )) { 00266 err = 1; 00267 return; 00268 } 00269 00270 err = 0; 00271 tr_debug("QUECTEL_M26_CellularStack:%s:%u: END [%s, %d]", __FUNCTION__, __LINE__, status, err); 00272 } 00273 00274 00275 nsapi_error_t QUECTEL_M26_CellularStack::socket_connect(nsapi_socket_t handle, const SocketAddress &address) 00276 { 00277 CellularSocket *socket = (CellularSocket *)handle; 00278 00279 int modem_connect_id = -1; 00280 int err = -1; 00281 00282 int request_connect_id = find_socket_index(socket); 00283 // assert here as its a programming error if the socket container doesn't contain 00284 // specified handle 00285 MBED_ASSERT(request_connect_id != -1); 00286 00287 _at.lock(); 00288 if (socket->proto == NSAPI_TCP ) { 00289 _at.cmd_start_stop("+QIOPEN", "=", "%d%s%s%d", request_connect_id, "TCP", 00290 address.get_ip_address(), address.get_port());; 00291 00292 handle_open_socket_response(modem_connect_id, err); 00293 00294 if ((_at.get_last_error() == NSAPI_ERROR_OK ) && err) { 00295 _at.at_cmd_discard("+QICLOSE", "=", "%d", modem_connect_id); 00296 00297 _at.cmd_start_stop("+QIOPEN", "=", "%d%s%s%d", request_connect_id, "TCP", 00298 address.get_ip_address(), address.get_port()); 00299 00300 handle_open_socket_response(modem_connect_id, err); 00301 } 00302 } 00303 00304 // If opened successfully BUT not requested one, close it 00305 if (!err && (modem_connect_id != request_connect_id)) { 00306 _at.at_cmd_discard("+QICLOSE", "=", "%d", modem_connect_id); 00307 } 00308 00309 nsapi_error_t ret_val = _at.get_last_error(); 00310 _at.unlock(); 00311 00312 if ((ret_val == NSAPI_ERROR_OK ) && (modem_connect_id == request_connect_id)) { 00313 socket->remoteAddress = address; 00314 socket->connected = true; 00315 return NSAPI_ERROR_OK ; 00316 } 00317 00318 return NSAPI_ERROR_NO_CONNECTION ; 00319 } 00320 00321 nsapi_error_t QUECTEL_M26_CellularStack::create_socket_impl(CellularSocket *socket) 00322 { 00323 // This modem is a special case. It takes in the socket ID rather than spitting 00324 // it out. So we will first try to use the index of the socket construct as the id 00325 // but if another opened socket is already opened with that id we will pick the next 00326 // id which is not in use 00327 bool duplicate = false; 00328 int potential_sid = -1; 00329 int index = find_socket_index(socket); 00330 00331 for (int i = 0; i < get_max_socket_count(); i++) { 00332 CellularSocket *sock = _socket[i]; 00333 if (sock && sock != socket && sock->id == index) { 00334 duplicate = true; 00335 } else if (duplicate && !sock) { 00336 potential_sid = i; 00337 break; 00338 } 00339 } 00340 00341 if (duplicate) { 00342 index = potential_sid; 00343 } 00344 00345 int request_connect_id = index; 00346 int modem_connect_id = request_connect_id; 00347 int err = -1; 00348 nsapi_error_t ret_val; 00349 tr_debug("QUECTEL_M26_CellularStack:%s:%u:[%d,%d]", __FUNCTION__, __LINE__, socket->proto, socket->connected); 00350 00351 if (socket->connected) { 00352 _at.cmd_start_stop("+QIOPEN", "=", "%d%s%s%d", request_connect_id, (socket->proto == NSAPI_TCP ) ? "TCP" : "UDP", 00353 socket->remoteAddress.get_ip_address(), socket->remoteAddress.get_port()); 00354 00355 handle_open_socket_response(modem_connect_id, err); 00356 00357 /* Close and retry if socket create fail */ 00358 if ((_at.get_last_error() != NSAPI_ERROR_OK ) || err) { 00359 _at.at_cmd_discard("+QICLOSE", "=", "%d", modem_connect_id); 00360 00361 _at.cmd_start_stop("+QIOPEN", "=", "%d%s%s%d", request_connect_id, (socket->proto == NSAPI_TCP ) ? "TCP" : "UDP", 00362 socket->remoteAddress.get_ip_address(), socket->remoteAddress.get_port()); 00363 00364 handle_open_socket_response(modem_connect_id, err); 00365 } 00366 00367 /* If opened successfully BUT not requested one, close it */ 00368 if (!err && (modem_connect_id != request_connect_id)) { 00369 _at.at_cmd_discard("+QICLOSE", "=", "%d", modem_connect_id); 00370 } 00371 00372 ret_val = _at.get_last_error(); 00373 if ((ret_val == NSAPI_ERROR_OK ) && (modem_connect_id == request_connect_id)) { 00374 socket->id = request_connect_id; 00375 } 00376 return ret_val; 00377 } else { 00378 ret_val = NSAPI_ERROR_OK ; 00379 } 00380 00381 tr_debug("QUECTEL_M26_CellularStack:%s:%u: END [%d]", __FUNCTION__, __LINE__, ret_val); 00382 return ret_val; 00383 } 00384 00385 00386 nsapi_size_or_error_t QUECTEL_M26_CellularStack::socket_sendto_impl(CellularSocket *socket, const SocketAddress &address, 00387 const void *data, nsapi_size_t size) 00388 { 00389 int sent_len = size; 00390 int sent_acked = 0; 00391 int sent_nacked = 0; 00392 int sent_len_before = 0; 00393 int sent_len_after = 0; 00394 nsapi_error_t error; 00395 00396 tr_debug("QUECTEL_M26_CellularStack:%s:%u:[%d-%d]", __FUNCTION__, __LINE__, sent_len, size); 00397 00398 if (sent_len == 0 || size > M26_SENT_BYTE_MAX) { 00399 tr_error("QUECTEL_M26_CellularStack:%s:%u:[NSAPI_ERROR_PARAMETER]", __FUNCTION__, __LINE__); 00400 return NSAPI_ERROR_PARAMETER ; 00401 } 00402 00403 if (socket->id == -1) { 00404 socket->remoteAddress = address; 00405 socket->connected = true; 00406 nsapi_error_t ret_val = create_socket_impl(socket); 00407 if ((ret_val != NSAPI_ERROR_OK ) || (socket->id == -1)) { 00408 tr_error("QUECTEL_M26_CellularStack:%s:%u:[NSAPI_ERROR_NO_SOCKET]", __FUNCTION__, __LINE__); 00409 return NSAPI_ERROR_NO_SOCKET ; 00410 } 00411 } 00412 00413 if (socket->proto == NSAPI_TCP ) { 00414 bool ready_to_send = false; 00415 uint64_t start_time = rtos::Kernel::get_ms_count(); 00416 while (!ready_to_send && start_time < rtos::Kernel::get_ms_count() + SOCKET_SEND_READY_TIMEOUT) { 00417 _at.cmd_start_stop("+QISACK", "=", "%d", socket->id); 00418 _at.resp_start("+QISACK:"); 00419 sent_len_before = _at.read_int(); 00420 sent_acked = _at.read_int(); 00421 (void)sent_acked; 00422 sent_nacked = _at.read_int(); 00423 _at.resp_stop(); 00424 00425 if (_at.get_last_error() != NSAPI_ERROR_OK ) { 00426 tr_error("QUECTEL_M26_CellularStack:%s:%u:[NSAPI_ERROR_DEVICE_ERROR]", __FUNCTION__, __LINE__); 00427 return NSAPI_ERROR_DEVICE_ERROR ; 00428 } 00429 00430 if (sent_nacked == 0) { 00431 ready_to_send = true; 00432 } else { 00433 tr_debug("QUECTEL_M26_CellularStack:%s:%u:[NSAPI_ERROR_WOULD_BLOCK]", __FUNCTION__, __LINE__); 00434 } 00435 } 00436 } 00437 00438 _at.cmd_start_stop("+QISEND", "=", "%d%d", socket->id, sent_len); 00439 00440 _at.resp_start(">"); 00441 _at.write_bytes((uint8_t *)data, sent_len); 00442 _at.resp_start(); 00443 _at.set_stop_tag("\r\n"); 00444 // Possible responses are SEND OK, SEND FAIL or ERROR. 00445 char response[16]; 00446 response[0] = '\0'; 00447 _at.read_string(response, sizeof(response)); 00448 _at.resp_stop(); 00449 if (strcmp(response, "SEND OK") != 0) { 00450 return NSAPI_ERROR_DEVICE_ERROR ; 00451 } 00452 00453 if (_at.get_last_error() != NSAPI_ERROR_OK ) { 00454 tr_error("QUECTEL_M26_CellularStack:%s:%u:[NSAPI_ERROR_DEVICE_ERROR]", __FUNCTION__, __LINE__); 00455 return NSAPI_ERROR_DEVICE_ERROR ; 00456 } 00457 00458 if (socket->proto == NSAPI_TCP ) { 00459 _at.cmd_start_stop("+QISACK", "=", "%d", socket->id); 00460 00461 _at.resp_start("+QISACK:"); 00462 sent_len_after = _at.read_int(); 00463 sent_acked = _at.read_int(); 00464 (void)sent_acked; // avoid compile warning, used only for debugging 00465 sent_nacked = _at.read_int(); 00466 _at.resp_stop(); 00467 00468 error = _at.get_last_error(); 00469 if (error == NSAPI_ERROR_OK ) { 00470 sent_len = sent_len_after - sent_len_before; 00471 tr_debug("QUECTEL_M26_CellularStack:%s:%u:[TCP: BA %d-%d, ACK %d-%d,LEN %d-%d]", __FUNCTION__, __LINE__, sent_len_before, sent_len_after, sent_acked, sent_nacked, sent_len, size); 00472 return sent_len; 00473 } 00474 00475 tr_error("QUECTEL_M26_CellularStack:%s:%u:[TCP: %d]", __FUNCTION__, __LINE__, error); 00476 return error; 00477 } 00478 00479 error = _at.get_last_error(); 00480 if (error == NSAPI_ERROR_OK ) { 00481 tr_debug("QUECTEL_M26_CellularStack:%s:%u:[UDP: %d]", __FUNCTION__, __LINE__, sent_len); 00482 return sent_len; 00483 } 00484 00485 tr_debug("QUECTEL_M26_CellularStack:%s:%u:[ERROR: %d]", __FUNCTION__, __LINE__, error); 00486 return error; 00487 } 00488 00489 nsapi_size_or_error_t QUECTEL_M26_CellularStack::socket_recvfrom_impl(CellularSocket *socket, SocketAddress *address, 00490 void *buffer, nsapi_size_t size) 00491 { 00492 int port; 00493 char type[8]; 00494 char ip_address[NSAPI_IP_SIZE + 1]; 00495 00496 tr_debug("QUECTEL_M26_CellularStack:%s:%u:[%d]", __FUNCTION__, __LINE__, size); 00497 00498 uint64_t start_time = rtos::Kernel::get_ms_count(); 00499 nsapi_size_t len = 0; 00500 for (; len < size;) { 00501 unsigned int read_len = (size - len > M26_RECV_BYTE_MAX) ? M26_RECV_BYTE_MAX : size - len; 00502 00503 _at.cmd_start_stop("+QIRD", "=", "%d%d%d%d", 0, 1, socket->id, read_len); 00504 00505 nsapi_size_t recv_len = 0; 00506 _at.resp_start("+QIRD:"); 00507 if (_at.info_resp()) { 00508 _at.set_delimiter(':'); 00509 _at.read_string(ip_address, sizeof(ip_address)); 00510 _at.set_default_delimiter(); 00511 port = _at.read_int(); 00512 _at.read_string(type, sizeof(type)); 00513 recv_len = _at.read_int(); 00514 _at.read_bytes((uint8_t *)buffer + len, recv_len); 00515 len += recv_len; 00516 } 00517 _at.resp_stop(); 00518 00519 if (_at.get_last_error() != NSAPI_ERROR_OK ) { 00520 tr_warn("QUECTEL_M26_CellularStack:%s:%u:[ERROR NSAPI_ERROR_OK]", __FUNCTION__, __LINE__); 00521 return NSAPI_ERROR_DEVICE_ERROR ; 00522 } 00523 00524 if (rtos::Kernel::get_ms_count() > start_time + SOCKET_READ_TIMEOUT) { 00525 tr_warn("QUECTEL_M26_CellularStack:%s:%u:[ERROR NSAPI_ERROR_TIMEOUT]", __FUNCTION__, __LINE__); 00526 return NSAPI_ERROR_TIMEOUT ; 00527 } 00528 00529 if (recv_len == 0 || recv_len < read_len) { 00530 break; 00531 } 00532 } 00533 00534 if (len == 0) { 00535 tr_debug("QUECTEL_M26_CellularStack:%s:%u:[ERROR NSAPI_ERROR_WOULD_BLOCK]", __FUNCTION__, __LINE__); 00536 return NSAPI_ERROR_WOULD_BLOCK ; 00537 } 00538 00539 if (address) { 00540 address->set_ip_address(ip_address); 00541 address->set_port(port); 00542 } 00543 00544 tr_debug("QUECTEL_M26_CellularStack:%s:%u:[%d]", __FUNCTION__, __LINE__, len); 00545 return len; 00546 }
Generated on Tue Jul 12 2022 13:54:46 by
