/* stream.h - high level (serial) stream handling Part of grblHAL Copyright (c) 2019-2025 Terje Io grblHAL is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. grblHAL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with grblHAL. If not, see . */ /*! \file Stream I/O functions structure for the HAL. Helper functions for saving away and restoring a stream input buffer. _Not referenced by the core._ */ #ifndef _STREAM_H_ #define _STREAM_H_ #define ASCII_SOH 0x01 #define ASCII_STX 0x02 #define ASCII_ETX 0x03 #define ASCII_EOT 0x04 #define ASCII_ENQ 0x05 #define ASCII_ACK 0x06 #define ASCII_BS 0x08 #define ASCII_TAB 0x09 #define ASCII_LF 0x0A #define ASCII_CR 0x0D #define ASCII_XON 0x11 #define ASCII_XOFF 0x13 #define ASCII_NAK 0x15 #define ASCII_EOF 0x1A #define ASCII_CAN 0x18 #define ASCII_EM 0x19 #define ASCII_ESC 0x1B #define ASCII_DEL 0x7F #define ASCII_EOL "\r\n" #ifndef RX_BUFFER_SIZE #define RX_BUFFER_SIZE 1024 // must be a power of 2 #endif #ifndef TX_BUFFER_SIZE #define TX_BUFFER_SIZE 512 // must be a power of 2 #endif #ifndef BLOCK_TX_BUFFER_SIZE #define BLOCK_TX_BUFFER_SIZE 256 #endif // Serial baud rate #ifndef BAUD_RATE #define BAUD_RATE 115200 #endif // Value to be returned from input stream when no data is available #ifndef SERIAL_NO_DATA #define SERIAL_NO_DATA -1 #endif #define BUFNEXT(ptr, buffer) ((ptr + 1) & (sizeof(buffer.data) - 1)) #define BUFCOUNT(head, tail, size) ((head >= tail) ? (head - tail) : (size - tail + head)) #include #include #include #include "vfs.h" typedef enum { StreamType_Serial = 0, StreamType_MPG, StreamType_Bluetooth, StreamType_Telnet, StreamType_WebSocket, StreamType_SDCard, // deprecated, use StreamType_File instead StreamType_File = StreamType_SDCard, StreamType_Redirected, StreamType_Null } stream_type_t; typedef enum { StreamSuspend_Off = 0, StreamSuspend_Pending, StreamSuspend_Active } stream_suspend_state_t; typedef enum { Serial_8bit = 0, Serial_7bit } serial_width_t; typedef enum { Serial_StopBits1 = 0, Serial_StopBits1_5, Serial_StopBits2, Serial_StopBits0_5, } serial_stopbits_t; typedef enum { Serial_ParityNone = 0, Serial_ParityEven, Serial_ParityOdd, Serial_ParityMark, Serial_ParitySpace, } serial_parity_t; typedef union { uint8_t value; struct { uint8_t width :2, stopbits :2, parity :3, unused :1; }; } serial_format_t; typedef union { uint8_t value; struct { uint8_t dtr :1, dsr :1, rts :1, cts :1, unused :4; }; } serial_linestate_t; /*! \brief Pointer to function for getting stream connected status. \returns \a true connected, \a false otherwise. */ typedef bool (*stream_is_connected_ptr)(void); /*! \brief Pointer to function for getting number of characters available or free in a stream buffer. \returns number of characters available or free. */ typedef uint16_t (*get_stream_buffer_count_ptr)(void); /*! \brief Pointer to function for reading a single character from a input stream. \returns character or -1 if none available. */ typedef int32_t (*stream_read_ptr)(void); /*! \brief Pointer to function for writing a null terminated string to the output stream. \param s pointer to null terminated string. __NOTE:__ output might be buffered until an #ASCII_LF is output, this is usually done by USB or network driver code to improve throughput. */ typedef void (*stream_write_ptr)(const char *s); /*! \brief Pointer to function for writing a \a n character long string to the output stream. \param s pointer to string. \param len number of characters to write. */ typedef void (*stream_write_n_ptr)(const uint8_t *s, uint16_t len); /*! \brief Pointer to function for writing a single character to the output stream. \param c the character to write. */ typedef bool (*stream_write_char_ptr)(const uint8_t c); /*! \brief Pointer to function for extracting real-time commands from the input stream and enqueue them for processing. This should be called by driver code prior to inserting a character into the input buffer. \param c character to check. \returns true if extracted, driver code should not insert the character into the input buffer if so. */ typedef bool (*enqueue_realtime_command_ptr)(uint8_t c); /*! \brief Pointer to function for setting the transfer direction control signal for half-duplex connections (RS-485). \param tx \a true when transmitting, \a false when receiving. */ typedef void (*stream_set_direction_ptr)(bool tx); /*! \brief Optional, but recommended, pointer to function for enqueueing realtime command characters. \param c character to enqueue. \returns \a true if successfully enqueued, \a false otherwise. __NOTE:__ Stream implementations should pass the character over the current handler registered by the set_enqueue_rt_handler(). User or plugin code should __not__ enqueue realtime command characters via this handler, it should call \a grbl.enqueue_realtime_command() instead. */ typedef bool (*enqueue_realtime_command2_ptr)(uint8_t c); /*! \brief Pointer to function for setting the enqueue realtime commands handler. \param handler a \a enqueue_realtime_command_ptr pointer to the new handler function. \returns \a enqueue_realtime_command_ptr pointer to the replaced function. __NOTE:__ Stream implementations should hold a pointer to the handler in a local variable and typically set it to protocol_enqueue_realtime_command() on initialization. */ typedef enqueue_realtime_command_ptr (*set_enqueue_rt_handler_ptr)(enqueue_realtime_command_ptr handler); /*! \brief Pointer to function for setting the stream format. \param format a \a serial_format_t struct. \returns true if successful. */ typedef bool (*set_format_ptr)(serial_format_t format); /*! \brief Pointer to function for setting the stream baud rate. \param baud_rate \returns true if successful. */ typedef bool (*set_baud_rate_ptr)(uint32_t baud_rate); /*! \brief Pointer to function for flushing a stream buffer. */ typedef void (*flush_stream_buffer_ptr)(void); /*! \brief Pointer to function for flushing the input buffer and inserting an #ASCII_CAN character. This function is typically called by the _enqueue_realtime_command_ handler when CMD_STOP or CMD_JOG_CANCEL character is processed. The #ASCII_CAN character might be checked for and used by upstream code to flush any buffers it may have. */ typedef void (*cancel_read_buffer_ptr)(void); /*! \brief Pointer to function for blocking reads from and restoring a input buffer. This function is called with the _await_ parameter true on executing a tool change command (M6), this shall block further reading from the input buffer. The core function stream_rx_suspend() can be called with the _await_ parameter to do this, it will replace the _hal.stream.read_ handler with a pointer to the dummy function stream_get_null(). Reading from the input is blocked until a tool change acknowledge character #CMD_TOOL_ACK is received, when the driver receives this the input buffer is to be saved away and reading from the input resumed by restoring the _hal.stream.read_ handler with its own read character function. Driver code can do this by calling the core function stream_rx_backup(). When the tool change is complete or a soft reset is executed the core will call this function with the _await_ parameter false, if the driver code called stream_rx_suspend() to block input it shall then call it again with the _await_ parameter as input to restore it. \param await bool \returns \a true if there is data in the buffer, \a false otherwise. */ typedef bool (*suspend_read_ptr)(bool await); /*! \brief Pointer to function for disabling/enabling stream input. Typically used to disable receive interrupts so that real-time command processing for the stream is blocked. Usually it is desirable to block processing when another stream provides the input, but sometimes not. E.g. when input is from a SD card real-time commands from the stream that initiated SD card streaming is needed for handing feed-holds, overrides, soft resets etc. \param disable \a true to disable stream, \a false to enable, */ typedef bool (*disable_rx_stream_ptr)(bool disable); /*! \brief Pointer to function for handling line state changed events. \param \a serial_linestate_t enum. */ typedef void (*on_linestate_changed_ptr)(serial_linestate_t state); typedef union { uint8_t value; struct { uint8_t claimable :1, claimed :1, can_set_baud :1, rx_only :1, modbus_ready :1, rts_handshake :1, init_ok :1, unused :1; }; } io_stream_flags_t; typedef union { uint8_t value; struct { uint8_t webui_connected :1, is_usb :1, linestate_event :1, //!< Set when driver supports on_linestate_changed event. passthru :1, //!< Set when stream is in passthru mode. utf8 :1, //!< Set when stream is in UTF8 mode. eof :1, //!< Set when a file stream reaches end-of-file. unused :2; }; } io_stream_state_t; //! Properties and handlers for stream I/O typedef struct { stream_type_t type; //!< Type of stream. uint8_t instance; //!< Instance of stream type, starts from 0. io_stream_state_t state; //!< Optional status flags such as connected status. stream_is_connected_ptr is_connected; //!< Handler for getting stream connected status. get_stream_buffer_count_ptr get_rx_buffer_free; //!< Handler for getting number of free characters in the input buffer. stream_write_ptr write; //!< Handler for writing string to current output stream only. stream_write_ptr write_all; //!< Handler for writing string to all active output streams. stream_write_char_ptr write_char; //!< Handler for writing a single character to current stream only. enqueue_realtime_command2_ptr enqueue_rt_command; //!< (Optional) handler for enqueueing a realtime command character. stream_read_ptr read; //!< Handler for reading a single character from the input stream. flush_stream_buffer_ptr reset_read_buffer; //!< Handler for flushing the input buffer. cancel_read_buffer_ptr cancel_read_buffer; //!< Handler for flushing the input buffer and inserting an #ASCII_CAN character. set_enqueue_rt_handler_ptr set_enqueue_rt_handler; //!< Handler for setting the enqueue realtime command character handler. suspend_read_ptr suspend_read; //!< Optional handler for saving away and restoring the current input buffer. stream_write_n_ptr write_n; //!< Optional handler for writing n characters to current output stream only. Required for Modbus support. disable_rx_stream_ptr disable_rx; //!< Optional handler for disabling/enabling a stream. Recommended? get_stream_buffer_count_ptr get_rx_buffer_count; //!< Optional handler for getting number of characters in the input buffer. get_stream_buffer_count_ptr get_tx_buffer_count; //!< Optional handler for getting number of characters in the output buffer(s). Count shall include any unsent characters in any transmit FIFO and/or transmit register. Required for Modbus/RS-485 support. flush_stream_buffer_ptr reset_write_buffer; //!< Optional handler for flushing the output buffer. Any transmit FIFO shall be flushed as well. Required for Modbus/RS-485 support. set_baud_rate_ptr set_baud_rate; //!< Optional handler for setting the stream baud rate. Required for Modbus/RS-485 support, recommended for Bluetooth support. set_format_ptr set_format; //!< Optional handler for setting the stream format. stream_set_direction_ptr set_direction; //!< Optional handler for setting the transfer direction for half-duplex communication. on_linestate_changed_ptr on_linestate_changed; //!< Optional handler to be called when line state changes. Set by client. vfs_file_t *file; //!< File handle, non-null if streaming from a file. } io_stream_t; typedef struct { io_stream_state_t state; //!< Optional status flags such as connected status. io_stream_flags_t flags; //!< Stream capability flags. uint32_t baud_rate; serial_format_t format; } io_stream_status_t; typedef const io_stream_t *(*stream_claim_ptr)(uint32_t baud_rate); typedef bool (*stream_release_ptr)(uint8_t instance); typedef const io_stream_status_t *(*stream_get_status_ptr)(uint8_t instance); typedef struct { stream_type_t type; //!< Type of stream. uint8_t instance; //!< Instance of stream type, starts from 0. io_stream_flags_t flags; stream_claim_ptr claim; stream_release_ptr release; stream_get_status_ptr get_status; //!< Optional handler for getting stream status, for UART streams only } io_stream_properties_t; typedef bool (*stream_enumerate_callback_ptr)(io_stream_properties_t const *properties, void *data); typedef struct io_stream_details { uint8_t n_streams; io_stream_properties_t *streams; struct io_stream_details *next; } io_stream_details_t; // The following structures and functions are not referenced in the core code, may be used by drivers typedef struct { volatile uint_fast16_t head; volatile uint_fast16_t tail; volatile bool rts_state; bool overflow; bool backup; uint8_t data[RX_BUFFER_SIZE]; } stream_rx_buffer_t; typedef struct { volatile uint_fast16_t head; volatile uint_fast16_t tail; uint8_t data[TX_BUFFER_SIZE]; } stream_tx_buffer_t; typedef struct { uint_fast16_t length; uint_fast16_t max_length; uint8_t *s; uint8_t data[BLOCK_TX_BUFFER_SIZE]; } stream_block_tx_buffer_t; // double buffered tx stream typedef struct { uint_fast16_t length; uint_fast16_t max_length; uint8_t *s; bool use_tx2data; uint8_t data[BLOCK_TX_BUFFER_SIZE]; uint8_t data2[BLOCK_TX_BUFFER_SIZE]; } stream_block_tx_buffer2_t; #ifdef __cplusplus extern "C" { #endif static inline bool stream_is_uart (stream_type_t type) { return type == StreamType_Serial || type == StreamType_Bluetooth; } /*! \brief Dummy function for reading data from a virtual empty input buffer. \returns always -1 as there is no data available. */ int32_t stream_get_null (void); /*! \brief Function for blocking reads from or restoring an input buffer. \param rxbuffer pointer to a stream_rx_buffer_t. \param suspend when true _hal.stream.read_ is changed to stream_get_null(), if false it is restored if already saved. \returns true if there is data in the buffer, false otherwise. */ bool stream_rx_suspend (stream_rx_buffer_t *rxbuffer, bool suspend); stream_suspend_state_t stream_is_rx_suspended (void); bool stream_mpg_register (const io_stream_t *stream, bool rx_only, stream_write_char_ptr write_char); /*! \brief Function for enabling/disabling input from a secondary input stream. \param on \a true if switching input to mpg stream, \a false when restoring original input. \returns \a true when succsessful, \a false otherwise. */ bool stream_mpg_enable (bool on); void stream_mpg_set_mode (void *data); bool stream_mpg_check_enable (uint8_t c); bool stream_buffer_all (uint8_t c); bool stream_tx_blocking (void); bool stream_enqueue_realtime_command (uint8_t c); void stream_register_streams (io_stream_details_t *details); bool stream_enumerate_streams (stream_enumerate_callback_ptr callback, void *data); bool stream_connect (const io_stream_t *stream); bool stream_connect_instance (uint8_t instance, uint32_t baud_rate); void stream_disconnect (const io_stream_t *stream); bool stream_connected (void); void stream_set_defaults (const io_stream_t *stream, uint32_t baud_rate); const io_stream_t *stream_get_base (void); io_stream_flags_t stream_get_flags (io_stream_t stream); const io_stream_status_t *stream_get_uart_status (uint8_t instance); const io_stream_t *stream_null_init (uint32_t baud_rate); io_stream_t const *stream_open_instance (uint8_t instance, uint32_t baud_rate, stream_write_char_ptr rx_handler, const char *description); bool stream_close (io_stream_t const *stream); bool stream_set_description (const io_stream_t *stream, const char *description); void debug_printf(const char *fmt, ...); #if defined(DEBUG) || defined(DEBUGOUT) #define DEBUG_PRINT 1 #ifdef DEBUGOUT void debug_write (const char *s); void debug_writeln (const char *s); bool debug_stream_init (void); #endif #else #define DEBUG_PRINT 0 #endif #define debug_print(fmt, ...) \ do { if(DEBUG_PRINT) debug_printf(fmt, __VA_ARGS__); } while(0) #ifdef __cplusplus } #endif #endif