From 117c28aad93b5cb0cc2bbcf3efa7777b9bea94e4 Mon Sep 17 00:00:00 2001 From: Mieszko Mierunski Date: Wed, 24 Oct 2018 14:09:31 +0200 Subject: [PATCH] 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 --- drivers/serial/Kconfig | 7 + include/uart.h | 390 +++++++++++++++++++++++++++++++++++++---- 2 files changed, 363 insertions(+), 34 deletions(-) diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 6685a6a464c..2bd8de26fa0 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -28,12 +28,19 @@ config SERIAL_SUPPORT_INTERRUPT 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 806ce019cc2..b5aa802a127 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 @@ extern "C" { #include +/** @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 #include @@ -38,33 +236,6 @@ extern "C" { */ #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 @@ enum uart_config_flow_control { 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 @@ struct uart_device_config { /** @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 @@ struct uart_driver_api { }; +#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 @@ static inline int _impl_uart_err_check(struct device *dev) * @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);