diff --git a/TESTS/netsocket/udp/udpsocket_echotest_burst.cpp b/TESTS/netsocket/udp/udpsocket_echotest_burst.cpp index 9e5c6c9bfc..87742c64db 100644 --- a/TESTS/netsocket/udp/udpsocket_echotest_burst.cpp +++ b/TESTS/netsocket/udp/udpsocket_echotest_burst.cpp @@ -100,7 +100,9 @@ void UDPSOCKET_ECHOTEST_BURST() if (check_oversized_packets(sent, tx_buffers[x].len)) { TEST_IGNORE_MESSAGE("This device does not handle oversized packets"); } - TEST_ASSERT_EQUAL(tx_buffers[x].len, sent); + if (sent != NSAPI_ERROR_NO_MEMORY) { + TEST_ASSERT_EQUAL(tx_buffers[x].len, sent); + } } bt_total = 0; diff --git a/features/cellular/framework/AT/AT_CellularStack.cpp b/features/cellular/framework/AT/AT_CellularStack.cpp index bfa502f86d..0606da4851 100644 --- a/features/cellular/framework/AT/AT_CellularStack.cpp +++ b/features/cellular/framework/AT/AT_CellularStack.cpp @@ -233,8 +233,10 @@ nsapi_error_t AT_CellularStack::socket_close(nsapi_socket_t handle) tr_info("Socket %d close (id %d, started %d, error %d)", index, sock_id, socket->started, err); } + _socket_mutex.lock(); _socket[index] = NULL; delete socket; + _socket_mutex.unlock(); _at.unlock(); diff --git a/features/cellular/framework/AT/AT_CellularStack.h b/features/cellular/framework/AT/AT_CellularStack.h index 8aef39a143..db966ee272 100644 --- a/features/cellular/framework/AT/AT_CellularStack.h +++ b/features/cellular/framework/AT/AT_CellularStack.h @@ -114,7 +114,8 @@ protected: started(false), tx_ready(false), tls_socket(false), - pending_bytes(0) + pending_bytes(0), + txfull_event(false) { } // Socket identifier, generally it will be the socket ID assigned by the @@ -132,6 +133,7 @@ protected: bool tx_ready; // socket is ready for sending on modem stack bool tls_socket; // socket uses modem's internal TLS socket functionality nsapi_size_t pending_bytes; // The number of received bytes pending + bool txfull_event; // socket event after wouldblock }; /** @@ -231,10 +233,10 @@ private: int get_socket_index_by_port(uint16_t port); - // mutex for write/read to a _socket array, needed when multiple threads may open sockets simultaneously +protected: + // mutex for write/read to a _socket array, needed when multiple threads may use sockets simultaneously PlatformMutex _socket_mutex; -protected: ATHandler &_at; AT_CellularDevice &_device; diff --git a/features/cellular/framework/targets/QUECTEL/BC95/QUECTEL_BC95_CellularStack.cpp b/features/cellular/framework/targets/QUECTEL/BC95/QUECTEL_BC95_CellularStack.cpp index badf70c3a5..2f4e3550a4 100644 --- a/features/cellular/framework/targets/QUECTEL/BC95/QUECTEL_BC95_CellularStack.cpp +++ b/features/cellular/framework/targets/QUECTEL/BC95/QUECTEL_BC95_CellularStack.cpp @@ -15,17 +15,28 @@ * limitations under the License. */ +#include "rtos/ThisThread.h" +#include "mbed_error.h" +#include "platform/mbed_atomic.h" +#include "events/EventQueue.h" +#include "events/mbed_shared_queues.h" + #include "QUECTEL_BC95_CellularStack.h" #include "CellularUtil.h" #include "CellularLog.h" #define PACKET_SIZE_MAX 1358 +#define TXFULL_EVENT_TIMEOUT (1 * 1000) // ms + +#define AT_UPLINK_BUSY 159 +#define AT_UART_BUFFER_ERROR 536 +#define AT_BACK_OFF_TIMER 537 using namespace mbed; using namespace mbed_cellular_util; QUECTEL_BC95_CellularStack::QUECTEL_BC95_CellularStack(ATHandler &atHandler, int cid, nsapi_ip_stack_t stack_type, AT_CellularDevice &device) : - AT_CellularStack(atHandler, cid, stack_type, device) + AT_CellularStack(atHandler, cid, stack_type, device), _event_queue(mbed_event_queue()), _txfull_event_id(0) { _at.set_urc_handler("+NSONMI:", mbed::Callback(this, &QUECTEL_BC95_CellularStack::urc_nsonmi)); _at.set_urc_handler("+NSOCLI:", mbed::Callback(this, &QUECTEL_BC95_CellularStack::urc_nsocli)); @@ -33,6 +44,10 @@ QUECTEL_BC95_CellularStack::QUECTEL_BC95_CellularStack(ATHandler &atHandler, int QUECTEL_BC95_CellularStack::~QUECTEL_BC95_CellularStack() { + if (_txfull_event_id) { + _event_queue->cancel(_txfull_event_id); + } + _at.set_urc_handler("+NSONMI:", NULL); _at.set_urc_handler("+NSOCLI:", NULL); } @@ -130,6 +145,9 @@ nsapi_error_t QUECTEL_BC95_CellularStack::socket_close_impl(int sock_id) if (sock && sock->closed) { return NSAPI_ERROR_OK; } + + sock->txfull_event = false; + nsapi_error_t err = _at.at_cmd_discard("+NSOCL", "=", "%d", sock_id); tr_info("Close socket: %d error: %d", sock_id, err); @@ -186,6 +204,8 @@ nsapi_size_or_error_t QUECTEL_BC95_CellularStack::socket_sendto_impl(CellularSoc return NSAPI_ERROR_PARAMETER; } + int retry = 0; +retry_send: if (socket->proto == NSAPI_UDP) { _at.cmd_start("AT+NSOST="); _at.write_int(socket->id); @@ -212,6 +232,36 @@ nsapi_size_or_error_t QUECTEL_BC95_CellularStack::socket_sendto_impl(CellularSoc return sent_len; } + // check for network congestion + device_err_t err = _at.get_last_device_error(); + if (err.errType == DeviceErrorTypeErrorCME && + (err.errCode == AT_UART_BUFFER_ERROR || err.errCode == AT_BACK_OFF_TIMER) || err.errCode == AT_UPLINK_BUSY) { + if (socket->proto == NSAPI_UDP) { + if (retry < 3) { + retry++; + tr_warn("Socket %d sendto EAGAIN", socket->id); + rtos::ThisThread::sleep_for(30); + _at.clear_error(); + goto retry_send; + } + return NSAPI_ERROR_NO_MEMORY; + } + _socket_mutex.lock(); + if (!socket->txfull_event && !_txfull_event_id) { + tr_warn("socket %d tx full", socket->id); + socket->txfull_event = true; + _txfull_event_id = _event_queue->call_in(TXFULL_EVENT_TIMEOUT, callback(this, &QUECTEL_BC95_CellularStack::txfull_event_timeout)); + if (!_txfull_event_id) { + MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_DRIVER, MBED_ERROR_CODE_ENOMEM), \ + "QUECTEL_BC95_CellularStack::socket_sendto_impl(): unable to add event to queue. Increase \"events.shared-eventsize\"\n"); + _socket_mutex.unlock(); + return NSAPI_ERROR_NO_MEMORY; + } + } + _socket_mutex.unlock(); + return NSAPI_ERROR_WOULD_BLOCK; + } + return _at.get_last_error(); } @@ -234,7 +284,7 @@ nsapi_size_or_error_t QUECTEL_BC95_CellularStack::socket_recvfrom_impl(CellularS _at.read_string(ip_address, sizeof(ip_address)); port = _at.read_int(); recv_len = _at.read_int(); - int hexlen = _at.read_hex_string((char *)buffer, size); + int hexlen = _at.read_hex_string((char *)buffer, recv_len); // remaining length _at.skip_param(); _at.resp_stop(); @@ -253,3 +303,17 @@ nsapi_size_or_error_t QUECTEL_BC95_CellularStack::socket_recvfrom_impl(CellularS } return recv_len; } + +void QUECTEL_BC95_CellularStack::txfull_event_timeout() +{ + _socket_mutex.lock(); + _txfull_event_id = 0; + for (int i = 0; i < get_max_socket_count(); i++) { + CellularSocket *sock = _socket[i]; + if (sock && sock->_cb && sock->txfull_event) { + sock->txfull_event = false; + sock->_cb(sock->_data); + } + } + _socket_mutex.unlock(); +} diff --git a/features/cellular/framework/targets/QUECTEL/BC95/QUECTEL_BC95_CellularStack.h b/features/cellular/framework/targets/QUECTEL/BC95/QUECTEL_BC95_CellularStack.h index 46546edf0f..8229cbfb6e 100644 --- a/features/cellular/framework/targets/QUECTEL/BC95/QUECTEL_BC95_CellularStack.h +++ b/features/cellular/framework/targets/QUECTEL/BC95/QUECTEL_BC95_CellularStack.h @@ -58,6 +58,10 @@ private: // URC handlers void urc_nsonmi(); void urc_nsocli(); + + events::EventQueue *_event_queue; + int _txfull_event_id; + void txfull_event_timeout(); }; } // namespace mbed #endif /* QUECTEL_BC95_CELLULARSTACK_H_ */