diff --git a/drivers/UARTSerial.cpp b/drivers/UARTSerial.cpp index 69aaa2fbad..2c590c1276 100644 --- a/drivers/UARTSerial.cpp +++ b/drivers/UARTSerial.cpp @@ -138,36 +138,47 @@ ssize_t UARTSerial::write(const void* buffer, size_t length) size_t data_written = 0; const char *buf_ptr = static_cast(buffer); + if (length == 0) { + return 0; + } + api_lock(); - while (_txbuf.full()) { - if (!_blocking) { - api_unlock(); - return -EAGAIN; - } - api_unlock(); - wait_ms(1); // XXX todo - proper wait, WFE for non-rtos ? - api_lock(); - } + // Unlike read, we should write the whole thing if blocking. POSIX only + // allows partial as a side-effect of signal handling; it normally tries to + // write everything if blocking. Without signals we can always write all. + while (data_written < length) { - while (data_written < length && !_txbuf.full()) { - _txbuf.push(*buf_ptr++); - data_written++; - } - - core_util_critical_section_enter(); - if (!_tx_irq_enabled) { - UARTSerial::tx_irq(); // only write to hardware in one place - if (!_txbuf.empty()) { - SerialBase::attach(callback(this, &UARTSerial::tx_irq), TxIrq); - _tx_irq_enabled = true; + if (_txbuf.full()) { + if (!_blocking) { + break; + } + do { + api_unlock(); + wait_ms(1); // XXX todo - proper wait, WFE for non-rtos ? + api_lock(); + } while (_txbuf.full()); } + + while (data_written < length && !_txbuf.full()) { + _txbuf.push(*buf_ptr++); + data_written++; + } + + core_util_critical_section_enter(); + if (!_tx_irq_enabled) { + UARTSerial::tx_irq(); // only write to hardware in one place + if (!_txbuf.empty()) { + SerialBase::attach(callback(this, &UARTSerial::tx_irq), TxIrq); + _tx_irq_enabled = true; + } + } + core_util_critical_section_exit(); } - core_util_critical_section_exit(); api_unlock(); - return data_written; + return data_written != 0 ? (ssize_t) data_written : (ssize_t) -EAGAIN; } ssize_t UARTSerial::read(void* buffer, size_t length) @@ -176,6 +187,10 @@ ssize_t UARTSerial::read(void* buffer, size_t length) char *ptr = static_cast(buffer); + if (length == 0) { + return 0; + } + api_lock(); while (_rxbuf.empty()) { diff --git a/drivers/UARTSerial.h b/drivers/UARTSerial.h index afd98c107b..026215b36d 100644 --- a/drivers/UARTSerial.h +++ b/drivers/UARTSerial.h @@ -70,6 +70,12 @@ public: using FileHandle::writable; /** Write the contents of a buffer to a file + * + * Follows POSIX semantics: + * + * * if blocking, block until all data is written + * * if no data can be written, and non-blocking set, return -EAGAIN + * * if some data can be written, and non-blocking set, write partial * * @param buffer The buffer to write from * @param length The number of bytes to write diff --git a/features/netsocket/TCPSocket.cpp b/features/netsocket/TCPSocket.cpp index 89b8260400..7897f8acca 100644 --- a/features/netsocket/TCPSocket.cpp +++ b/features/netsocket/TCPSocket.cpp @@ -105,7 +105,9 @@ nsapi_error_t TCPSocket::connect(const char *host, uint16_t port) nsapi_size_or_error_t TCPSocket::send(const void *data, nsapi_size_t size) { _lock.lock(); + const uint8_t *data_ptr = static_cast(data); nsapi_size_or_error_t ret; + nsapi_size_t written = 0; // If this assert is hit then there are two threads // performing a send at the same time which is undefined @@ -113,6 +115,9 @@ nsapi_size_or_error_t TCPSocket::send(const void *data, nsapi_size_t size) MBED_ASSERT(!_write_in_progress); _write_in_progress = true; + // Unlike recv, we should write the whole thing if blocking. POSIX only + // allows partial as a side-effect of signal handling; it normally tries to + // write everything if blocking. Without signals we can always write all. while (true) { if (!_socket) { ret = NSAPI_ERROR_NO_SOCKET; @@ -120,10 +125,16 @@ nsapi_size_or_error_t TCPSocket::send(const void *data, nsapi_size_t size) } _pending = 0; - ret = _stack->socket_send(_socket, data, size); - if ((_timeout == 0) || (ret != NSAPI_ERROR_WOULD_BLOCK)) { + ret = _stack->socket_send(_socket, data_ptr + written, size - written); + if (ret >= 0) { + written += ret; + if (written >= size) { + break; + } + } + if (_timeout == 0) { break; - } else { + } else if (ret == NSAPI_ERROR_WOULD_BLOCK) { uint32_t flag; // Release lock before blocking so other threads @@ -134,15 +145,22 @@ nsapi_size_or_error_t TCPSocket::send(const void *data, nsapi_size_t size) if (flag & osFlagsError) { // Timeout break - ret = NSAPI_ERROR_WOULD_BLOCK; break; } + } else if (ret < 0) { + break; } } _write_in_progress = false; _lock.unlock(); - return ret; + if (ret <= 0 && ret != NSAPI_ERROR_WOULD_BLOCK) { + return ret; + } else if (written == 0) { + return NSAPI_ERROR_WOULD_BLOCK; + } else { + return written; + } } nsapi_size_or_error_t TCPSocket::recv(void *data, nsapi_size_t size) diff --git a/features/netsocket/TCPSocket.h b/features/netsocket/TCPSocket.h index 6005121484..7b12ba3a9e 100644 --- a/features/netsocket/TCPSocket.h +++ b/features/netsocket/TCPSocket.h @@ -88,9 +88,9 @@ public: * The socket must be connected to a remote host. Returns the number of * bytes sent from the buffer. * - * By default, send blocks until data is sent. If socket is set to - * non-blocking or times out, NSAPI_ERROR_WOULD_BLOCK is returned - * immediately. + * By default, send blocks until all data is sent. If socket is set to + * non-blocking or times out, a partial amount can be written. + * NSAPI_ERROR_WOULD_BLOCK is returned if no data was written. * * @param data Buffer of data to send to the host * @param size Size of the buffer in bytes @@ -104,9 +104,9 @@ public: * The socket must be connected to a remote host. Returns the number of * bytes received into the buffer. * - * By default, recv blocks until data is sent. If socket is set to - * non-blocking or times out, NSAPI_ERROR_WOULD_BLOCK is returned - * immediately. + * By default, recv blocks until some data is received. If socket is set to + * non-blocking or times out, NSAPI_ERROR_WOULD_BLOCK can be returned to + * indicate no data. * * @param data Destination buffer for data received from the host * @param size Size of the buffer in bytes diff --git a/platform/FileHandle.h b/platform/FileHandle.h index 6a769b4b8f..ecec462f7a 100644 --- a/platform/FileHandle.h +++ b/platform/FileHandle.h @@ -51,7 +51,7 @@ public: * Devices acting as FileHandles should follow POSIX semantics: * * * if no data is available, and non-blocking set return -EAGAIN - * * if no data is available, and blocking set, wait until data is available + * * if no data is available, and blocking set, wait until some data is available * * If any data is available, call returns immediately * * @param buffer The buffer to read in to @@ -61,6 +61,12 @@ public: virtual ssize_t read(void *buffer, size_t size) = 0; /** Write the contents of a buffer to a file + * + * Devices acting as FileHandles should follow POSIX semantics: + * + * * if blocking, block until all data is written + * * if no data can be written, and non-blocking set, return -EAGAIN + * * if some data can be written, and non-blocking set, write partial * * @param buffer The buffer to write from * @param size The number of bytes to write