api: uart: Add new asynchronous UART API.
Added new UART API, that allows for longer transmissions, leaves
IRQ handling on driver side and allows for DMA usage.
Signed-off-by: Mieszko Mierunski <mieszko.mierunski@nordicsemi.no>
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index 6685a6a..2bd8de2 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -28,12 +28,19 @@
This is an option to be enabled by individual serial driver
to signal that the driver and hardware supports interrupts.
+config UART_ASYNC_API
+ bool "Enable new asynchronous UART API [EXPERIMENTAL]"
+ help
+ This option enables new asynchronous UART API.
+
+if UART_ASYNC_API=n
config UART_INTERRUPT_DRIVEN
bool "Enable UART Interrupt support"
depends on SERIAL_SUPPORT_INTERRUPT
help
This option enables interrupt support for UART allowing console
input and other UART based drivers.
+endif
config UART_LINE_CTRL
bool "Enable Serial Line Control API"
diff --git a/include/uart.h b/include/uart.h
index 806ce01..b5aa802 100644
--- a/include/uart.h
+++ b/include/uart.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018 Nordic Semiconductor ASA
+ * Copyright (c) 2018-2019 Nordic Semiconductor ASA
* Copyright (c) 2015 Wind River Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
@@ -29,6 +29,204 @@
#include <device.h>
+/** @brief Line control signals. */
+enum uart_line_ctrl {
+ UART_LINE_CTRL_RTS = (1 << 1),
+ UART_LINE_CTRL_DTR = (1 << 2),
+ UART_LINE_CTRL_DCD = (1 << 3),
+ UART_LINE_CTRL_DSR = (1 << 4),
+};
+
+/**
+ * @brief Types of events passed to callback in UART_ASYNC_API
+ *
+ * Receiving:
+ * 1. To start receiving, uart_rx_enable has to be called with first buffer
+ * 2. When receiving starts to current buffer, UART_RX_BUF_REQUEST will be
+ * generated, in response to that user can either:
+ *
+ * - Provide second buffer using uart_rx_buf_rsp, when first buffer is
+ * filled, receiving will automatically start to second buffer.
+ * - Ignore the event, this way when current buffer is filled UART_RX_DONE
+ * event will be generated and receiving will be stopped.
+ *
+ * 3. If some data was received and timeout occurred UART_RX_RDY event will be
+ * generated. It can happen multiples times for the same buffer. RX timeout
+ * is counted from last byte received i.e. if no data was received, there
+ * won't be any timeout event.
+ * 4. After buffer is filled UART_RX_RDY will be generated, immediately
+ * followed by UART_RX_BUF_RELEASED indicating that current buffer is no
+ * longer used.
+ * 5. If there was second buffer provided, it will become current buffer and
+ * we start again at point 2.
+ * If no second buffer was specified receiving is stopped and
+ * UART_RX_DISABLED event is generated. After that whole process can be
+ * repeated.
+ *
+ * Any time during reception UART_RX_STOPPED event can occur. It will be
+ * followed by UART_RX_BUF_RELEASED event for every buffer currently passed to
+ * driver and finally by UART_RX_DISABLED event.
+ *
+ * Receiving can be disabled using uart_rx_disable, after calling that
+ * function any data received will be lost, UART_RX_BUF_RELEASED event will be
+ * generated for every buffer currently passed to driver and UART_RX_DISABLED
+ * event will occur.
+ *
+ * Transmitting:
+ * 1. Transmitting starts by uart_tx function.
+ * 2. If whole buffer was transmitted UART_TX_DONE is generated.
+ * If timeout occurred UART_TX_ABORTED will be generated.
+ *
+ * Transmitting can be aborted using uart_tx_abort, after calling that
+ * function UART_TX_ABORTED event will be generated.
+ *
+ */
+enum uart_event_type {
+ /** @brief Whole TX buffer was transmitted. */
+ UART_TX_DONE,
+ /**
+ * @brief Transmitting aborted due to timeout or uart_tx_abort call
+ *
+ * When flow control is enabled, there is a possibility that TX transfer
+ * won't finish in the allotted time. Some data may have been
+ * transferred, information about it can be found in event data.
+ */
+ UART_TX_ABORTED,
+ /**
+ * @brief Received data is ready for processing.
+ *
+ * This event is generated in two cases:
+ * - When RX timeout occurred, and data was stored in provided buffer.
+ * This can happen multiple times in the same buffer.
+ * - When provided buffer is full.
+ */
+ UART_RX_RDY,
+ /**
+ * @brief Driver requests next buffer for continuous reception.
+ *
+ * This event is triggered when receiving has started for a new buffer,
+ * i.e. it's time to provide a next buffer for a seamless switchover to
+ * it. For continuous reliable receiving, user should provide another RX
+ * buffer in response to this event, using uart_rx_buf_rsp function
+ *
+ * If uart_rx_buf_rsp is not called before current buffer
+ * is filled up, receiving will stop.
+ */
+ UART_RX_BUF_REQUEST,
+ /**
+ * @brief Buffer is no longer used by UART driver.
+ */
+ UART_RX_BUF_RELEASED,
+ /**
+ * @brief RX has been disabled and can be reenabled.
+ *
+ * This event is generated whenever receiver has been stopped, disabled
+ * or finished its operation and can be enabled again using
+ * uart_rx_enable
+ */
+ UART_RX_DISABLED,
+ /**
+ * @brief RX has stopped due to external event.
+ *
+ * Reason is one of uart_rx_stop_reason.
+ */
+ UART_RX_STOPPED,
+};
+
+
+/**
+ * @brief Reception stop reasons.
+ *
+ * Values that correspond to events or errors responsible for stopping
+ * receiving.
+ *
+ * We start counting from 1, to not use 0 which is treated as no error.
+ */
+enum uart_rx_stop_reason {
+ /**
+ * @brief Break interrupt
+ *
+ * A break interrupt was received. This happens when the serial input
+ * is held at a logic '0' state for longer than the sum of
+ * start time + data bits + parity + stop bits.
+ */
+ UART_BREAK = 1,
+ /** @brief Overrun error */
+ UART_ERROR_OVERRUN,
+ /** @brief Parity error */
+ UART_ERROR_PARITY,
+ /** @brief Framing error */
+ UART_ERROR_FRAMING,
+};
+
+/** @brief Backward compatibility defines, deprecated */
+#define UART_ERROR_BREAK UART_BREAK
+#define LINE_CTRL_BAUD_RATE (1 << 0)
+#define LINE_CTRL_RTS UART_LINE_CTRL_RTS
+#define LINE_CTRL_DTR UART_LINE_CTRL_DTR
+#define LINE_CTRL_DCD UART_LINE_CTRL_DCD
+#define LINE_CTRL_DSR UART_LINE_CTRL_DSR
+
+
+/** @brief UART TX event data. */
+struct uart_event_tx {
+ /** @brief Pointer to current buffer. */
+ const u8_t *buf;
+ /** @brief Number of bytes sent. */
+ size_t len;
+};
+
+/** @brief UART RX event data. */
+struct uart_event_rx {
+ /** @brief Pointer to current buffer. */
+ u8_t *buf;
+ /** @brief Offset from buffer start to currently received data. */
+ size_t offset;
+ /** @brief Number of bytes received. */
+ size_t len;
+};
+
+/** @brief UART RX buffer released event data. */
+struct uart_event_rx_buf {
+ /* @brief Pointer to buffer that is no longer in use. */
+ u8_t *buf;
+};
+
+/** @brief UART RX stopped data. */
+struct uart_event_rx_stop {
+ /** @brief Reason why receiving stopped */
+ enum uart_rx_stop_reason reason;
+ /** @brief Last received data. */
+ struct uart_event_rx data;
+};
+
+/** @brief Structure containing information about current event. */
+struct uart_event {
+ /** @brief Type of event */
+ enum uart_event_type type;
+ /** @brief Event data */
+ union {
+ /** @brief UART_TX_DONE and UART_TX_ABORTED events data. */
+ struct uart_event_tx tx;
+ /** @brief UART_RX_RDY event data. */
+ struct uart_event_rx rx;
+ /** @brief UART_RX_BUF_RELEASED event data. */
+ struct uart_event_rx_buf rx_buf;
+ /** @brief UART_RX_STOPPED event data. */
+ struct uart_event_rx_stop rx_stop;
+ } data;
+};
+
+/**
+ * @typedef uart_callback_t
+ * @brief Define the application callback function signature for
+ * uart_set_callback() function.
+ *
+ * @param evt Pointer to uart_event structure.
+ * @param user_data Pointer to data specified by user.
+ */
+typedef void (*uart_callback_t)(struct uart_event *evt, void *user_data);
+
#ifdef CONFIG_PCI
#include <drivers/pci/pci.h>
#include <drivers/pci/pci_mgr.h>
@@ -38,33 +236,6 @@
*/
#define UART_OPTION_AFCE 0x01
-/** Common line controls for UART.*/
-#define LINE_CTRL_BAUD_RATE (1 << 0)
-#define LINE_CTRL_RTS (1 << 1)
-#define LINE_CTRL_DTR (1 << 2)
-#define LINE_CTRL_DCD (1 << 3)
-#define LINE_CTRL_DSR (1 << 4)
-
-/* Common communication errors for UART.*/
-
-/** @brief Overrun error */
-#define UART_ERROR_OVERRUN (1 << 0)
-
-/** @brief Parity error */
-#define UART_ERROR_PARITY (1 << 1)
-
-/** @brief Framing error */
-#define UART_ERROR_FRAMING (1 << 2)
-
-/**
- * @brief Break interrupt error:
- *
- * A break interrupt was received. This happens when the serial input is
- * held at a logic '0' state for longer than the sum of start time + data bits
- * + parity + stop bits.
- */
-#define UART_ERROR_BREAK (1 << 3)
-
/**
* @brief UART controller configuration structure
*
@@ -120,7 +291,6 @@
UART_CFG_FLOW_CTRL_DTR_DSR,
};
-
/**
* @typedef uart_irq_callback_user_data_t
* @brief Define the application callback function signature for
@@ -175,6 +345,23 @@
/** @brief Driver API structure. */
struct uart_driver_api {
+
+#ifdef CONFIG_UART_ASYNC_API
+
+ int (*callback_set)(struct device *dev, uart_callback_t callback,
+ void *user_data);
+
+ int (*tx)(struct device *dev, const u8_t *buf, size_t len,
+ u32_t timeout);
+ int (*tx_abort)(struct device *dev);
+
+ int (*rx_enable)(struct device *dev, u8_t *buf, size_t len,
+ u32_t timeout);
+ int (*rx_buf_rsp)(struct device *dev, u8_t *buf, size_t len);
+ int (*rx_disable)(struct device *dev);
+
+#endif
+
/** Console I/O function */
int (*poll_in)(struct device *dev, unsigned char *p_char);
void (*poll_out)(struct device *dev, unsigned char out_char);
@@ -245,16 +432,150 @@
};
+#ifdef CONFIG_UART_ASYNC_API
+
+/**
+ * @brief Set event handler function.
+ *
+ * @param dev UART device structure.
+ * @param callback Event handler.
+ * @param user_data Data to pass to event handler function.
+ *
+ * @retval 0 If successful, negative errno code otherwise.
+ */
+static inline int uart_callback_set(struct device *dev,
+ uart_callback_t callback,
+ void *user_data)
+{
+ const struct uart_driver_api *api =
+ (const struct uart_driver_api *)dev->driver_api;
+
+ return api->callback_set(dev, callback, user_data);
+}
+
+/**
+ * @brief Send given number of bytes from buffer through UART.
+ *
+ * Function returns immediately and event handler,
+ * set using @ref uart_set_callback, is called after transfer is finished.
+ *
+ * @param dev UART device structure.
+ * @param buf Pointer to transmit buffer.
+ * @param len Length of transmit buffer.
+ * @param timeout Timeout in milliseconds. Valid only if flow control is enabled
+ *
+ * @retval -EBUSY There is already an ongoing transfer.
+ * @retval 0 If successful, negative errno code otherwise.
+ */
+static inline int uart_tx(struct device *dev,
+ const u8_t *buf,
+ size_t len,
+ u32_t timeout)
+
+{
+ const struct uart_driver_api *api =
+ (const struct uart_driver_api *)dev->driver_api;
+
+ return api->tx(dev, buf, len, timeout);
+}
+
+/**
+ * @brief Abort current TX transmission.
+ *
+ * UART_TX_DONE event will be generated with amount of data sent.
+ *
+ * @param dev UART device structure.
+ *
+ * @retval -EFAULT There is no active transmission.
+ * @retval 0 If successful, negative errno code otherwise.
+ */
+static inline int uart_tx_abort(struct device *dev)
+{
+ const struct uart_driver_api *api =
+ (const struct uart_driver_api *)dev->driver_api;
+
+ return api->tx_abort(dev);
+}
+
+/**
+ * @brief Start receiving data through UART.
+ *
+ * Function sets given buffer as first buffer for receiving and returns
+ * immediately. After that event handler, set using @ref uart_set_callback,
+ * is called with UART_RX_RDY or UART_RX_BUF_REQUEST events.
+ *
+ * @param dev UART device structure.
+ * @param buf Pointer to receive buffer.
+ * @param len Buffer length.
+ * @param timeout Timeout in milliseconds.
+ *
+ * @retval -EBUSY RX already in progress.
+ * @retval 0 If successful, negative errno code otherwise.
+ *
+ */
+static inline int uart_rx_enable(struct device *dev, u8_t *buf, size_t len,
+ u32_t timeout)
+{
+ const struct uart_driver_api *api =
+ (const struct uart_driver_api *)dev->driver_api;
+
+ return api->rx_enable(dev, buf, len, timeout);
+}
+
+/**
+ * @brief Provide receive buffer in response to UART_RX_BUF_REQUEST event.
+ *
+ * Provide pointer to RX buffer, which will be used when current buffer is
+ * filled.
+ *
+ * @note Providing buffer that is already in usage by driver leads to
+ * undefined behavior. Buffer can be reused when it has been released
+ * by driver.
+ *
+ * @param dev UART device structure.
+ * @param buf Pointer to receive buffer.
+ * @param len Buffer length.
+ *
+ * @retval -EBUSY Next buffer already set.
+ * @retval 0 If successful, negative errno code otherwise.
+ *
+ */
+static inline int uart_rx_buf_rsp(struct device *dev, u8_t *buf, size_t len)
+{
+ const struct uart_driver_api *api =
+ (const struct uart_driver_api *)dev->driver_api;
+
+ return api->rx_buf_rsp(dev, buf, len);
+}
+
+/**
+ * @brief Disable RX
+ *
+ * UART_RX_BUF_RELEASED event will be generated for every buffer scheduled,
+ * after that UART_RX_DISABLED event will be generated.
+ *
+ * @param dev UART device structure.
+ *
+ * @retval -EFAULT There is no active reception.
+ * @retval 0 If successful, negative errno code otherwise.
+ */
+static inline int uart_rx_disable(struct device *dev)
+{
+ const struct uart_driver_api *api =
+ (const struct uart_driver_api *)dev->driver_api;
+
+ return api->rx_disable(dev);
+}
+
+#endif
+
/**
* @brief Check whether an error was detected.
*
* @param dev UART device structure.
*
- * @retval UART_ERROR_OVERRUN if an overrun error was detected.
- * @retval UART_ERROR_PARITY if a parity error was detected.
- * @retval UART_ERROR_FRAMING if a framing error was detected.
- * @retval UART_ERROR_BREAK if a break error was detected.
- * @retval 0 Otherwise.
+ * @retval uart_rx_stop_reason If error during receiving occurred.
+ * @retval 0 Otherwise.
*/
__syscall int uart_err_check(struct device *dev);
@@ -280,6 +601,7 @@
* @retval -1 If no character was available to read (i.e., the UART
* input buffer was empty).
* @retval -ENOTSUP If the operation is not supported.
+ * @retval -EBUSY If reception was enabled using uart_rx_enabled
*/
__syscall int uart_poll_in(struct device *dev, unsigned char *p_char);