mbed-os/drivers/UARTSerial.cpp

424 lines
9.4 KiB
C++
Raw Normal View History

/* mbed Microcontroller Library
* Copyright (c) 2006-2017 ARM Limited
2018-11-09 11:22:52 +00:00
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
2018-10-21 13:26:03 +00:00
#include "drivers/UARTSerial.h"
#if (DEVICE_SERIAL && DEVICE_INTERRUPTIN)
#include "platform/mbed_poll.h"
#if MBED_CONF_RTOS_PRESENT
#include "rtos/ThisThread.h"
#else
#include "platform/mbed_wait_api.h"
#endif
namespace mbed {
Major Refactoring & extensions For keep supporting external APIs with the same name (supposedly there are a larger number of users of those APIs), BufferedSerial and ATParser are being renamed. BufferedSerial becomes UARTSerial, will complement a future USBSerial etc. ATParser becomes ATCmdParser. * UARTSerial moves to /drivers * APN_db.h is moved from platform to cellular/util/. * Original CellularInterface is restored for backward compatability (again, supposedly there are users of that). * A new file, CellularBase is added which will now servce as the base class for all upcoming drivers. * Special restructuring for the driver has been undertaken. This makes a clear cut distinction between an on-board or an off-board implementation. - PPPCellularInterface is a generic network interface that works with a generic FileHandle and PPP. A derived class is needed to pass that FileHandle. - PPPCellularInterface provides some base functionality like network registration, AT setup, PPP connection etc. Lower level job is delegated to the derived classes and various modem specific APIs are provided which are supposed to be overridden. - UARTCellularInterface is derived from PPPCellularInterface. It constructs a FileHandle and passes it back to PPPCellularInterface as well as provides modem hangupf functionality. In future we could proive a USBInterface that would derive from PPPCellularInterface and could pass the FileHandle back. - OnboardCellularInterface is derived from UARTCellularInterfae and provides hooks to the target provided implementation of onbard_modem_api.h. An off-board modem, i.e, a modem on a shield has to override the modem_init(), modem_power_up() etc as it cannot use onboard_modem_api.h.
2017-05-23 17:07:24 +00:00
UARTSerial::UARTSerial(PinName tx, PinName rx, int baud) :
2018-05-24 15:58:14 +00:00
SerialBase(tx, rx, baud),
_blocking(true),
_tx_irq_enabled(false),
_rx_irq_enabled(false),
_tx_enabled(true),
_rx_enabled(true),
2018-05-24 15:58:14 +00:00
_dcd_irq(NULL)
{
/* Attatch IRQ routines to the serial device. */
enable_rx_irq();
}
Major Refactoring & extensions For keep supporting external APIs with the same name (supposedly there are a larger number of users of those APIs), BufferedSerial and ATParser are being renamed. BufferedSerial becomes UARTSerial, will complement a future USBSerial etc. ATParser becomes ATCmdParser. * UARTSerial moves to /drivers * APN_db.h is moved from platform to cellular/util/. * Original CellularInterface is restored for backward compatability (again, supposedly there are users of that). * A new file, CellularBase is added which will now servce as the base class for all upcoming drivers. * Special restructuring for the driver has been undertaken. This makes a clear cut distinction between an on-board or an off-board implementation. - PPPCellularInterface is a generic network interface that works with a generic FileHandle and PPP. A derived class is needed to pass that FileHandle. - PPPCellularInterface provides some base functionality like network registration, AT setup, PPP connection etc. Lower level job is delegated to the derived classes and various modem specific APIs are provided which are supposed to be overridden. - UARTCellularInterface is derived from PPPCellularInterface. It constructs a FileHandle and passes it back to PPPCellularInterface as well as provides modem hangupf functionality. In future we could proive a USBInterface that would derive from PPPCellularInterface and could pass the FileHandle back. - OnboardCellularInterface is derived from UARTCellularInterfae and provides hooks to the target provided implementation of onbard_modem_api.h. An off-board modem, i.e, a modem on a shield has to override the modem_init(), modem_power_up() etc as it cannot use onboard_modem_api.h.
2017-05-23 17:07:24 +00:00
UARTSerial::~UARTSerial()
{
Major Refactoring & extensions For keep supporting external APIs with the same name (supposedly there are a larger number of users of those APIs), BufferedSerial and ATParser are being renamed. BufferedSerial becomes UARTSerial, will complement a future USBSerial etc. ATParser becomes ATCmdParser. * UARTSerial moves to /drivers * APN_db.h is moved from platform to cellular/util/. * Original CellularInterface is restored for backward compatability (again, supposedly there are users of that). * A new file, CellularBase is added which will now servce as the base class for all upcoming drivers. * Special restructuring for the driver has been undertaken. This makes a clear cut distinction between an on-board or an off-board implementation. - PPPCellularInterface is a generic network interface that works with a generic FileHandle and PPP. A derived class is needed to pass that FileHandle. - PPPCellularInterface provides some base functionality like network registration, AT setup, PPP connection etc. Lower level job is delegated to the derived classes and various modem specific APIs are provided which are supposed to be overridden. - UARTCellularInterface is derived from PPPCellularInterface. It constructs a FileHandle and passes it back to PPPCellularInterface as well as provides modem hangupf functionality. In future we could proive a USBInterface that would derive from PPPCellularInterface and could pass the FileHandle back. - OnboardCellularInterface is derived from UARTCellularInterfae and provides hooks to the target provided implementation of onbard_modem_api.h. An off-board modem, i.e, a modem on a shield has to override the modem_init(), modem_power_up() etc as it cannot use onboard_modem_api.h.
2017-05-23 17:07:24 +00:00
delete _dcd_irq;
}
Major Refactoring & extensions For keep supporting external APIs with the same name (supposedly there are a larger number of users of those APIs), BufferedSerial and ATParser are being renamed. BufferedSerial becomes UARTSerial, will complement a future USBSerial etc. ATParser becomes ATCmdParser. * UARTSerial moves to /drivers * APN_db.h is moved from platform to cellular/util/. * Original CellularInterface is restored for backward compatability (again, supposedly there are users of that). * A new file, CellularBase is added which will now servce as the base class for all upcoming drivers. * Special restructuring for the driver has been undertaken. This makes a clear cut distinction between an on-board or an off-board implementation. - PPPCellularInterface is a generic network interface that works with a generic FileHandle and PPP. A derived class is needed to pass that FileHandle. - PPPCellularInterface provides some base functionality like network registration, AT setup, PPP connection etc. Lower level job is delegated to the derived classes and various modem specific APIs are provided which are supposed to be overridden. - UARTCellularInterface is derived from PPPCellularInterface. It constructs a FileHandle and passes it back to PPPCellularInterface as well as provides modem hangupf functionality. In future we could proive a USBInterface that would derive from PPPCellularInterface and could pass the FileHandle back. - OnboardCellularInterface is derived from UARTCellularInterfae and provides hooks to the target provided implementation of onbard_modem_api.h. An off-board modem, i.e, a modem on a shield has to override the modem_init(), modem_power_up() etc as it cannot use onboard_modem_api.h.
2017-05-23 17:07:24 +00:00
void UARTSerial::dcd_irq()
{
wake();
}
2017-06-22 11:26:48 +00:00
void UARTSerial::set_baud(int baud)
{
SerialBase::baud(baud);
}
Major Refactoring & extensions For keep supporting external APIs with the same name (supposedly there are a larger number of users of those APIs), BufferedSerial and ATParser are being renamed. BufferedSerial becomes UARTSerial, will complement a future USBSerial etc. ATParser becomes ATCmdParser. * UARTSerial moves to /drivers * APN_db.h is moved from platform to cellular/util/. * Original CellularInterface is restored for backward compatability (again, supposedly there are users of that). * A new file, CellularBase is added which will now servce as the base class for all upcoming drivers. * Special restructuring for the driver has been undertaken. This makes a clear cut distinction between an on-board or an off-board implementation. - PPPCellularInterface is a generic network interface that works with a generic FileHandle and PPP. A derived class is needed to pass that FileHandle. - PPPCellularInterface provides some base functionality like network registration, AT setup, PPP connection etc. Lower level job is delegated to the derived classes and various modem specific APIs are provided which are supposed to be overridden. - UARTCellularInterface is derived from PPPCellularInterface. It constructs a FileHandle and passes it back to PPPCellularInterface as well as provides modem hangupf functionality. In future we could proive a USBInterface that would derive from PPPCellularInterface and could pass the FileHandle back. - OnboardCellularInterface is derived from UARTCellularInterfae and provides hooks to the target provided implementation of onbard_modem_api.h. An off-board modem, i.e, a modem on a shield has to override the modem_init(), modem_power_up() etc as it cannot use onboard_modem_api.h.
2017-05-23 17:07:24 +00:00
void UARTSerial::set_data_carrier_detect(PinName dcd_pin, bool active_high)
{
2018-05-24 15:58:14 +00:00
delete _dcd_irq;
Major Refactoring & extensions For keep supporting external APIs with the same name (supposedly there are a larger number of users of those APIs), BufferedSerial and ATParser are being renamed. BufferedSerial becomes UARTSerial, will complement a future USBSerial etc. ATParser becomes ATCmdParser. * UARTSerial moves to /drivers * APN_db.h is moved from platform to cellular/util/. * Original CellularInterface is restored for backward compatability (again, supposedly there are users of that). * A new file, CellularBase is added which will now servce as the base class for all upcoming drivers. * Special restructuring for the driver has been undertaken. This makes a clear cut distinction between an on-board or an off-board implementation. - PPPCellularInterface is a generic network interface that works with a generic FileHandle and PPP. A derived class is needed to pass that FileHandle. - PPPCellularInterface provides some base functionality like network registration, AT setup, PPP connection etc. Lower level job is delegated to the derived classes and various modem specific APIs are provided which are supposed to be overridden. - UARTCellularInterface is derived from PPPCellularInterface. It constructs a FileHandle and passes it back to PPPCellularInterface as well as provides modem hangupf functionality. In future we could proive a USBInterface that would derive from PPPCellularInterface and could pass the FileHandle back. - OnboardCellularInterface is derived from UARTCellularInterfae and provides hooks to the target provided implementation of onbard_modem_api.h. An off-board modem, i.e, a modem on a shield has to override the modem_init(), modem_power_up() etc as it cannot use onboard_modem_api.h.
2017-05-23 17:07:24 +00:00
_dcd_irq = NULL;
Major Refactoring & extensions For keep supporting external APIs with the same name (supposedly there are a larger number of users of those APIs), BufferedSerial and ATParser are being renamed. BufferedSerial becomes UARTSerial, will complement a future USBSerial etc. ATParser becomes ATCmdParser. * UARTSerial moves to /drivers * APN_db.h is moved from platform to cellular/util/. * Original CellularInterface is restored for backward compatability (again, supposedly there are users of that). * A new file, CellularBase is added which will now servce as the base class for all upcoming drivers. * Special restructuring for the driver has been undertaken. This makes a clear cut distinction between an on-board or an off-board implementation. - PPPCellularInterface is a generic network interface that works with a generic FileHandle and PPP. A derived class is needed to pass that FileHandle. - PPPCellularInterface provides some base functionality like network registration, AT setup, PPP connection etc. Lower level job is delegated to the derived classes and various modem specific APIs are provided which are supposed to be overridden. - UARTCellularInterface is derived from PPPCellularInterface. It constructs a FileHandle and passes it back to PPPCellularInterface as well as provides modem hangupf functionality. In future we could proive a USBInterface that would derive from PPPCellularInterface and could pass the FileHandle back. - OnboardCellularInterface is derived from UARTCellularInterfae and provides hooks to the target provided implementation of onbard_modem_api.h. An off-board modem, i.e, a modem on a shield has to override the modem_init(), modem_power_up() etc as it cannot use onboard_modem_api.h.
2017-05-23 17:07:24 +00:00
if (dcd_pin != NC) {
_dcd_irq = new InterruptIn(dcd_pin);
if (active_high) {
Major Refactoring & extensions For keep supporting external APIs with the same name (supposedly there are a larger number of users of those APIs), BufferedSerial and ATParser are being renamed. BufferedSerial becomes UARTSerial, will complement a future USBSerial etc. ATParser becomes ATCmdParser. * UARTSerial moves to /drivers * APN_db.h is moved from platform to cellular/util/. * Original CellularInterface is restored for backward compatability (again, supposedly there are users of that). * A new file, CellularBase is added which will now servce as the base class for all upcoming drivers. * Special restructuring for the driver has been undertaken. This makes a clear cut distinction between an on-board or an off-board implementation. - PPPCellularInterface is a generic network interface that works with a generic FileHandle and PPP. A derived class is needed to pass that FileHandle. - PPPCellularInterface provides some base functionality like network registration, AT setup, PPP connection etc. Lower level job is delegated to the derived classes and various modem specific APIs are provided which are supposed to be overridden. - UARTCellularInterface is derived from PPPCellularInterface. It constructs a FileHandle and passes it back to PPPCellularInterface as well as provides modem hangupf functionality. In future we could proive a USBInterface that would derive from PPPCellularInterface and could pass the FileHandle back. - OnboardCellularInterface is derived from UARTCellularInterfae and provides hooks to the target provided implementation of onbard_modem_api.h. An off-board modem, i.e, a modem on a shield has to override the modem_init(), modem_power_up() etc as it cannot use onboard_modem_api.h.
2017-05-23 17:07:24 +00:00
_dcd_irq->fall(callback(this, &UARTSerial::dcd_irq));
} else {
Major Refactoring & extensions For keep supporting external APIs with the same name (supposedly there are a larger number of users of those APIs), BufferedSerial and ATParser are being renamed. BufferedSerial becomes UARTSerial, will complement a future USBSerial etc. ATParser becomes ATCmdParser. * UARTSerial moves to /drivers * APN_db.h is moved from platform to cellular/util/. * Original CellularInterface is restored for backward compatability (again, supposedly there are users of that). * A new file, CellularBase is added which will now servce as the base class for all upcoming drivers. * Special restructuring for the driver has been undertaken. This makes a clear cut distinction between an on-board or an off-board implementation. - PPPCellularInterface is a generic network interface that works with a generic FileHandle and PPP. A derived class is needed to pass that FileHandle. - PPPCellularInterface provides some base functionality like network registration, AT setup, PPP connection etc. Lower level job is delegated to the derived classes and various modem specific APIs are provided which are supposed to be overridden. - UARTCellularInterface is derived from PPPCellularInterface. It constructs a FileHandle and passes it back to PPPCellularInterface as well as provides modem hangupf functionality. In future we could proive a USBInterface that would derive from PPPCellularInterface and could pass the FileHandle back. - OnboardCellularInterface is derived from UARTCellularInterfae and provides hooks to the target provided implementation of onbard_modem_api.h. An off-board modem, i.e, a modem on a shield has to override the modem_init(), modem_power_up() etc as it cannot use onboard_modem_api.h.
2017-05-23 17:07:24 +00:00
_dcd_irq->rise(callback(this, &UARTSerial::dcd_irq));
}
}
}
void UARTSerial::set_format(int bits, Parity parity, int stop_bits)
{
api_lock();
SerialBase::format(bits, parity, stop_bits);
api_unlock();
}
#if DEVICE_SERIAL_FC
void UARTSerial::set_flow_control(Flow type, PinName flow1, PinName flow2)
{
api_lock();
SerialBase::set_flow_control(type, flow1, flow2);
api_unlock();
}
#endif
Major Refactoring & extensions For keep supporting external APIs with the same name (supposedly there are a larger number of users of those APIs), BufferedSerial and ATParser are being renamed. BufferedSerial becomes UARTSerial, will complement a future USBSerial etc. ATParser becomes ATCmdParser. * UARTSerial moves to /drivers * APN_db.h is moved from platform to cellular/util/. * Original CellularInterface is restored for backward compatability (again, supposedly there are users of that). * A new file, CellularBase is added which will now servce as the base class for all upcoming drivers. * Special restructuring for the driver has been undertaken. This makes a clear cut distinction between an on-board or an off-board implementation. - PPPCellularInterface is a generic network interface that works with a generic FileHandle and PPP. A derived class is needed to pass that FileHandle. - PPPCellularInterface provides some base functionality like network registration, AT setup, PPP connection etc. Lower level job is delegated to the derived classes and various modem specific APIs are provided which are supposed to be overridden. - UARTCellularInterface is derived from PPPCellularInterface. It constructs a FileHandle and passes it back to PPPCellularInterface as well as provides modem hangupf functionality. In future we could proive a USBInterface that would derive from PPPCellularInterface and could pass the FileHandle back. - OnboardCellularInterface is derived from UARTCellularInterfae and provides hooks to the target provided implementation of onbard_modem_api.h. An off-board modem, i.e, a modem on a shield has to override the modem_init(), modem_power_up() etc as it cannot use onboard_modem_api.h.
2017-05-23 17:07:24 +00:00
int UARTSerial::close()
{
/* Does not let us pass a file descriptor. So how to close ?
* Also, does it make sense to close a device type file descriptor*/
return 0;
}
Major Refactoring & extensions For keep supporting external APIs with the same name (supposedly there are a larger number of users of those APIs), BufferedSerial and ATParser are being renamed. BufferedSerial becomes UARTSerial, will complement a future USBSerial etc. ATParser becomes ATCmdParser. * UARTSerial moves to /drivers * APN_db.h is moved from platform to cellular/util/. * Original CellularInterface is restored for backward compatability (again, supposedly there are users of that). * A new file, CellularBase is added which will now servce as the base class for all upcoming drivers. * Special restructuring for the driver has been undertaken. This makes a clear cut distinction between an on-board or an off-board implementation. - PPPCellularInterface is a generic network interface that works with a generic FileHandle and PPP. A derived class is needed to pass that FileHandle. - PPPCellularInterface provides some base functionality like network registration, AT setup, PPP connection etc. Lower level job is delegated to the derived classes and various modem specific APIs are provided which are supposed to be overridden. - UARTCellularInterface is derived from PPPCellularInterface. It constructs a FileHandle and passes it back to PPPCellularInterface as well as provides modem hangupf functionality. In future we could proive a USBInterface that would derive from PPPCellularInterface and could pass the FileHandle back. - OnboardCellularInterface is derived from UARTCellularInterfae and provides hooks to the target provided implementation of onbard_modem_api.h. An off-board modem, i.e, a modem on a shield has to override the modem_init(), modem_power_up() etc as it cannot use onboard_modem_api.h.
2017-05-23 17:07:24 +00:00
int UARTSerial::isatty()
{
return 1;
}
Major Refactoring & extensions For keep supporting external APIs with the same name (supposedly there are a larger number of users of those APIs), BufferedSerial and ATParser are being renamed. BufferedSerial becomes UARTSerial, will complement a future USBSerial etc. ATParser becomes ATCmdParser. * UARTSerial moves to /drivers * APN_db.h is moved from platform to cellular/util/. * Original CellularInterface is restored for backward compatability (again, supposedly there are users of that). * A new file, CellularBase is added which will now servce as the base class for all upcoming drivers. * Special restructuring for the driver has been undertaken. This makes a clear cut distinction between an on-board or an off-board implementation. - PPPCellularInterface is a generic network interface that works with a generic FileHandle and PPP. A derived class is needed to pass that FileHandle. - PPPCellularInterface provides some base functionality like network registration, AT setup, PPP connection etc. Lower level job is delegated to the derived classes and various modem specific APIs are provided which are supposed to be overridden. - UARTCellularInterface is derived from PPPCellularInterface. It constructs a FileHandle and passes it back to PPPCellularInterface as well as provides modem hangupf functionality. In future we could proive a USBInterface that would derive from PPPCellularInterface and could pass the FileHandle back. - OnboardCellularInterface is derived from UARTCellularInterfae and provides hooks to the target provided implementation of onbard_modem_api.h. An off-board modem, i.e, a modem on a shield has to override the modem_init(), modem_power_up() etc as it cannot use onboard_modem_api.h.
2017-05-23 17:07:24 +00:00
off_t UARTSerial::seek(off_t offset, int whence)
{
/*XXX lseek can be done theoratically, but is it sane to mark positions on a dynamically growing/shrinking
* buffer system (from an interrupt context) */
return -ESPIPE;
}
Major Refactoring & extensions For keep supporting external APIs with the same name (supposedly there are a larger number of users of those APIs), BufferedSerial and ATParser are being renamed. BufferedSerial becomes UARTSerial, will complement a future USBSerial etc. ATParser becomes ATCmdParser. * UARTSerial moves to /drivers * APN_db.h is moved from platform to cellular/util/. * Original CellularInterface is restored for backward compatability (again, supposedly there are users of that). * A new file, CellularBase is added which will now servce as the base class for all upcoming drivers. * Special restructuring for the driver has been undertaken. This makes a clear cut distinction between an on-board or an off-board implementation. - PPPCellularInterface is a generic network interface that works with a generic FileHandle and PPP. A derived class is needed to pass that FileHandle. - PPPCellularInterface provides some base functionality like network registration, AT setup, PPP connection etc. Lower level job is delegated to the derived classes and various modem specific APIs are provided which are supposed to be overridden. - UARTCellularInterface is derived from PPPCellularInterface. It constructs a FileHandle and passes it back to PPPCellularInterface as well as provides modem hangupf functionality. In future we could proive a USBInterface that would derive from PPPCellularInterface and could pass the FileHandle back. - OnboardCellularInterface is derived from UARTCellularInterfae and provides hooks to the target provided implementation of onbard_modem_api.h. An off-board modem, i.e, a modem on a shield has to override the modem_init(), modem_power_up() etc as it cannot use onboard_modem_api.h.
2017-05-23 17:07:24 +00:00
int UARTSerial::sync()
{
api_lock();
while (!_txbuf.empty()) {
api_unlock();
// Doing better than wait would require TxIRQ to also do wake() when becoming empty. Worth it?
wait_ms(1);
api_lock();
}
api_unlock();
return 0;
}
2018-05-24 15:58:14 +00:00
void UARTSerial::sigio(Callback<void()> func)
{
core_util_critical_section_enter();
_sigio_cb = func;
if (_sigio_cb) {
short current_events = poll(0x7FFF);
if (current_events) {
_sigio_cb();
}
}
core_util_critical_section_exit();
}
/* Special synchronous write designed to work from critical section, such
* as in mbed_error_vprintf.
*/
ssize_t UARTSerial::write_unbuffered(const char *buf_ptr, size_t length)
{
while (!_txbuf.empty()) {
tx_irq();
}
for (size_t data_written = 0; data_written < length; data_written++) {
SerialBase::_base_putc(*buf_ptr++);
}
return length;
}
2018-05-24 15:58:14 +00:00
ssize_t UARTSerial::write(const void *buffer, size_t length)
{
size_t data_written = 0;
const char *buf_ptr = static_cast<const char *>(buffer);
Make UARTSerial send all data when blocking Previously, write() was somewhat soft - it only ever made one attempt to wait for buffer space, so it would take as much data as would fit in the buffer in one call. This is not the intent of a POSIX filehandle write. It should try to send everything if blocking, and only send less if interrupted by a signal: - If the O_NONBLOCK flag is clear, write() shall block the calling thread until the data can be accepted. - If the O_NONBLOCK flag is set, write() shall not block the thread. If some data can be written without blocking the thread, write() shall write what it can and return the number of bytes written. Otherwise, it shall return -1 and set errno to [EAGAIN]. This "send all" behaviour is of slightly limited usefulness in POSIX, as you still usually have to worry about the interruption possibility: - If write() is interrupted by a signal before it writes any data, it shall return -1 with errno set to [EINTR]. - If write() is interrupted by a signal after it successfully writes some data, it shall return the number of bytes written. But as mbed OS does not have the possibility of signal interruption, if we strengthen write to write everything, we can make applications' lives easier - they can just do "write(large amount)" confident that it will all go in one call (if no errors). So, rework to make multiple writes to the buffer, blocking as necessary, until all data is written. This change does not apply to read(), which is correct in only blocking until some data is available: - If O_NONBLOCK is set, read() shall return -1 and set errno to [EAGAIN]. - If O_NONBLOCK is clear, read() shall block the calling thread until some data becomes available. - The use of the O_NONBLOCK flag has no effect if there is some data available.
2017-11-09 12:30:55 +00:00
if (length == 0) {
return 0;
}
if (core_util_in_critical_section()) {
return write_unbuffered(buf_ptr, length);
}
api_lock();
Make UARTSerial send all data when blocking Previously, write() was somewhat soft - it only ever made one attempt to wait for buffer space, so it would take as much data as would fit in the buffer in one call. This is not the intent of a POSIX filehandle write. It should try to send everything if blocking, and only send less if interrupted by a signal: - If the O_NONBLOCK flag is clear, write() shall block the calling thread until the data can be accepted. - If the O_NONBLOCK flag is set, write() shall not block the thread. If some data can be written without blocking the thread, write() shall write what it can and return the number of bytes written. Otherwise, it shall return -1 and set errno to [EAGAIN]. This "send all" behaviour is of slightly limited usefulness in POSIX, as you still usually have to worry about the interruption possibility: - If write() is interrupted by a signal before it writes any data, it shall return -1 with errno set to [EINTR]. - If write() is interrupted by a signal after it successfully writes some data, it shall return the number of bytes written. But as mbed OS does not have the possibility of signal interruption, if we strengthen write to write everything, we can make applications' lives easier - they can just do "write(large amount)" confident that it will all go in one call (if no errors). So, rework to make multiple writes to the buffer, blocking as necessary, until all data is written. This change does not apply to read(), which is correct in only blocking until some data is available: - If O_NONBLOCK is set, read() shall return -1 and set errno to [EAGAIN]. - If O_NONBLOCK is clear, read() shall block the calling thread until some data becomes available. - The use of the O_NONBLOCK flag has no effect if there is some data available.
2017-11-09 12:30:55 +00:00
// 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) {
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());
}
Make UARTSerial send all data when blocking Previously, write() was somewhat soft - it only ever made one attempt to wait for buffer space, so it would take as much data as would fit in the buffer in one call. This is not the intent of a POSIX filehandle write. It should try to send everything if blocking, and only send less if interrupted by a signal: - If the O_NONBLOCK flag is clear, write() shall block the calling thread until the data can be accepted. - If the O_NONBLOCK flag is set, write() shall not block the thread. If some data can be written without blocking the thread, write() shall write what it can and return the number of bytes written. Otherwise, it shall return -1 and set errno to [EAGAIN]. This "send all" behaviour is of slightly limited usefulness in POSIX, as you still usually have to worry about the interruption possibility: - If write() is interrupted by a signal before it writes any data, it shall return -1 with errno set to [EINTR]. - If write() is interrupted by a signal after it successfully writes some data, it shall return the number of bytes written. But as mbed OS does not have the possibility of signal interruption, if we strengthen write to write everything, we can make applications' lives easier - they can just do "write(large amount)" confident that it will all go in one call (if no errors). So, rework to make multiple writes to the buffer, blocking as necessary, until all data is written. This change does not apply to read(), which is correct in only blocking until some data is available: - If O_NONBLOCK is set, read() shall return -1 and set errno to [EAGAIN]. - If O_NONBLOCK is clear, read() shall block the calling thread until some data becomes available. - The use of the O_NONBLOCK flag has no effect if there is some data available.
2017-11-09 12:30:55 +00:00
while (data_written < length && !_txbuf.full()) {
_txbuf.push(*buf_ptr++);
data_written++;
}
Make UARTSerial send all data when blocking Previously, write() was somewhat soft - it only ever made one attempt to wait for buffer space, so it would take as much data as would fit in the buffer in one call. This is not the intent of a POSIX filehandle write. It should try to send everything if blocking, and only send less if interrupted by a signal: - If the O_NONBLOCK flag is clear, write() shall block the calling thread until the data can be accepted. - If the O_NONBLOCK flag is set, write() shall not block the thread. If some data can be written without blocking the thread, write() shall write what it can and return the number of bytes written. Otherwise, it shall return -1 and set errno to [EAGAIN]. This "send all" behaviour is of slightly limited usefulness in POSIX, as you still usually have to worry about the interruption possibility: - If write() is interrupted by a signal before it writes any data, it shall return -1 with errno set to [EINTR]. - If write() is interrupted by a signal after it successfully writes some data, it shall return the number of bytes written. But as mbed OS does not have the possibility of signal interruption, if we strengthen write to write everything, we can make applications' lives easier - they can just do "write(large amount)" confident that it will all go in one call (if no errors). So, rework to make multiple writes to the buffer, blocking as necessary, until all data is written. This change does not apply to read(), which is correct in only blocking until some data is available: - If O_NONBLOCK is set, read() shall return -1 and set errno to [EAGAIN]. - If O_NONBLOCK is clear, read() shall block the calling thread until some data becomes available. - The use of the O_NONBLOCK flag has no effect if there is some data available.
2017-11-09 12:30:55 +00:00
core_util_critical_section_enter();
if (_tx_enabled && !_tx_irq_enabled) {
Make UARTSerial send all data when blocking Previously, write() was somewhat soft - it only ever made one attempt to wait for buffer space, so it would take as much data as would fit in the buffer in one call. This is not the intent of a POSIX filehandle write. It should try to send everything if blocking, and only send less if interrupted by a signal: - If the O_NONBLOCK flag is clear, write() shall block the calling thread until the data can be accepted. - If the O_NONBLOCK flag is set, write() shall not block the thread. If some data can be written without blocking the thread, write() shall write what it can and return the number of bytes written. Otherwise, it shall return -1 and set errno to [EAGAIN]. This "send all" behaviour is of slightly limited usefulness in POSIX, as you still usually have to worry about the interruption possibility: - If write() is interrupted by a signal before it writes any data, it shall return -1 with errno set to [EINTR]. - If write() is interrupted by a signal after it successfully writes some data, it shall return the number of bytes written. But as mbed OS does not have the possibility of signal interruption, if we strengthen write to write everything, we can make applications' lives easier - they can just do "write(large amount)" confident that it will all go in one call (if no errors). So, rework to make multiple writes to the buffer, blocking as necessary, until all data is written. This change does not apply to read(), which is correct in only blocking until some data is available: - If O_NONBLOCK is set, read() shall return -1 and set errno to [EAGAIN]. - If O_NONBLOCK is clear, read() shall block the calling thread until some data becomes available. - The use of the O_NONBLOCK flag has no effect if there is some data available.
2017-11-09 12:30:55 +00:00
UARTSerial::tx_irq(); // only write to hardware in one place
if (!_txbuf.empty()) {
enable_tx_irq();
Make UARTSerial send all data when blocking Previously, write() was somewhat soft - it only ever made one attempt to wait for buffer space, so it would take as much data as would fit in the buffer in one call. This is not the intent of a POSIX filehandle write. It should try to send everything if blocking, and only send less if interrupted by a signal: - If the O_NONBLOCK flag is clear, write() shall block the calling thread until the data can be accepted. - If the O_NONBLOCK flag is set, write() shall not block the thread. If some data can be written without blocking the thread, write() shall write what it can and return the number of bytes written. Otherwise, it shall return -1 and set errno to [EAGAIN]. This "send all" behaviour is of slightly limited usefulness in POSIX, as you still usually have to worry about the interruption possibility: - If write() is interrupted by a signal before it writes any data, it shall return -1 with errno set to [EINTR]. - If write() is interrupted by a signal after it successfully writes some data, it shall return the number of bytes written. But as mbed OS does not have the possibility of signal interruption, if we strengthen write to write everything, we can make applications' lives easier - they can just do "write(large amount)" confident that it will all go in one call (if no errors). So, rework to make multiple writes to the buffer, blocking as necessary, until all data is written. This change does not apply to read(), which is correct in only blocking until some data is available: - If O_NONBLOCK is set, read() shall return -1 and set errno to [EAGAIN]. - If O_NONBLOCK is clear, read() shall block the calling thread until some data becomes available. - The use of the O_NONBLOCK flag has no effect if there is some data available.
2017-11-09 12:30:55 +00:00
}
}
Make UARTSerial send all data when blocking Previously, write() was somewhat soft - it only ever made one attempt to wait for buffer space, so it would take as much data as would fit in the buffer in one call. This is not the intent of a POSIX filehandle write. It should try to send everything if blocking, and only send less if interrupted by a signal: - If the O_NONBLOCK flag is clear, write() shall block the calling thread until the data can be accepted. - If the O_NONBLOCK flag is set, write() shall not block the thread. If some data can be written without blocking the thread, write() shall write what it can and return the number of bytes written. Otherwise, it shall return -1 and set errno to [EAGAIN]. This "send all" behaviour is of slightly limited usefulness in POSIX, as you still usually have to worry about the interruption possibility: - If write() is interrupted by a signal before it writes any data, it shall return -1 with errno set to [EINTR]. - If write() is interrupted by a signal after it successfully writes some data, it shall return the number of bytes written. But as mbed OS does not have the possibility of signal interruption, if we strengthen write to write everything, we can make applications' lives easier - they can just do "write(large amount)" confident that it will all go in one call (if no errors). So, rework to make multiple writes to the buffer, blocking as necessary, until all data is written. This change does not apply to read(), which is correct in only blocking until some data is available: - If O_NONBLOCK is set, read() shall return -1 and set errno to [EAGAIN]. - If O_NONBLOCK is clear, read() shall block the calling thread until some data becomes available. - The use of the O_NONBLOCK flag has no effect if there is some data available.
2017-11-09 12:30:55 +00:00
core_util_critical_section_exit();
}
api_unlock();
2018-05-24 15:58:14 +00:00
return data_written != 0 ? (ssize_t) data_written : (ssize_t) - EAGAIN;
}
2018-05-24 15:58:14 +00:00
ssize_t UARTSerial::read(void *buffer, size_t length)
{
size_t data_read = 0;
char *ptr = static_cast<char *>(buffer);
Make UARTSerial send all data when blocking Previously, write() was somewhat soft - it only ever made one attempt to wait for buffer space, so it would take as much data as would fit in the buffer in one call. This is not the intent of a POSIX filehandle write. It should try to send everything if blocking, and only send less if interrupted by a signal: - If the O_NONBLOCK flag is clear, write() shall block the calling thread until the data can be accepted. - If the O_NONBLOCK flag is set, write() shall not block the thread. If some data can be written without blocking the thread, write() shall write what it can and return the number of bytes written. Otherwise, it shall return -1 and set errno to [EAGAIN]. This "send all" behaviour is of slightly limited usefulness in POSIX, as you still usually have to worry about the interruption possibility: - If write() is interrupted by a signal before it writes any data, it shall return -1 with errno set to [EINTR]. - If write() is interrupted by a signal after it successfully writes some data, it shall return the number of bytes written. But as mbed OS does not have the possibility of signal interruption, if we strengthen write to write everything, we can make applications' lives easier - they can just do "write(large amount)" confident that it will all go in one call (if no errors). So, rework to make multiple writes to the buffer, blocking as necessary, until all data is written. This change does not apply to read(), which is correct in only blocking until some data is available: - If O_NONBLOCK is set, read() shall return -1 and set errno to [EAGAIN]. - If O_NONBLOCK is clear, read() shall block the calling thread until some data becomes available. - The use of the O_NONBLOCK flag has no effect if there is some data available.
2017-11-09 12:30:55 +00:00
if (length == 0) {
return 0;
}
api_lock();
while (_rxbuf.empty()) {
if (!_blocking) {
api_unlock();
return -EAGAIN;
}
api_unlock();
wait_ms(1); // XXX todo - proper wait, WFE for non-rtos ?
api_lock();
}
while (data_read < length && !_rxbuf.empty()) {
_rxbuf.pop(*ptr++);
data_read++;
}
core_util_critical_section_enter();
if (_rx_enabled && !_rx_irq_enabled) {
UARTSerial::rx_irq(); // only read from hardware in one place
if (!_rxbuf.full()) {
enable_rx_irq();
}
}
core_util_critical_section_exit();
api_unlock();
return data_read;
}
Major Refactoring & extensions For keep supporting external APIs with the same name (supposedly there are a larger number of users of those APIs), BufferedSerial and ATParser are being renamed. BufferedSerial becomes UARTSerial, will complement a future USBSerial etc. ATParser becomes ATCmdParser. * UARTSerial moves to /drivers * APN_db.h is moved from platform to cellular/util/. * Original CellularInterface is restored for backward compatability (again, supposedly there are users of that). * A new file, CellularBase is added which will now servce as the base class for all upcoming drivers. * Special restructuring for the driver has been undertaken. This makes a clear cut distinction between an on-board or an off-board implementation. - PPPCellularInterface is a generic network interface that works with a generic FileHandle and PPP. A derived class is needed to pass that FileHandle. - PPPCellularInterface provides some base functionality like network registration, AT setup, PPP connection etc. Lower level job is delegated to the derived classes and various modem specific APIs are provided which are supposed to be overridden. - UARTCellularInterface is derived from PPPCellularInterface. It constructs a FileHandle and passes it back to PPPCellularInterface as well as provides modem hangupf functionality. In future we could proive a USBInterface that would derive from PPPCellularInterface and could pass the FileHandle back. - OnboardCellularInterface is derived from UARTCellularInterfae and provides hooks to the target provided implementation of onbard_modem_api.h. An off-board modem, i.e, a modem on a shield has to override the modem_init(), modem_power_up() etc as it cannot use onboard_modem_api.h.
2017-05-23 17:07:24 +00:00
bool UARTSerial::hup() const
{
Major Refactoring & extensions For keep supporting external APIs with the same name (supposedly there are a larger number of users of those APIs), BufferedSerial and ATParser are being renamed. BufferedSerial becomes UARTSerial, will complement a future USBSerial etc. ATParser becomes ATCmdParser. * UARTSerial moves to /drivers * APN_db.h is moved from platform to cellular/util/. * Original CellularInterface is restored for backward compatability (again, supposedly there are users of that). * A new file, CellularBase is added which will now servce as the base class for all upcoming drivers. * Special restructuring for the driver has been undertaken. This makes a clear cut distinction between an on-board or an off-board implementation. - PPPCellularInterface is a generic network interface that works with a generic FileHandle and PPP. A derived class is needed to pass that FileHandle. - PPPCellularInterface provides some base functionality like network registration, AT setup, PPP connection etc. Lower level job is delegated to the derived classes and various modem specific APIs are provided which are supposed to be overridden. - UARTCellularInterface is derived from PPPCellularInterface. It constructs a FileHandle and passes it back to PPPCellularInterface as well as provides modem hangupf functionality. In future we could proive a USBInterface that would derive from PPPCellularInterface and could pass the FileHandle back. - OnboardCellularInterface is derived from UARTCellularInterfae and provides hooks to the target provided implementation of onbard_modem_api.h. An off-board modem, i.e, a modem on a shield has to override the modem_init(), modem_power_up() etc as it cannot use onboard_modem_api.h.
2017-05-23 17:07:24 +00:00
return _dcd_irq && _dcd_irq->read() != 0;
}
Major Refactoring & extensions For keep supporting external APIs with the same name (supposedly there are a larger number of users of those APIs), BufferedSerial and ATParser are being renamed. BufferedSerial becomes UARTSerial, will complement a future USBSerial etc. ATParser becomes ATCmdParser. * UARTSerial moves to /drivers * APN_db.h is moved from platform to cellular/util/. * Original CellularInterface is restored for backward compatability (again, supposedly there are users of that). * A new file, CellularBase is added which will now servce as the base class for all upcoming drivers. * Special restructuring for the driver has been undertaken. This makes a clear cut distinction between an on-board or an off-board implementation. - PPPCellularInterface is a generic network interface that works with a generic FileHandle and PPP. A derived class is needed to pass that FileHandle. - PPPCellularInterface provides some base functionality like network registration, AT setup, PPP connection etc. Lower level job is delegated to the derived classes and various modem specific APIs are provided which are supposed to be overridden. - UARTCellularInterface is derived from PPPCellularInterface. It constructs a FileHandle and passes it back to PPPCellularInterface as well as provides modem hangupf functionality. In future we could proive a USBInterface that would derive from PPPCellularInterface and could pass the FileHandle back. - OnboardCellularInterface is derived from UARTCellularInterfae and provides hooks to the target provided implementation of onbard_modem_api.h. An off-board modem, i.e, a modem on a shield has to override the modem_init(), modem_power_up() etc as it cannot use onboard_modem_api.h.
2017-05-23 17:07:24 +00:00
void UARTSerial::wake()
{
if (_sigio_cb) {
_sigio_cb();
}
}
2018-05-24 15:58:14 +00:00
short UARTSerial::poll(short events) const
{
short revents = 0;
/* Check the Circular Buffer if space available for writing out */
if (!_rxbuf.empty()) {
revents |= POLLIN;
}
/* POLLHUP and POLLOUT are mutually exclusive */
if (hup()) {
revents |= POLLHUP;
} else if (!_txbuf.full()) {
revents |= POLLOUT;
}
/*TODO Handle other event types */
return revents;
}
void UARTSerial::lock()
{
// This is the override for SerialBase.
// No lock required as we only use SerialBase from interrupt or from
// inside our own critical section.
}
void UARTSerial::unlock()
{
// This is the override for SerialBase.
}
void UARTSerial::api_lock(void)
{
_mutex.lock();
}
void UARTSerial::api_unlock(void)
{
_mutex.unlock();
}
Major Refactoring & extensions For keep supporting external APIs with the same name (supposedly there are a larger number of users of those APIs), BufferedSerial and ATParser are being renamed. BufferedSerial becomes UARTSerial, will complement a future USBSerial etc. ATParser becomes ATCmdParser. * UARTSerial moves to /drivers * APN_db.h is moved from platform to cellular/util/. * Original CellularInterface is restored for backward compatability (again, supposedly there are users of that). * A new file, CellularBase is added which will now servce as the base class for all upcoming drivers. * Special restructuring for the driver has been undertaken. This makes a clear cut distinction between an on-board or an off-board implementation. - PPPCellularInterface is a generic network interface that works with a generic FileHandle and PPP. A derived class is needed to pass that FileHandle. - PPPCellularInterface provides some base functionality like network registration, AT setup, PPP connection etc. Lower level job is delegated to the derived classes and various modem specific APIs are provided which are supposed to be overridden. - UARTCellularInterface is derived from PPPCellularInterface. It constructs a FileHandle and passes it back to PPPCellularInterface as well as provides modem hangupf functionality. In future we could proive a USBInterface that would derive from PPPCellularInterface and could pass the FileHandle back. - OnboardCellularInterface is derived from UARTCellularInterfae and provides hooks to the target provided implementation of onbard_modem_api.h. An off-board modem, i.e, a modem on a shield has to override the modem_init(), modem_power_up() etc as it cannot use onboard_modem_api.h.
2017-05-23 17:07:24 +00:00
void UARTSerial::rx_irq(void)
{
bool was_empty = _rxbuf.empty();
/* Fill in the receive buffer if the peripheral is readable
* and receive buffer is not full. */
while (!_rxbuf.full() && SerialBase::readable()) {
char data = SerialBase::_base_getc();
_rxbuf.push(data);
}
if (_rx_irq_enabled && _rxbuf.full()) {
disable_rx_irq();
}
/* Report the File handler that data is ready to be read from the buffer. */
if (was_empty && !_rxbuf.empty()) {
wake();
}
}
// Also called from write to start transfer
Major Refactoring & extensions For keep supporting external APIs with the same name (supposedly there are a larger number of users of those APIs), BufferedSerial and ATParser are being renamed. BufferedSerial becomes UARTSerial, will complement a future USBSerial etc. ATParser becomes ATCmdParser. * UARTSerial moves to /drivers * APN_db.h is moved from platform to cellular/util/. * Original CellularInterface is restored for backward compatability (again, supposedly there are users of that). * A new file, CellularBase is added which will now servce as the base class for all upcoming drivers. * Special restructuring for the driver has been undertaken. This makes a clear cut distinction between an on-board or an off-board implementation. - PPPCellularInterface is a generic network interface that works with a generic FileHandle and PPP. A derived class is needed to pass that FileHandle. - PPPCellularInterface provides some base functionality like network registration, AT setup, PPP connection etc. Lower level job is delegated to the derived classes and various modem specific APIs are provided which are supposed to be overridden. - UARTCellularInterface is derived from PPPCellularInterface. It constructs a FileHandle and passes it back to PPPCellularInterface as well as provides modem hangupf functionality. In future we could proive a USBInterface that would derive from PPPCellularInterface and could pass the FileHandle back. - OnboardCellularInterface is derived from UARTCellularInterfae and provides hooks to the target provided implementation of onbard_modem_api.h. An off-board modem, i.e, a modem on a shield has to override the modem_init(), modem_power_up() etc as it cannot use onboard_modem_api.h.
2017-05-23 17:07:24 +00:00
void UARTSerial::tx_irq(void)
{
bool was_full = _txbuf.full();
char data;
/* Write to the peripheral if there is something to write
* and if the peripheral is available to write. */
while (SerialBase::writeable() && _txbuf.pop(data)) {
SerialBase::_base_putc(data);
}
if (_tx_irq_enabled && _txbuf.empty()) {
disable_tx_irq();
}
/* Report the File handler that data can be written to peripheral. */
if (was_full && !_txbuf.full() && !hup()) {
wake();
}
}
/* These are all called from critical section */
void UARTSerial::enable_rx_irq()
{
SerialBase::attach(callback(this, &UARTSerial::rx_irq), RxIrq);
_rx_irq_enabled = true;
}
void UARTSerial::disable_rx_irq()
{
SerialBase::attach(NULL, RxIrq);
_rx_irq_enabled = false;
}
void UARTSerial::enable_tx_irq()
{
SerialBase::attach(callback(this, &UARTSerial::tx_irq), TxIrq);
_tx_irq_enabled = true;
}
void UARTSerial::disable_tx_irq()
{
SerialBase::attach(NULL, TxIrq);
_tx_irq_enabled = false;
}
int UARTSerial::enable_input(bool enabled)
{
core_util_critical_section_enter();
if (_rx_enabled != enabled) {
if (enabled) {
UARTSerial::rx_irq();
if (!_rxbuf.full()) {
enable_rx_irq();
}
} else {
disable_rx_irq();
}
_rx_enabled = enabled;
}
core_util_critical_section_exit();
return 0;
}
int UARTSerial::enable_output(bool enabled)
{
core_util_critical_section_enter();
if (_tx_enabled != enabled) {
if (enabled) {
UARTSerial::tx_irq();
if (!_txbuf.empty()) {
enable_tx_irq();
}
} else {
disable_tx_irq();
}
_tx_enabled = enabled;
}
core_util_critical_section_exit();
return 0;
}
void UARTSerial::wait_ms(uint32_t millisec)
{
/* wait_ms implementation for RTOS spins until exact microseconds - we
* want to just sleep until next tick.
*/
#if MBED_CONF_RTOS_PRESENT
rtos::ThisThread::sleep_for(millisec);
#else
::wait_ms(millisec);
#endif
}
} //namespace mbed
#endif //(DEVICE_SERIAL && DEVICE_INTERRUPTIN)