Kenji Arai / mbed-os_TYBLE16

Dependents:   TYBLE16_simple_data_logger TYBLE16_MP3_Air

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers QUECTEL_M26_CellularStack.cpp Source File

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 }