diff --git a/README.md b/README.md index 8dc5574..2937c7b 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ This repository does not provide a ready-to-load shared library by itself. It pr ## What It Provides -- Header-only C-compatible serial API definitions under `include/cpp_core` +- Header-only C++ API definitions with unmangled C linkage under `include/cpp_core` - Modern C++26 helper surface for `std::expected`-based error propagation, strong typed config values, and compile-time reflection helpers - A generated version surface used consistently across all platform bindings - An installable CMake package target: `cpp_core::cpp_core` @@ -30,7 +30,7 @@ This repository does not provide a ready-to-load shared library by itself. It pr - `include/cpp_core/serial.h`: aggregated C ABI for serial operations - `include/cpp_core/status_code.h`: shared status-code model -- `include/cpp_core/interface/get_version.h`: version struct and `getVersion` +- `include/cpp_core/interface/meta.h`: metadata struct and exported `meta` function ## Quick Start @@ -50,14 +50,11 @@ Use the exported headers in your implementation: ```cpp #include -#include +#include auto serialOpen( - void *port, - int baudrate, - int data_bits, - int parity, - int stop_bits, + const char *port, + const cpp_core::SerialConfig *config, ErrorCallbackT error_callback ) -> intptr_t; ``` @@ -65,10 +62,10 @@ auto serialOpen( Read the version data baked into the checkout: ```cpp -#include +#include -cpp_core::Version version{}; -getVersion(&version); +cpp_core::Meta metadata{}; +meta(&metadata); ``` ## Building This Repository @@ -94,21 +91,35 @@ The main aggregated interface lives in: #include ``` -The ABI is intentionally plain-C friendly: functions either return a status code, return a value-or-negative-status, or return an opaque handle-or-negative-status. +The API requires a C++ compiler, while exported functions use unmangled C linkage through `MODULE_API`. Functions either return a status code, return a value-or-negative-status, or return an opaque handle-or-negative-status. Example: ```cpp MODULE_API auto serialOpen( - void *port, - int baudrate, - int data_bits, - int parity = 0, - int stop_bits = 0, + const char *port, + const cpp_core::SerialConfig *config, ErrorCallbackT error_callback = nullptr ) -> intptr_t; ``` +Line settings and per-operation timeout settings use explicit configuration +structures. `flow_mode` is applied as part of `serialOpen` together with the +other line settings: + +```cpp +constexpr auto serial_config = cpp_core::SerialConfig::make< + 115'200, + cpp_core::DataBits::kEight, + cpp_core::Parity::kNone, + cpp_core::StopBits::kOne, + cpp_core::FlowControl::kRtsCts>(); +constexpr auto timeout_config = cpp_core::SerialTimeoutConfig::make<50, 1>(); + +const auto handle = serialOpen(port, &serial_config); +const auto bytes_read = serialRead(handle, buffer, buffer_size, &timeout_config); +``` + This model keeps the ABI easy to consume from TypeScript hosts, Rust, Python, or other FFI hosts without requiring C++ runtime coupling. For C++ callers, the helper surface includes: @@ -116,7 +127,7 @@ For C++ callers, the helper surface includes: - `include/cpp_core/result.hpp`: `Result`, `Status`, `forwardUnexpected(...)`, plus the native `std::expected` monadic operations - `include/cpp_core/scope_guard.hpp`: `onScopeExit(...)`, `onScopeFail(...)`, `onScopeSuccess(...)`, `defer(...)` - `include/cpp_core/strong_types.hpp`: arithmetic-preserving strong integral wrappers and enum conversion helpers -- `include/cpp_core/serial_config.hpp`: typed config construction with `Result` validation helpers +- `include/cpp_core/serial_config.hpp`: typed line and timeout config construction with validation helpers - `include/cpp_core/reflection.hpp`: GCC 16 / C++26 reflection helpers such as enum/member counts and names, plus public field counts and names ## Versioning @@ -131,8 +142,8 @@ Version information is generated from Git during CMake configure and written int The version data is exposed through: - the `version` namespace in `include/cpp_core/version.hpp` -- the `cpp_core::Version` struct -- the `getVersion(cpp_core::Version *out)` ABI function +- the `cpp_core::Meta` struct +- the exported `meta(cpp_core::Meta *out)` ABI function ## Relationship to Platform Repositories diff --git a/include/cpp_core/error_callback.h b/include/cpp_core/error_callback.h index ffef1ab..dcd8bc0 100644 --- a/include/cpp_core/error_callback.h +++ b/include/cpp_core/error_callback.h @@ -1,12 +1,3 @@ #pragma once -#ifdef __cplusplus -extern "C" -{ -#endif - - using ErrorCallbackT = void (*)(int error_code, const char *message); - -#ifdef __cplusplus -} // extern "C" -#endif +using ErrorCallbackT = void (*)(int error_code, const char *message); diff --git a/include/cpp_core/interface/get_version.h b/include/cpp_core/interface/get_version.h deleted file mode 100644 index eee1893..0000000 --- a/include/cpp_core/interface/get_version.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once -#include "../module_api.h" -#include "../version.hpp" - -#ifdef __cplusplus -extern "C" -{ -#endif - - namespace cpp_core - { - struct Version - { - int major = version::MAJOR; - int minor = version::MINOR; - int patch = version::PATCH; - const char *commit_hash_short = version::GIT_COMMIT_HASH_SHORT; - const char *commit_hash_full = version::GIT_COMMIT_HASH_FULL; - const char *commit_date = version::GIT_COMMIT_DATE; - const char *branch = version::GIT_BRANCH; - const char *version_string = version::VERSION; - }; - } // namespace cpp_core - - /** - * @brief Copy the compile-time version of the cpp_core library. - * - * Writes the version baked into the library at build-time to the structure - * pointed to by @p out. If @p out is `nullptr` the call is a no-op. - * - * @param[out] out Pointer to a `cpp_core::Version` structure that receives the version information. May be - * `nullptr`. - */ - inline MODULE_API void getVersion(cpp_core::Version *out) - { - if (out != nullptr) - { - *out = cpp_core::Version(); - } - } - -#ifdef __cplusplus -} -#endif diff --git a/include/cpp_core/interface/meta.h b/include/cpp_core/interface/meta.h new file mode 100644 index 0000000..29cc492 --- /dev/null +++ b/include/cpp_core/interface/meta.h @@ -0,0 +1,38 @@ +#pragma once +#include "../module_api.h" +#include "../version.hpp" + +namespace cpp_core +{ +/** + * Build and source metadata for the loaded cpp_core-based library. + */ +struct Meta +{ + int major = version::MAJOR; ///< Semantic-version major component. + int minor = version::MINOR; ///< Semantic-version minor component. + int patch = version::PATCH; ///< Semantic-version patch component. + int commits_since_tag = version::GIT_COMMIT_COUNT; ///< Commits since the closest Git tag. + int is_dirty = version::GIT_IS_DIRTY ? 1 : 0; ///< 1 for uncommitted changes, otherwise 0. + + const char *version_string = version::VERSION; ///< Complete generated version string. + const char *prerelease = version::PRERELEASE; ///< Prerelease identifier, or an empty string. + const char *prerelease_type = version::PRERELEASE_TYPE; ///< Prerelease kind such as `alpha` or `rc`. + const char *prerelease_number = version::PRERELEASE_NUMBER; ///< Prerelease number, or an empty string. + + const char *git_tag = version::GIT_TAG; ///< Closest Git tag. + const char *git_describe_hash = version::GIT_DESCRIBE_HASH; ///< Hash component reported by Git describe. + const char *git_commit_hash_short = version::GIT_COMMIT_HASH_SHORT; ///< Abbreviated commit hash. + const char *git_commit_hash_full = version::GIT_COMMIT_HASH_FULL; ///< Full commit hash. + const char *git_commit_date = version::GIT_COMMIT_DATE; ///< Commit timestamp including timezone. + const char *git_branch = version::GIT_BRANCH; ///< Branch name used for the build. + const char *git_dirty_suffix = version::GIT_DIRTY_SUFFIX; ///< `-dirty` or an empty string. +}; +} // namespace cpp_core + +/** + * @brief Copy metadata for the loaded library. + * + * @param[out] out Structure receiving the metadata. Passing `nullptr` is a no-op. + */ +MODULE_API void meta(cpp_core::Meta *out); diff --git a/include/cpp_core/interface/serial_abort_read.h b/include/cpp_core/interface/serial_abort_read.h index 6ac8008..43fd366 100644 --- a/include/cpp_core/interface/serial_abort_read.h +++ b/include/cpp_core/interface/serial_abort_read.h @@ -3,23 +3,14 @@ #include "../module_api.h" #include -#ifdef __cplusplus -extern "C" -{ -#endif - - /** - * @brief Abort a blocking read operation running in a different thread. - * - * The target read function returns immediately with - * ::cpp_core::StatusCode::Io::kAbortReadError. - * - * @param handle Port handle. - * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. - * @return 0 on success or a negative error code from ::cpp_core::StatusCode on error. - */ - MODULE_API auto serialAbortRead(int64_t handle, ErrorCallbackT error_callback = nullptr) -> int; - -#ifdef __cplusplus -} -#endif +/** + * @brief Abort a blocking read operation running in a different thread. + * + * The target read function returns immediately with + * ::cpp_core::StatusCode::Io::kAbortReadError. + * + * @param handle Port handle. + * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. + * @return 0 on success or a negative error code from ::cpp_core::StatusCode on error. + */ +MODULE_API auto serialAbortRead(int64_t handle, ErrorCallbackT error_callback = nullptr) -> int; diff --git a/include/cpp_core/interface/serial_abort_write.h b/include/cpp_core/interface/serial_abort_write.h index d579d94..42485ff 100644 --- a/include/cpp_core/interface/serial_abort_write.h +++ b/include/cpp_core/interface/serial_abort_write.h @@ -3,23 +3,14 @@ #include "../module_api.h" #include -#ifdef __cplusplus -extern "C" -{ -#endif - - /** - * @brief Abort a blocking write operation running in a different thread. - * - * The target write function returns immediately with - * ::cpp_core::StatusCode::Io::kAbortWriteError. - * - * @param handle Port handle. - * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. - * @return 0 on success or a negative error code from ::cpp_core::StatusCode on error. - */ - MODULE_API auto serialAbortWrite(int64_t handle, ErrorCallbackT error_callback = nullptr) -> int; - -#ifdef __cplusplus -} -#endif +/** + * @brief Abort a blocking write operation running in a different thread. + * + * The target write function returns immediately with + * ::cpp_core::StatusCode::Io::kAbortWriteError. + * + * @param handle Port handle. + * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. + * @return 0 on success or a negative error code from ::cpp_core::StatusCode on error. + */ +MODULE_API auto serialAbortWrite(int64_t handle, ErrorCallbackT error_callback = nullptr) -> int; diff --git a/include/cpp_core/interface/serial_clear_buffer_in.h b/include/cpp_core/interface/serial_clear_buffer_in.h index 9870293..869ce97 100644 --- a/include/cpp_core/interface/serial_clear_buffer_in.h +++ b/include/cpp_core/interface/serial_clear_buffer_in.h @@ -3,23 +3,14 @@ #include "../module_api.h" #include -#ifdef __cplusplus -extern "C" -{ -#endif - - /** - * @brief Clear (flush) the device's input buffer. - * - * Discards every byte the driver has already received but the application - * has not yet read. - * - * @param handle Port handle. - * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. - * @return 0 on success or a negative error code from ::cpp_core::StatusCode on error. - */ - MODULE_API auto serialClearBufferIn(int64_t handle, ErrorCallbackT error_callback = nullptr) -> int; - -#ifdef __cplusplus -} -#endif +/** + * @brief Clear (flush) the device's input buffer. + * + * Discards every byte the driver has already received but the application + * has not yet read. + * + * @param handle Port handle. + * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. + * @return 0 on success or a negative error code from ::cpp_core::StatusCode on error. + */ +MODULE_API auto serialClearBufferIn(int64_t handle, ErrorCallbackT error_callback = nullptr) -> int; diff --git a/include/cpp_core/interface/serial_clear_buffer_out.h b/include/cpp_core/interface/serial_clear_buffer_out.h index eea1966..341573e 100644 --- a/include/cpp_core/interface/serial_clear_buffer_out.h +++ b/include/cpp_core/interface/serial_clear_buffer_out.h @@ -3,23 +3,14 @@ #include "../module_api.h" #include -#ifdef __cplusplus -extern "C" -{ -#endif - - /** - * @brief Clear (flush) the device's output buffer. - * - * Blocks until all queued bytes have been transmitted and then discards any - * remaining data. - * - * @param handle Port handle. - * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. - * @return 0 on success or a negative error code from ::cpp_core::StatusCode on error. - */ - MODULE_API auto serialClearBufferOut(int64_t handle, ErrorCallbackT error_callback = nullptr) -> int; - -#ifdef __cplusplus -} -#endif +/** + * @brief Clear (flush) the device's output buffer. + * + * Blocks until all queued bytes have been transmitted and then discards any + * remaining data. + * + * @param handle Port handle. + * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. + * @return 0 on success or a negative error code from ::cpp_core::StatusCode on error. + */ +MODULE_API auto serialClearBufferOut(int64_t handle, ErrorCallbackT error_callback = nullptr) -> int; diff --git a/include/cpp_core/interface/serial_close.h b/include/cpp_core/interface/serial_close.h index 0d3a7a5..b0d48df 100644 --- a/include/cpp_core/interface/serial_close.h +++ b/include/cpp_core/interface/serial_close.h @@ -3,23 +3,14 @@ #include "../module_api.h" #include -#ifdef __cplusplus -extern "C" -{ -#endif - - /** - * @brief Close a previously opened serial port. - * - * The handle becomes invalid after the call. Passing an already invalid - * (<= 0) handle is a no-op. - * - * @param handle Handle obtained from serialOpen(). - * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. - * @return 0 on success or a negative error code from ::cpp_core::StatusCode on error. - */ - MODULE_API auto serialClose(int64_t handle, ErrorCallbackT error_callback = nullptr) -> int; - -#ifdef __cplusplus -} -#endif +/** + * @brief Close a previously opened serial port. + * + * The handle becomes invalid after the call. Passing an already invalid + * (<= 0) handle is a no-op. + * + * @param handle Handle obtained from serialOpen(). + * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. + * @return 0 on success or a negative error code from ::cpp_core::StatusCode on error. + */ +MODULE_API auto serialClose(int64_t handle, ErrorCallbackT error_callback = nullptr) -> int; diff --git a/include/cpp_core/interface/serial_drain.h b/include/cpp_core/interface/serial_drain.h deleted file mode 100644 index 16f3cf8..0000000 --- a/include/cpp_core/interface/serial_drain.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once -#include "../error_callback.h" -#include "../module_api.h" -#include - -#ifdef __cplusplus -extern "C" -{ -#endif - - /** - * @brief Wait until the operating-system driver has physically sent every queued byte. - * - * The function blocks the *calling thread* until the device driver reports - * that the transmit FIFO is **empty** - i.e. all bytes handed to previous - * `serialWrite*` calls have been shifted out on the wire. It does *not* - * flush higher-level protocol buffers you may have implemented yourself. - * - * Typical use-case: ensure a complete command frame has left the UART - * before toggling RTS/DTR or powering down the device. - * - * @code{.c} - * // Send a frame and make sure it actually hits the line - * serialWrite(h, frame, frame_len, 50, 1); - * if (serialDrain(h) < 0) { - * fprintf(stderr, "drain failed\n"); - * } - * @endcode - * - * @param handle Port handle. - * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. - * @return 0 on success or a negative error code from ::cpp_core::StatusCode on error. - */ - MODULE_API auto serialDrain(int64_t handle, ErrorCallbackT error_callback = nullptr) -> int; - -#ifdef __cplusplus -} -#endif diff --git a/include/cpp_core/interface/serial_get_baudrate.h b/include/cpp_core/interface/serial_get_baudrate.h index 157cf75..b0de738 100644 --- a/include/cpp_core/interface/serial_get_baudrate.h +++ b/include/cpp_core/interface/serial_get_baudrate.h @@ -3,24 +3,15 @@ #include "../module_api.h" #include -#ifdef __cplusplus -extern "C" -{ -#endif - - /** - * @brief Query the current baud rate of an open serial port. - * - * Reads back the baud rate that the OS driver is currently using. Useful for - * verifying that serialOpen() or serialSetBaudrate() applied the requested - * value. - * - * @param handle Port handle obtained from serialOpen(). - * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. - * @return Current baud rate in bit/s (>= 300) or a negative error code from ::cpp_core::StatusCode. - */ - MODULE_API auto serialGetBaudrate(int64_t handle, ErrorCallbackT error_callback = nullptr) -> int; - -#ifdef __cplusplus -} -#endif +/** + * @brief Query the current baud rate of an open serial port. + * + * Reads back the baud rate that the OS driver is currently using. Useful for + * verifying that serialOpen() or serialSetBaudrate() applied the requested + * value. + * + * @param handle Port handle obtained from serialOpen(). + * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. + * @return Current baud rate in bit/s (>= 300) or a negative error code from ::cpp_core::StatusCode. + */ +MODULE_API auto serialGetBaudrate(int64_t handle, ErrorCallbackT error_callback = nullptr) -> int; diff --git a/include/cpp_core/interface/serial_get_cts.h b/include/cpp_core/interface/serial_get_cts.h index 8b2a695..04200c1 100644 --- a/include/cpp_core/interface/serial_get_cts.h +++ b/include/cpp_core/interface/serial_get_cts.h @@ -3,24 +3,15 @@ #include "../module_api.h" #include -#ifdef __cplusplus -extern "C" -{ -#endif - - /** - * @brief Read the current state of the Clear To Send (CTS) input line. - * - * CTS is asserted by the remote device to indicate it is ready to receive - * data. Polling this line is useful when manual flow-control logic is - * required instead of (or in addition to) automatic RTS/CTS flow control. - * - * @param handle Port handle obtained from serialOpen(). - * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. - * @return 1 if asserted (HIGH), 0 if de-asserted (LOW), or a negative error code from ::cpp_core::StatusCode. - */ - MODULE_API auto serialGetCts(int64_t handle, ErrorCallbackT error_callback = nullptr) -> int; - -#ifdef __cplusplus -} -#endif +/** + * @brief Read the current state of the Clear To Send (CTS) input line. + * + * CTS is asserted by the remote device to indicate it is ready to receive + * data. Polling this line is useful when manual flow-control logic is + * required instead of (or in addition to) automatic RTS/CTS flow control. + * + * @param handle Port handle obtained from serialOpen(). + * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. + * @return 1 if asserted (HIGH), 0 if de-asserted (LOW), or a negative error code from ::cpp_core::StatusCode. + */ +MODULE_API auto serialGetCts(int64_t handle, ErrorCallbackT error_callback = nullptr) -> int; diff --git a/include/cpp_core/interface/serial_get_data_bits.h b/include/cpp_core/interface/serial_get_data_bits.h index ce537d5..8df7f8a 100644 --- a/include/cpp_core/interface/serial_get_data_bits.h +++ b/include/cpp_core/interface/serial_get_data_bits.h @@ -1,22 +1,15 @@ #pragma once #include "../error_callback.h" #include "../module_api.h" +#include "../strong_types.hpp" #include -#ifdef __cplusplus -extern "C" -{ -#endif - - /** - * @brief Query the current number of data bits of an open serial port. - * - * @param handle Port handle obtained from serialOpen(). - * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. - * @return Current data bits (5-8) or a negative error code from ::cpp_core::StatusCode. - */ - MODULE_API auto serialGetDataBits(int64_t handle, ErrorCallbackT error_callback = nullptr) -> int; - -#ifdef __cplusplus -} -#endif +/** + * @brief Query the current number of data bits of an open serial port. + * + * @param handle Port handle obtained from serialOpen(). + * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. + * @return Current data-bit setting. On error, the underlying integer value is a negative error code from + * ::cpp_core::StatusCode. + */ +MODULE_API auto serialGetDataBits(int64_t handle, ErrorCallbackT error_callback = nullptr) -> cpp_core::DataBits; diff --git a/include/cpp_core/interface/serial_get_dcd.h b/include/cpp_core/interface/serial_get_dcd.h index e34021c..49b48b0 100644 --- a/include/cpp_core/interface/serial_get_dcd.h +++ b/include/cpp_core/interface/serial_get_dcd.h @@ -3,24 +3,15 @@ #include "../module_api.h" #include -#ifdef __cplusplus -extern "C" -{ -#endif - - /** - * @brief Read the current state of the Data Carrier Detect (DCD) input line. - * - * DCD is asserted by a modem when a carrier signal has been detected on the - * telephone line. For direct serial links it can serve as a general-purpose - * "connection alive" indicator. - * - * @param handle Port handle obtained from serialOpen(). - * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. - * @return 1 if asserted (HIGH), 0 if de-asserted (LOW), or a negative error code from ::cpp_core::StatusCode. - */ - MODULE_API auto serialGetDcd(int64_t handle, ErrorCallbackT error_callback = nullptr) -> int; - -#ifdef __cplusplus -} -#endif +/** + * @brief Read the current state of the Data Carrier Detect (DCD) input line. + * + * DCD is asserted by a modem when a carrier signal has been detected on the + * telephone line. For direct serial links it can serve as a general-purpose + * "connection alive" indicator. + * + * @param handle Port handle obtained from serialOpen(). + * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. + * @return 1 if asserted (HIGH), 0 if de-asserted (LOW), or a negative error code from ::cpp_core::StatusCode. + */ +MODULE_API auto serialGetDcd(int64_t handle, ErrorCallbackT error_callback = nullptr) -> int; diff --git a/include/cpp_core/interface/serial_get_dsr.h b/include/cpp_core/interface/serial_get_dsr.h index 8408782..34cf871 100644 --- a/include/cpp_core/interface/serial_get_dsr.h +++ b/include/cpp_core/interface/serial_get_dsr.h @@ -3,23 +3,14 @@ #include "../module_api.h" #include -#ifdef __cplusplus -extern "C" -{ -#endif - - /** - * @brief Read the current state of the Data Set Ready (DSR) input line. - * - * DSR is asserted by the remote device to indicate it is powered on and - * ready to communicate. It is the counterpart to DTR. - * - * @param handle Port handle obtained from serialOpen(). - * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. - * @return 1 if asserted (HIGH), 0 if de-asserted (LOW), or a negative error code from ::cpp_core::StatusCode. - */ - MODULE_API auto serialGetDsr(int64_t handle, ErrorCallbackT error_callback = nullptr) -> int; - -#ifdef __cplusplus -} -#endif +/** + * @brief Read the current state of the Data Set Ready (DSR) input line. + * + * DSR is asserted by the remote device to indicate it is powered on and + * ready to communicate. It is the counterpart to DTR. + * + * @param handle Port handle obtained from serialOpen(). + * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. + * @return 1 if asserted (HIGH), 0 if de-asserted (LOW), or a negative error code from ::cpp_core::StatusCode. + */ +MODULE_API auto serialGetDsr(int64_t handle, ErrorCallbackT error_callback = nullptr) -> int; diff --git a/include/cpp_core/interface/serial_get_flow_control.h b/include/cpp_core/interface/serial_get_flow_control.h index 26f25ad..1b970e0 100644 --- a/include/cpp_core/interface/serial_get_flow_control.h +++ b/include/cpp_core/interface/serial_get_flow_control.h @@ -1,22 +1,15 @@ #pragma once #include "../error_callback.h" #include "../module_api.h" +#include "../strong_types.hpp" #include -#ifdef __cplusplus -extern "C" -{ -#endif - - /** - * @brief Query the current flow-control mode of an open serial port. - * - * @param handle Port handle obtained from serialOpen(). - * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. - * @return 0 = none, 1 = RTS/CTS, 2 = XON/XOFF, or a negative error code from ::cpp_core::StatusCode. - */ - MODULE_API auto serialGetFlowControl(int64_t handle, ErrorCallbackT error_callback = nullptr) -> int; - -#ifdef __cplusplus -} -#endif +/** + * @brief Query the current flow-control mode of an open serial port. + * + * @param handle Port handle obtained from serialOpen(). + * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. + * @return Current flow-control mode. On error, the underlying integer value is a negative error code from + * ::cpp_core::StatusCode. + */ +MODULE_API auto serialGetFlowControl(int64_t handle, ErrorCallbackT error_callback = nullptr) -> cpp_core::FlowControl; diff --git a/include/cpp_core/interface/serial_get_parity.h b/include/cpp_core/interface/serial_get_parity.h index cf3106e..3d1c601 100644 --- a/include/cpp_core/interface/serial_get_parity.h +++ b/include/cpp_core/interface/serial_get_parity.h @@ -1,22 +1,15 @@ #pragma once #include "../error_callback.h" #include "../module_api.h" +#include "../strong_types.hpp" #include -#ifdef __cplusplus -extern "C" -{ -#endif - - /** - * @brief Query the current parity setting of an open serial port. - * - * @param handle Port handle obtained from serialOpen(). - * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. - * @return 0 = none, 1 = even, 2 = odd, or a negative error code from ::cpp_core::StatusCode. - */ - MODULE_API auto serialGetParity(int64_t handle, ErrorCallbackT error_callback = nullptr) -> int; - -#ifdef __cplusplus -} -#endif +/** + * @brief Query the current parity setting of an open serial port. + * + * @param handle Port handle obtained from serialOpen(). + * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. + * @return Current parity mode. On error, the underlying integer value is a negative error code from + * ::cpp_core::StatusCode. + */ +MODULE_API auto serialGetParity(int64_t handle, ErrorCallbackT error_callback = nullptr) -> cpp_core::Parity; diff --git a/include/cpp_core/interface/serial_get_ri.h b/include/cpp_core/interface/serial_get_ri.h index 6cffea1..5eeddab 100644 --- a/include/cpp_core/interface/serial_get_ri.h +++ b/include/cpp_core/interface/serial_get_ri.h @@ -3,23 +3,14 @@ #include "../module_api.h" #include -#ifdef __cplusplus -extern "C" -{ -#endif - - /** - * @brief Read the current state of the Ring Indicator (RI) input line. - * - * RI is asserted by a modem to signal an incoming call. On non-modem - * hardware it can be repurposed as a general-purpose input signal. - * - * @param handle Port handle obtained from serialOpen(). - * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. - * @return 1 if asserted (HIGH), 0 if de-asserted (LOW), or a negative error code from ::cpp_core::StatusCode. - */ - MODULE_API auto serialGetRi(int64_t handle, ErrorCallbackT error_callback = nullptr) -> int; - -#ifdef __cplusplus -} -#endif +/** + * @brief Read the current state of the Ring Indicator (RI) input line. + * + * RI is asserted by a modem to signal an incoming call. On non-modem + * hardware it can be repurposed as a general-purpose input signal. + * + * @param handle Port handle obtained from serialOpen(). + * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. + * @return 1 if asserted (HIGH), 0 if de-asserted (LOW), or a negative error code from ::cpp_core::StatusCode. + */ +MODULE_API auto serialGetRi(int64_t handle, ErrorCallbackT error_callback = nullptr) -> int; diff --git a/include/cpp_core/interface/serial_get_stop_bits.h b/include/cpp_core/interface/serial_get_stop_bits.h index 90f39d8..8fb49f8 100644 --- a/include/cpp_core/interface/serial_get_stop_bits.h +++ b/include/cpp_core/interface/serial_get_stop_bits.h @@ -1,22 +1,15 @@ #pragma once #include "../error_callback.h" #include "../module_api.h" +#include "../strong_types.hpp" #include -#ifdef __cplusplus -extern "C" -{ -#endif - - /** - * @brief Query the current stop-bit setting of an open serial port. - * - * @param handle Port handle obtained from serialOpen(). - * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. - * @return 0 = 1 stop bit, 2 = 2 stop bits, or a negative error code from ::cpp_core::StatusCode. - */ - MODULE_API auto serialGetStopBits(int64_t handle, ErrorCallbackT error_callback = nullptr) -> int; - -#ifdef __cplusplus -} -#endif +/** + * @brief Query the current stop-bit setting of an open serial port. + * + * @param handle Port handle obtained from serialOpen(). + * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. + * @return Current stop-bit setting. On error, the underlying integer value is a negative error code from + * ::cpp_core::StatusCode. + */ +MODULE_API auto serialGetStopBits(int64_t handle, ErrorCallbackT error_callback = nullptr) -> cpp_core::StopBits; diff --git a/include/cpp_core/interface/serial_in_bytes_total.h b/include/cpp_core/interface/serial_in_bytes_total.h index 01511bf..4584311 100644 --- a/include/cpp_core/interface/serial_in_bytes_total.h +++ b/include/cpp_core/interface/serial_in_bytes_total.h @@ -3,20 +3,11 @@ #include "../module_api.h" #include -#ifdef __cplusplus -extern "C" -{ -#endif - - /** - * @brief Total number of bytes received since the port was opened. - * - * @param handle Port handle. - * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. - * @return Total number of bytes read or a negative error code from ::cpp_core::StatusCode on error. - */ - MODULE_API auto serialInBytesTotal(int64_t handle, ErrorCallbackT error_callback = nullptr) -> int64_t; - -#ifdef __cplusplus -} -#endif +/** + * @brief Total number of bytes received since the port was opened. + * + * @param handle Port handle. + * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. + * @return Total number of bytes read or a negative error code from ::cpp_core::StatusCode on error. + */ +MODULE_API auto serialInBytesTotal(int64_t handle, ErrorCallbackT error_callback = nullptr) -> int64_t; diff --git a/include/cpp_core/interface/serial_in_bytes_waiting.h b/include/cpp_core/interface/serial_in_bytes_waiting.h index 0db50e1..6430539 100644 --- a/include/cpp_core/interface/serial_in_bytes_waiting.h +++ b/include/cpp_core/interface/serial_in_bytes_waiting.h @@ -3,31 +3,23 @@ #include "../module_api.h" #include -#ifdef __cplusplus -extern "C" -{ -#endif - - /** - * @brief Query how many bytes can be read *immediately* without blocking. - * - * The number reflects the size of the driver's RX FIFO **after** accounting - * for data already consumed by the application. A value of `0` therefore - * means a read call would have to wait for the next byte to arrive. - * - * @code{.c} - * int pending = serialInBytesWaiting(h); - * if (pending > 0) { - * serialRead(h, buf, pending, 0, 1); // non-blocking read - * } - * @endcode - * - * @param handle Port handle. - * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. - * @return Bytes available for instant reading or a negative error code from ::cpp_core::StatusCode on error. - */ - MODULE_API auto serialInBytesWaiting(int64_t handle, ErrorCallbackT error_callback = nullptr) -> int; - -#ifdef __cplusplus -} -#endif +/** + * @brief Query how many bytes are immediately available to read. + * + * The number reflects the size of the driver's RX FIFO **after** accounting + * for data already consumed by the application. A value of `0` therefore + * means a read call would have to wait for the next byte to arrive. + * + * @code{.cpp} + * constexpr cpp_core::SerialTimeoutConfig timeout{.timeout_ms = 0, .multiplier = 1}; + * int pending = serialInBytesWaiting(h); + * if (pending > 0) { + * serialRead(h, buf, pending, &timeout); // returns immediately + * } + * @endcode + * + * @param handle Port handle. + * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. + * @return Bytes available for instant reading or a negative error code from ::cpp_core::StatusCode on error. + */ +MODULE_API auto serialInBytesWaiting(int64_t handle, ErrorCallbackT error_callback = nullptr) -> int; diff --git a/include/cpp_core/interface/serial_list_ports.h b/include/cpp_core/interface/serial_list_ports.h index 66b01e0..bcee02e 100644 --- a/include/cpp_core/interface/serial_list_ports.h +++ b/include/cpp_core/interface/serial_list_ports.h @@ -2,27 +2,18 @@ #include "../error_callback.h" #include "../module_api.h" -#ifdef __cplusplus -extern "C" -{ -#endif - - /** - * @brief Enumerate all available serial ports on the system. - * - * The supplied callback is invoked once for every discovered port. All string - * parameters may be `nullptr` if the information is unknown. - * - * @param callback_fn Callback receiving port information. - * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. - * @return Number of ports found or a negative error code from ::cpp_core::StatusCode on error. - */ - MODULE_API auto serialListPorts(void (*callback_fn)(const char *port, const char *path, const char *manufacturer, - const char *serial_number, const char *pnp_id, - const char *location_id, const char *product_id, - const char *vendor_id), - ErrorCallbackT error_callback = nullptr) -> int; - -#ifdef __cplusplus -} -#endif +/** + * @brief Enumerate all available serial ports on the system. + * + * The supplied callback is invoked once for every discovered port. All string + * parameters may be `nullptr` if the information is unknown. + * + * @param callback_fn Callback receiving port information. + * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. + * @return Number of ports found or a negative error code from ::cpp_core::StatusCode on error. + */ +MODULE_API auto serialListPorts(void (*callback_fn)(const char *port, const char *path, const char *manufacturer, + const char *serial_number, const char *pnp_id, + const char *location_id, const char *product_id, + const char *vendor_id), + ErrorCallbackT error_callback = nullptr) -> int; diff --git a/include/cpp_core/interface/serial_monitor_ports.h b/include/cpp_core/interface/serial_monitor_ports.h deleted file mode 100644 index 01e3b8c..0000000 --- a/include/cpp_core/interface/serial_monitor_ports.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once -#include "../error_callback.h" -#include "../module_api.h" - -#ifdef __cplusplus -extern "C" -{ -#endif - - /** - * @brief Start or stop port attach/detach notifications. - * - * Passing a non-null callback starts monitoring and invokes it with `event = 1` - * for attach and `event = 0` for detach notifications. Passing `nullptr` - * stops a previously running monitor. - * - * @param callback_fn Notification callback or `nullptr` to stop monitoring. - * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. - * @return 0 on success or a negative error code from ::cpp_core::StatusCode on error. - */ - MODULE_API auto serialMonitorPorts(void (*callback_fn)(int event, const char *port), - ErrorCallbackT error_callback = nullptr) -> int; - -#ifdef __cplusplus -} -#endif diff --git a/include/cpp_core/interface/serial_open.h b/include/cpp_core/interface/serial_open.h index 46ec7ec..ddf30b4 100644 --- a/include/cpp_core/interface/serial_open.h +++ b/include/cpp_core/interface/serial_open.h @@ -1,32 +1,22 @@ #pragma once #include "../error_callback.h" #include "../module_api.h" +#include "../serial_config.hpp" #include -#ifdef __cplusplus -extern "C" -{ -#endif - - /** - * @brief Open and configure a serial port. - * - * The function attempts to open the device referenced by @p port and applies - * the given line settings. The pointer is interpreted as - * a UTF-8 encoded null-terminated string (`const char*`) on all platforms. - * - * @param port Null-terminated device identifier (e.g. "COM3", "/dev/ttyUSB0"). Passing `nullptr` results in - * a failure. - * @param baudrate Desired baud rate in bit/s (>= 300). - * @param data_bits Number of data bits (5-8). - * @param parity 0 = none, 1 = even, 2 = odd. - * @param stop_bits 0 = 1 stop bit, 2 = 2 stop bits. - * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. - * @return A positive opaque handle on success or a negative value from ::cpp_core::StatusCode on failure. - */ - MODULE_API auto serialOpen(void *port, int baudrate, int data_bits, int parity = 0, int stop_bits = 0, - ErrorCallbackT error_callback = nullptr) -> intptr_t; - -#ifdef __cplusplus -} -#endif +/** + * @brief Open and configure a serial port. + * + * The function attempts to open the device referenced by @p port and applies + * the line settings in @p config. @p port is interpreted as a UTF-8 encoded + * null-terminated string on all platforms. + * + * @param port Null-terminated device identifier (e.g. "COM3", "/dev/ttyUSB0"). Passing `nullptr` results in + * a failure. + * @param config Serial line configuration. Includes baud rate, data bits, + * parity, stop bits, and flow-control mode. Passing `nullptr` results in a failure. + * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. + * @return A positive opaque handle on success or a negative value from ::cpp_core::StatusCode on failure. + */ +MODULE_API auto serialOpen(const char *port, const cpp_core::SerialConfig *config, + ErrorCallbackT error_callback = nullptr) -> intptr_t; diff --git a/include/cpp_core/interface/serial_out_bytes_total.h b/include/cpp_core/interface/serial_out_bytes_total.h index d0c692f..4946ad2 100644 --- a/include/cpp_core/interface/serial_out_bytes_total.h +++ b/include/cpp_core/interface/serial_out_bytes_total.h @@ -3,20 +3,11 @@ #include "../module_api.h" #include -#ifdef __cplusplus -extern "C" -{ -#endif - - /** - * @brief Total number of bytes transmitted since the port was opened. - * - * @param handle Port handle. - * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. - * @return Total number of bytes written or a negative error code from ::cpp_core::StatusCode on error. - */ - MODULE_API auto serialOutBytesTotal(int64_t handle, ErrorCallbackT error_callback = nullptr) -> int64_t; - -#ifdef __cplusplus -} -#endif +/** + * @brief Total number of bytes transmitted since the port was opened. + * + * @param handle Port handle. + * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. + * @return Total number of bytes written or a negative error code from ::cpp_core::StatusCode on error. + */ +MODULE_API auto serialOutBytesTotal(int64_t handle, ErrorCallbackT error_callback = nullptr) -> int64_t; diff --git a/include/cpp_core/interface/serial_out_bytes_waiting.h b/include/cpp_core/interface/serial_out_bytes_waiting.h index 713946f..afe09fc 100644 --- a/include/cpp_core/interface/serial_out_bytes_waiting.h +++ b/include/cpp_core/interface/serial_out_bytes_waiting.h @@ -3,29 +3,20 @@ #include "../module_api.h" #include -#ifdef __cplusplus -extern "C" -{ -#endif - - /** - * @brief Return the number of bytes that have been accepted by the driver but not yet sent. - * - * Useful for gauging transmission progress in the background or for pacing - * further writes to avoid unbounded buffering. - * - * @code{.c} - * while (serialOutBytesWaiting(h) > 0) { - * usleep(1000); // wait 1 ms and poll again - * } - * @endcode - * - * @param handle Port handle. - * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. - * @return Bytes still waiting in the TX FIFO or a negative error code from ::cpp_core::StatusCode on error. - */ - MODULE_API auto serialOutBytesWaiting(int64_t handle, ErrorCallbackT error_callback = nullptr) -> int; - -#ifdef __cplusplus -} -#endif +/** + * @brief Return the number of bytes that have been accepted by the driver but not yet sent. + * + * Useful for gauging transmission progress in the background or for pacing + * further writes to avoid unbounded buffering. + * + * @code{.c} + * while (serialOutBytesWaiting(h) > 0) { + * usleep(1000); // wait 1 ms and poll again + * } + * @endcode + * + * @param handle Port handle. + * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. + * @return Bytes still waiting in the TX FIFO or a negative error code from ::cpp_core::StatusCode on error. + */ +MODULE_API auto serialOutBytesWaiting(int64_t handle, ErrorCallbackT error_callback = nullptr) -> int; diff --git a/include/cpp_core/interface/serial_read.h b/include/cpp_core/interface/serial_read.h index 4403316..97dfb26 100644 --- a/include/cpp_core/interface/serial_read.h +++ b/include/cpp_core/interface/serial_read.h @@ -1,33 +1,23 @@ #pragma once #include "../error_callback.h" #include "../module_api.h" +#include "../serial_config.hpp" #include -#ifdef __cplusplus -extern "C" -{ -#endif - - /** - * @brief Read raw bytes from the serial port. - * - * The call blocks for at most @p timeout_ms milliseconds while waiting for - * the FIRST byte. For every subsequent byte the individual timeout is - * calculated as `timeout_ms * multiplier`. - * - * @param handle Port handle. - * @param buffer Destination buffer (must not be `nullptr`). - * @param buffer_size Size of @p buffer in bytes (> 0). - * @param timeout_ms Base timeout per byte in milliseconds (applied to the first byte as-is; subsequent bytes use - * `timeout_ms * multiplier`). - * @param multiplier Factor applied to @p timeout_ms for every byte after the first. 0 -> return immediately after - * the first byte. - * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. - * @return Bytes read (0 on timeout) or a negative error code from ::cpp_core::StatusCode on error. - */ - MODULE_API auto serialRead(int64_t handle, void *buffer, int buffer_size, int timeout_ms, int multiplier, - ErrorCallbackT error_callback = nullptr) -> int; - -#ifdef __cplusplus -} -#endif +/** + * @brief Read raw bytes from the serial port. + * + * The call blocks for at most `timeout_config->timeout_ms` milliseconds while waiting for + * the FIRST byte. For every subsequent byte the individual timeout is + * calculated as `timeout_ms * multiplier` from @p timeout_config. + * + * @param handle Port handle. + * @param buffer Destination buffer (must not be `nullptr`). + * @param buffer_size Size of @p buffer in bytes (> 0). + * @param timeout_config Timeout settings for this operation. Passing `nullptr` results in a failure. + * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. + * @return Bytes read (0 on timeout) or a negative error code from ::cpp_core::StatusCode on error. + */ +MODULE_API auto serialRead(int64_t handle, std::uint8_t *buffer, int buffer_size, + const cpp_core::SerialTimeoutConfig *timeout_config, ErrorCallbackT error_callback = nullptr) + -> int; diff --git a/include/cpp_core/interface/serial_read_line.h b/include/cpp_core/interface/serial_read_line.h deleted file mode 100644 index 75a122c..0000000 --- a/include/cpp_core/interface/serial_read_line.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once -#include "../error_callback.h" -#include "../module_api.h" -#include - -#ifdef __cplusplus -extern "C" -{ -#endif - - /** - * @brief Read a single line terminated by '\n'. - * - * Timeout handling is identical to serialRead(); the newline character is - * included in the returned data. - * - * @param handle Port handle. - * @param buffer Destination buffer. - * @param buffer_size Capacity of @p buffer in bytes. - * @param timeout_ms Base timeout per byte in milliseconds (applied to the first byte as-is; subsequent bytes use - * `timeout_ms * multiplier`). - * @param multiplier Factor applied to the timeout for subsequent bytes. - * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. - * @return Bytes read (0 on timeout) or a negative error code from ::cpp_core::StatusCode on error. - */ - MODULE_API auto serialReadLine(int64_t handle, void *buffer, int buffer_size, int timeout_ms, int multiplier, - ErrorCallbackT error_callback = nullptr) -> int; - -#ifdef __cplusplus -} -#endif diff --git a/include/cpp_core/interface/serial_read_until.h b/include/cpp_core/interface/serial_read_until.h deleted file mode 100644 index 5876b2c..0000000 --- a/include/cpp_core/interface/serial_read_until.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once -#include "../error_callback.h" -#include "../module_api.h" -#include - -#ifdef __cplusplus -extern "C" -{ -#endif - - /** - * @brief Read bytes until a terminator character appears. - * - * Semantics are identical to serialRead() but reading stops as soon as the - * byte pointed to by @p until_char has been received. The terminator is part - * of the returned data. - * - * @param handle Port handle. - * @param buffer Destination buffer. - * @param buffer_size Capacity of @p buffer in bytes. - * @param timeout_ms Base timeout per byte in milliseconds (applied to the first byte as-is; each additional byte - * uses `timeout_ms * multiplier`). - * @param multiplier Factor applied to the timeout for every additional byte. - * @param until_char Pointer to the terminator character (must not be `nullptr`). - * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. - * @return Bytes read (including the terminator), 0 on timeout or a negative error code from ::cpp_core::StatusCode - * on error. - */ - MODULE_API auto serialReadUntil(int64_t handle, void *buffer, int buffer_size, int timeout_ms, int multiplier, - void *until_char, ErrorCallbackT error_callback = nullptr) -> int; - -#ifdef __cplusplus -} -#endif diff --git a/include/cpp_core/interface/serial_read_until_sequence.h b/include/cpp_core/interface/serial_read_until_sequence.h index f0ee7d7..b7d2d5d 100644 --- a/include/cpp_core/interface/serial_read_until_sequence.h +++ b/include/cpp_core/interface/serial_read_until_sequence.h @@ -1,33 +1,26 @@ #pragma once #include "../error_callback.h" #include "../module_api.h" +#include "../serial_config.hpp" #include -#ifdef __cplusplus -extern "C" -{ -#endif - - /** - * @brief Read until a specific byte sequence appears. - * - * Works like serialReadUntil() but supports an arbitrary terminator string. - * The terminator is included in the returned data. - * - * @param handle Port handle. - * @param buffer Destination buffer. - * @param buffer_size Capacity of @p buffer in bytes. - * @param timeout_ms Base timeout per byte in milliseconds (first byte uses this value; each additional byte uses - * `timeout_ms * multiplier`). - * @param multiplier Factor applied to the timeout for subsequent bytes. - * @param sequence Pointer to the terminating byte sequence (must not be `nullptr`). - * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. - * @return Bytes read (including the terminator) or a negative error code from ::cpp_core::StatusCode on error. - */ - MODULE_API auto serialReadUntilSequence(int64_t handle, void *buffer, int buffer_size, int timeout_ms, - int multiplier, void *sequence, ErrorCallbackT error_callback = nullptr) - -> int; - -#ifdef __cplusplus -} -#endif +/** + * @brief Read until a specific byte sequence appears. + * + * Reads bytes using the same timeout semantics as serialRead() and stops as + * soon as @p sequence has been received. Sequences may contain zero bytes, + * and the complete terminator is included in the returned data. + * + * @param handle Port handle. + * @param buffer Destination buffer. + * @param buffer_size Capacity of @p buffer in bytes. + * @param timeout_config Timeout settings for this operation. Passing `nullptr` results in a failure. + * @param sequence Terminating byte sequence (must not be `nullptr`). + * @param sequence_size Size of @p sequence in bytes (> 0). + * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. + * @return Bytes read (including the terminator) or a negative error code from ::cpp_core::StatusCode on error. + */ +MODULE_API auto serialReadUntilSequence(int64_t handle, std::uint8_t *buffer, int buffer_size, + const cpp_core::SerialTimeoutConfig *timeout_config, + const std::uint8_t *sequence, int sequence_size, + ErrorCallbackT error_callback = nullptr) -> int; diff --git a/include/cpp_core/interface/serial_send_break.h b/include/cpp_core/interface/serial_send_break.h index d389f14..44a95af 100644 --- a/include/cpp_core/interface/serial_send_break.h +++ b/include/cpp_core/interface/serial_send_break.h @@ -3,31 +3,22 @@ #include "../module_api.h" #include -#ifdef __cplusplus -extern "C" -{ -#endif - - /** - * @brief Send a break condition on the serial line. - * - * A break is a sustained logic-LOW that lasts longer than a normal - * character frame. Many protocols rely on it: - * - * - **DMX512**: A break of >= 88 us marks the start of a new frame. - * - **LIN bus**: The master starts each frame with a 13-bit break. - * - **MODBUS RTU**: Some implementations use break for frame sync. - * - * The @p duration_ms parameter is a *minimum* - the actual break may be - * slightly longer due to OS scheduling. - * - * @param handle Port handle obtained from serialOpen(). - * @param duration_ms Break duration in milliseconds (> 0). - * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. - * @return 0 on success or a negative error code from ::cpp_core::StatusCode on error. - */ - MODULE_API auto serialSendBreak(int64_t handle, int duration_ms, ErrorCallbackT error_callback = nullptr) -> int; - -#ifdef __cplusplus -} -#endif +/** + * @brief Send a break condition on the serial line. + * + * A break is a sustained logic-LOW that lasts longer than a normal + * character frame. Many protocols rely on it: + * + * - **DMX512**: A break of >= 88 us marks the start of a new frame. + * - **LIN bus**: The master starts each frame with a 13-bit break. + * - **MODBUS RTU**: Some implementations use break for frame sync. + * + * The @p duration_ms parameter is a *minimum* - the actual break may be + * slightly longer due to OS scheduling. + * + * @param handle Port handle obtained from serialOpen(). + * @param duration_ms Break duration in milliseconds (> 0). + * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. + * @return 0 on success or a negative error code from ::cpp_core::StatusCode on error. + */ +MODULE_API auto serialSendBreak(int64_t handle, int duration_ms, ErrorCallbackT error_callback = nullptr) -> int; diff --git a/include/cpp_core/interface/serial_set_baudrate.h b/include/cpp_core/interface/serial_set_baudrate.h index fbd615b..52d8c2b 100644 --- a/include/cpp_core/interface/serial_set_baudrate.h +++ b/include/cpp_core/interface/serial_set_baudrate.h @@ -3,29 +3,19 @@ #include "../module_api.h" #include -#ifdef __cplusplus -extern "C" -{ -#endif - - /** - * @brief Change the baud rate of an already-open serial port. - * - * The new rate takes effect immediately without closing and re-opening the - * port. All other line settings (data bits, parity, stop bits, flow control) - * remain unchanged. - * - * Typical use-case: a bootloader handshake starts at a safe 9600 baud and - * then both sides switch to a higher speed for the actual data transfer. - * - * @param handle Port handle obtained from serialOpen(). - * @param baudrate New baud rate in bit/s (>= 300). - * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. - * @return 0 on success or a negative error code from ::cpp_core::StatusCode on error. - */ - MODULE_API auto serialSetBaudrate(int64_t handle, int baudrate, - ErrorCallbackT error_callback = nullptr) -> int; - -#ifdef __cplusplus -} -#endif +/** + * @brief Change the baud rate of an already-open serial port. + * + * The new rate takes effect immediately without closing and re-opening the + * port. All other line settings (data bits, parity, stop bits, flow control) + * remain unchanged. + * + * Typical use-case: a bootloader handshake starts at a safe 9600 baud and + * then both sides switch to a higher speed for the actual data transfer. + * + * @param handle Port handle obtained from serialOpen(). + * @param baudrate New baud rate in bit/s (>= 300). + * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. + * @return 0 on success or a negative error code from ::cpp_core::StatusCode on error. + */ +MODULE_API auto serialSetBaudrate(int64_t handle, int baudrate, ErrorCallbackT error_callback = nullptr) -> int; diff --git a/include/cpp_core/interface/serial_set_data_bits.h b/include/cpp_core/interface/serial_set_data_bits.h index 5887397..62b4344 100644 --- a/include/cpp_core/interface/serial_set_data_bits.h +++ b/include/cpp_core/interface/serial_set_data_bits.h @@ -1,26 +1,18 @@ #pragma once #include "../error_callback.h" #include "../module_api.h" +#include "../strong_types.hpp" #include -#ifdef __cplusplus -extern "C" -{ -#endif - - /** - * @brief Change the number of data bits on an already-open serial port. - * - * Takes effect immediately. All other line settings remain unchanged. - * - * @param handle Port handle obtained from serialOpen(). - * @param data_bits Number of data bits (5-8). - * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. - * @return 0 on success or a negative error code from ::cpp_core::StatusCode on error. - */ - MODULE_API auto serialSetDataBits(int64_t handle, int data_bits, - ErrorCallbackT error_callback = nullptr) -> int; - -#ifdef __cplusplus -} -#endif +/** + * @brief Change the number of data bits on an already-open serial port. + * + * Takes effect immediately. All other line settings remain unchanged. + * + * @param handle Port handle obtained from serialOpen(). + * @param data_bits Number of data bits. + * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. + * @return 0 on success or a negative error code from ::cpp_core::StatusCode on error. + */ +MODULE_API auto serialSetDataBits(int64_t handle, cpp_core::DataBits data_bits, ErrorCallbackT error_callback = nullptr) + -> int; diff --git a/include/cpp_core/interface/serial_set_dtr.h b/include/cpp_core/interface/serial_set_dtr.h index 97c4c5e..0c0d35d 100644 --- a/include/cpp_core/interface/serial_set_dtr.h +++ b/include/cpp_core/interface/serial_set_dtr.h @@ -3,26 +3,17 @@ #include "../module_api.h" #include -#ifdef __cplusplus -extern "C" -{ -#endif - - /** - * @brief Set or clear the Data Terminal Ready (DTR) modem control line. - * - * DTR is commonly used for: - * - Signalling readiness to the remote device. - * - Triggering a board reset on Arduino-compatible hardware. - * - Half-duplex direction control on RS-485 adapters. - * - * @param handle Port handle obtained from serialOpen(). - * @param state Non-zero to assert (HIGH), zero to de-assert (LOW). - * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. - * @return 0 on success or a negative error code from ::cpp_core::StatusCode on error. - */ - MODULE_API auto serialSetDtr(int64_t handle, int state, ErrorCallbackT error_callback = nullptr) -> int; - -#ifdef __cplusplus -} -#endif +/** + * @brief Set or clear the Data Terminal Ready (DTR) modem control line. + * + * DTR is commonly used for: + * - Signalling readiness to the remote device. + * - Triggering a board reset on Arduino-compatible hardware. + * - Half-duplex direction control on RS-485 adapters. + * + * @param handle Port handle obtained from serialOpen(). + * @param state Non-zero to assert (HIGH), zero to de-assert (LOW). + * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. + * @return 0 on success or a negative error code from ::cpp_core::StatusCode on error. + */ +MODULE_API auto serialSetDtr(int64_t handle, int state, ErrorCallbackT error_callback = nullptr) -> int; diff --git a/include/cpp_core/interface/serial_set_error_callback.h b/include/cpp_core/interface/serial_set_error_callback.h index 46e28a2..23fc278 100644 --- a/include/cpp_core/interface/serial_set_error_callback.h +++ b/include/cpp_core/interface/serial_set_error_callback.h @@ -2,21 +2,12 @@ #include "../error_callback.h" #include "../module_api.h" -#ifdef __cplusplus -extern "C" -{ -#endif - - /** - * @brief Register a callback that is invoked whenever an error occurs. - * - * Pass `nullptr` to disable the callback. - * - * @param error_callback Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. Gets - * invoked on any error. - */ - MODULE_API void serialSetErrorCallback(ErrorCallbackT error_callback); - -#ifdef __cplusplus -} -#endif +/** + * @brief Register a callback that is invoked whenever an error occurs. + * + * Pass `nullptr` to disable the callback. + * + * @param error_callback Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. Gets + * invoked on any error. + */ +MODULE_API void serialSetErrorCallback(ErrorCallbackT error_callback); diff --git a/include/cpp_core/interface/serial_set_event_callback.h b/include/cpp_core/interface/serial_set_event_callback.h new file mode 100644 index 0000000..a9c1bf9 --- /dev/null +++ b/include/cpp_core/interface/serial_set_event_callback.h @@ -0,0 +1,18 @@ +#pragma once +#include "../error_callback.h" +#include "../module_api.h" +#include "../strong_types.hpp" + +/** + * @brief Register or clear the serial-port lifecycle event callback. + * + * Passing a non-null callback starts monitoring and invokes it with + * ::cpp_core::PortEvent::kAttached or ::cpp_core::PortEvent::kDetached. + * Passing `nullptr` clears the callback and stops monitoring. + * + * @param callback_fn Notification callback or `nullptr` to stop monitoring. + * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. + * @return 0 on success or a negative error code from ::cpp_core::StatusCode on error. + */ +MODULE_API auto serialSetEventCallback(void (*callback_fn)(cpp_core::PortEvent event, const char *port), + ErrorCallbackT error_callback = nullptr) -> int; diff --git a/include/cpp_core/interface/serial_set_flow_control.h b/include/cpp_core/interface/serial_set_flow_control.h index d5bf779..a9131ff 100644 --- a/include/cpp_core/interface/serial_set_flow_control.h +++ b/include/cpp_core/interface/serial_set_flow_control.h @@ -1,38 +1,27 @@ #pragma once #include "../error_callback.h" #include "../module_api.h" +#include "../strong_types.hpp" #include -#ifdef __cplusplus -extern "C" -{ -#endif - - /** - * @brief Configure the flow-control mode for an open serial port. - * - * Flow control prevents buffer overruns when one side is slower than the - * other. Three modes are supported: - * - * | @p mode | Meaning | - * |---------|-----------------------------------------------| - * | 0 | None - no flow control (default after open). | - * | 1 | Hardware (RTS/CTS) - the UART automatically | - * | | de-asserts RTS when the RX buffer is full and | - * | | pauses TX when CTS is de-asserted. | - * | 2 | Software (XON/XOFF) - in-band control chars | - * | | `0x11` (XON) and `0x13` (XOFF) are sent to | - * | | pause/resume the remote transmitter. | - * - * Changing the mode on an already-open port takes effect immediately. - * - * @param handle Port handle obtained from serialOpen(). - * @param mode Flow-control mode: 0 = none, 1 = RTS/CTS, 2 = XON/XOFF. - * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. - * @return 0 on success or a negative error code from ::cpp_core::StatusCode on error. - */ - MODULE_API auto serialSetFlowControl(int64_t handle, int mode, ErrorCallbackT error_callback = nullptr) -> int; - -#ifdef __cplusplus -} -#endif +/** + * @brief Configure the flow-control mode for an open serial port. + * + * Flow control prevents buffer overruns when one side is slower than the + * other. Three modes are supported: + * + * | @p flow_mode | Meaning | + * |------------------------------|----------------------------------------------| + * | `FlowControl::kNone` | No flow control. | + * | `FlowControl::kRtsCts` | Hardware RTS/CTS flow control. | + * | `FlowControl::kXonXoff` | Software XON/XOFF flow control. | + * + * Changing the mode on an already-open port takes effect immediately. + * + * @param handle Port handle obtained from serialOpen(). + * @param flow_mode Flow-control mode. + * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. + * @return 0 on success or a negative error code from ::cpp_core::StatusCode on error. + */ +MODULE_API auto serialSetFlowControl(int64_t handle, cpp_core::FlowControl flow_mode, + ErrorCallbackT error_callback = nullptr) -> int; diff --git a/include/cpp_core/interface/serial_set_parity.h b/include/cpp_core/interface/serial_set_parity.h index d67193f..cac2775 100644 --- a/include/cpp_core/interface/serial_set_parity.h +++ b/include/cpp_core/interface/serial_set_parity.h @@ -1,25 +1,18 @@ #pragma once #include "../error_callback.h" #include "../module_api.h" +#include "../strong_types.hpp" #include -#ifdef __cplusplus -extern "C" -{ -#endif - - /** - * @brief Change the parity setting on an already-open serial port. - * - * Takes effect immediately. All other line settings remain unchanged. - * - * @param handle Port handle obtained from serialOpen(). - * @param parity 0 = none, 1 = even, 2 = odd. - * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. - * @return 0 on success or a negative error code from ::cpp_core::StatusCode on error. - */ - MODULE_API auto serialSetParity(int64_t handle, int parity, ErrorCallbackT error_callback = nullptr) -> int; - -#ifdef __cplusplus -} -#endif +/** + * @brief Change the parity setting on an already-open serial port. + * + * Takes effect immediately. All other line settings remain unchanged. + * + * @param handle Port handle obtained from serialOpen(). + * @param parity Parity mode. + * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. + * @return 0 on success or a negative error code from ::cpp_core::StatusCode on error. + */ +MODULE_API auto serialSetParity(int64_t handle, cpp_core::Parity parity, ErrorCallbackT error_callback = nullptr) + -> int; diff --git a/include/cpp_core/interface/serial_set_read_callback.h b/include/cpp_core/interface/serial_set_read_callback.h index 2b45ae0..4a191bd 100644 --- a/include/cpp_core/interface/serial_set_read_callback.h +++ b/include/cpp_core/interface/serial_set_read_callback.h @@ -1,20 +1,11 @@ #pragma once #include "../module_api.h" -#ifdef __cplusplus -extern "C" -{ -#endif - - /** - * @brief Register a callback that is invoked whenever bytes are read. - * - * Pass `nullptr` to disable the callback. - * - * @param callback Function receiving the number of bytes that have just been read. - */ - MODULE_API void serialSetReadCallback(void (*callback_fn)(int bytes_read)); - -#ifdef __cplusplus -} -#endif +/** + * @brief Register a callback that is invoked whenever bytes are read. + * + * Pass `nullptr` to disable the callback. + * + * @param callback Function receiving the number of bytes that have just been read. + */ +MODULE_API void serialSetReadCallback(void (*callback_fn)(int bytes_read)); diff --git a/include/cpp_core/interface/serial_set_rts.h b/include/cpp_core/interface/serial_set_rts.h index e6f2bf9..faa9ce0 100644 --- a/include/cpp_core/interface/serial_set_rts.h +++ b/include/cpp_core/interface/serial_set_rts.h @@ -3,26 +3,17 @@ #include "../module_api.h" #include -#ifdef __cplusplus -extern "C" -{ -#endif - - /** - * @brief Set or clear the Request To Send (RTS) modem control line. - * - * RTS is typically used for hardware flow control (RTS/CTS) or - * as a transmit-enable signal in half-duplex RS-485 setups. When - * hardware flow control is **not** enabled via serialSetFlowControl(), - * this function gives manual control over the line. - * - * @param handle Port handle obtained from serialOpen(). - * @param state Non-zero to assert (HIGH), zero to de-assert (LOW). - * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. - * @return 0 on success or a negative error code from ::cpp_core::StatusCode on error. - */ - MODULE_API auto serialSetRts(int64_t handle, int state, ErrorCallbackT error_callback = nullptr) -> int; - -#ifdef __cplusplus -} -#endif +/** + * @brief Set or clear the Request To Send (RTS) modem control line. + * + * RTS is typically used for hardware flow control (RTS/CTS) or + * as a transmit-enable signal in half-duplex RS-485 setups. When + * hardware flow control is **not** enabled via serialSetFlowControl(), + * this function gives manual control over the line. + * + * @param handle Port handle obtained from serialOpen(). + * @param state Non-zero to assert (HIGH), zero to de-assert (LOW). + * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. + * @return 0 on success or a negative error code from ::cpp_core::StatusCode on error. + */ +MODULE_API auto serialSetRts(int64_t handle, int state, ErrorCallbackT error_callback = nullptr) -> int; diff --git a/include/cpp_core/interface/serial_set_stop_bits.h b/include/cpp_core/interface/serial_set_stop_bits.h index fe9fe98..f64ba9a 100644 --- a/include/cpp_core/interface/serial_set_stop_bits.h +++ b/include/cpp_core/interface/serial_set_stop_bits.h @@ -1,26 +1,18 @@ #pragma once #include "../error_callback.h" #include "../module_api.h" +#include "../strong_types.hpp" #include -#ifdef __cplusplus -extern "C" -{ -#endif - - /** - * @brief Change the stop-bit setting on an already-open serial port. - * - * Takes effect immediately. All other line settings remain unchanged. - * - * @param handle Port handle obtained from serialOpen(). - * @param stop_bits 0 = 1 stop bit, 2 = 2 stop bits. - * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. - * @return 0 on success or a negative error code from ::cpp_core::StatusCode on error. - */ - MODULE_API auto serialSetStopBits(int64_t handle, int stop_bits, - ErrorCallbackT error_callback = nullptr) -> int; - -#ifdef __cplusplus -} -#endif +/** + * @brief Change the stop-bit setting on an already-open serial port. + * + * Takes effect immediately. All other line settings remain unchanged. + * + * @param handle Port handle obtained from serialOpen(). + * @param stop_bits Stop-bit mode. + * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. + * @return 0 on success or a negative error code from ::cpp_core::StatusCode on error. + */ +MODULE_API auto serialSetStopBits(int64_t handle, cpp_core::StopBits stop_bits, ErrorCallbackT error_callback = nullptr) + -> int; diff --git a/include/cpp_core/interface/serial_set_write_callback.h b/include/cpp_core/interface/serial_set_write_callback.h index a521e40..ff1fcfe 100644 --- a/include/cpp_core/interface/serial_set_write_callback.h +++ b/include/cpp_core/interface/serial_set_write_callback.h @@ -1,20 +1,11 @@ #pragma once #include "../module_api.h" -#ifdef __cplusplus -extern "C" -{ -#endif - - /** - * @brief Register a callback that is invoked whenever bytes are written. - * - * Pass `nullptr` to disable the callback. - * - * @param callback Function receiving the number of bytes that have just been written. - */ - MODULE_API void serialSetWriteCallback(void (*callback_fn)(int bytes_written)); - -#ifdef __cplusplus -} -#endif +/** + * @brief Register a callback that is invoked whenever bytes are written. + * + * Pass `nullptr` to disable the callback. + * + * @param callback Function receiving the number of bytes that have just been written. + */ +MODULE_API void serialSetWriteCallback(void (*callback_fn)(int bytes_written)); diff --git a/include/cpp_core/interface/serial_wait_for_drain.h b/include/cpp_core/interface/serial_wait_for_drain.h new file mode 100644 index 0000000..d36463d --- /dev/null +++ b/include/cpp_core/interface/serial_wait_for_drain.h @@ -0,0 +1,30 @@ +#pragma once +#include "../error_callback.h" +#include "../module_api.h" +#include + +/** + * @brief Wait until the operating-system driver has physically sent every queued byte. + * + * The function blocks the *calling thread* until the device driver reports + * that the transmit FIFO is **empty** - i.e. all bytes handed to previous + * `serialWrite*` calls have been shifted out on the wire. It does *not* + * flush higher-level protocol buffers you may have implemented yourself. + * + * Typical use-case: ensure a complete command frame has left the UART + * before toggling RTS/DTR or powering down the device. + * + * @code{.cpp} + * // Send a frame and make sure it actually hits the line + * constexpr cpp_core::SerialTimeoutConfig timeout{.timeout_ms = 50, .multiplier = 1}; + * serialWrite(h, frame, frame_len, &timeout); + * if (serialWaitForDrain(h) < 0) { + * fprintf(stderr, "drain failed\n"); + * } + * @endcode + * + * @param handle Port handle. + * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. + * @return 0 on success or a negative error code from ::cpp_core::StatusCode on error. + */ +MODULE_API auto serialWaitForDrain(int64_t handle, ErrorCallbackT error_callback = nullptr) -> int; diff --git a/include/cpp_core/interface/serial_write.h b/include/cpp_core/interface/serial_write.h index be6734f..2d2678d 100644 --- a/include/cpp_core/interface/serial_write.h +++ b/include/cpp_core/interface/serial_write.h @@ -1,31 +1,22 @@ #pragma once #include "../error_callback.h" #include "../module_api.h" +#include "../serial_config.hpp" #include -#ifdef __cplusplus -extern "C" -{ -#endif - - /** - * @brief Write raw bytes to the serial port. - * - * Timeout handling mirrors serialRead(): @p timeout_ms applies to the first - * byte, `timeout_ms * multiplier` to every subsequent one. - * - * @param handle Port handle. - * @param buffer Data to transmit (must not be `nullptr`). - * @param buffer_size Number of bytes in @p buffer (> 0). - * @param timeout_ms Base timeout per byte in milliseconds (applied to the first byte as-is; subsequent bytes use - * `timeout_ms * multiplier`). - * @param multiplier Factor applied to the timeout for subsequent bytes. - * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. - * @return Bytes written (may be 0 on timeout) or a negative error code from ::cpp_core::StatusCode on error. - */ - MODULE_API auto serialWrite(int64_t handle, const void *buffer, int buffer_size, int timeout_ms, int multiplier, - ErrorCallbackT error_callback = nullptr) -> int; - -#ifdef __cplusplus -} -#endif +/** + * @brief Write raw bytes to the serial port. + * + * Timeout handling mirrors serialRead(): `timeout_config->timeout_ms` applies + * to the first byte, `timeout_ms * multiplier` to every subsequent one. + * + * @param handle Port handle. + * @param buffer Data to transmit (must not be `nullptr`). + * @param buffer_size Number of bytes in @p buffer (> 0). + * @param timeout_config Timeout settings for this operation. Passing `nullptr` results in a failure. + * @param error_callback [optional] Callback to invoke on error. Defined in error_callback.h. Default is `nullptr`. + * @return Bytes written (may be 0 on timeout) or a negative error code from ::cpp_core::StatusCode on error. + */ +MODULE_API auto serialWrite(int64_t handle, const std::uint8_t *buffer, int buffer_size, + const cpp_core::SerialTimeoutConfig *timeout_config, + ErrorCallbackT error_callback = nullptr) -> int; diff --git a/include/cpp_core/module_api.h b/include/cpp_core/module_api.h index e066a42..82478d4 100644 --- a/include/cpp_core/module_api.h +++ b/include/cpp_core/module_api.h @@ -1,11 +1,15 @@ #pragma once +/** + * Give an exported function C language linkage and platform-specific symbol visibility. + * This macro is intended for C++ declarations and definitions only. + */ #if defined(_WIN32) || defined(__CYGWIN__) #ifdef cpp_bindings_windows_EXPORTS -#define MODULE_API __declspec(dllexport) +#define MODULE_API extern "C" __declspec(dllexport) #else -#define MODULE_API __declspec(dllimport) +#define MODULE_API extern "C" __declspec(dllimport) #endif #else -#define MODULE_API __attribute__((visibility("default"))) +#define MODULE_API extern "C" __attribute__((visibility("default"))) #endif diff --git a/include/cpp_core/reflection.test.cpp b/include/cpp_core/reflection.test.cpp index 1967f84..3eee04f 100644 --- a/include/cpp_core/reflection.test.cpp +++ b/include/cpp_core/reflection.test.cpp @@ -1,4 +1,5 @@ #include "cpp_core/reflection.hpp" +#include "cpp_core/interface/meta.h" #include "cpp_core/result.hpp" #include "cpp_core/serial_config.hpp" #include "cpp_core/strong_types.hpp" @@ -12,15 +13,24 @@ struct PrivateMemberAggregate int value; }; +static_assert(cpp_core::reflection::enumeratorCount() == 4); static_assert(cpp_core::reflection::enumeratorCount() == 3); static_assert(cpp_core::reflection::enumeratorCount() == 2); static_assert(cpp_core::reflection::enumeratorCount() == 3); +static_assert(cpp_core::reflection::enumeratorCount() == 2); +static_assert(cpp_core::reflection::enumeratorName() == "kEight"); static_assert(cpp_core::reflection::enumeratorName() == "kNone"); static_assert(cpp_core::reflection::enumeratorName() == "kEven"); static_assert(cpp_core::reflection::enumerator_name_v == "kXonXoff"); +static_assert(cpp_core::reflection::enumerator_name_v == "kDetached"); static_assert(cpp_core::reflection::hasPubliclyReflectableFields()); -static_assert(cpp_core::reflection::publicFieldCount() == 4); +static_assert(cpp_core::reflection::publicFieldCount() == 5); static_assert(cpp_core::reflection::publicFieldName() == "parity"); +static_assert(cpp_core::reflection::publicFieldName() == "flow_mode"); +static_assert(cpp_core::reflection::publicFieldCount() == 2); +static_assert(cpp_core::reflection::hasPubliclyReflectableFields()); +static_assert(cpp_core::reflection::publicFieldCount() == 16); +static_assert(cpp_core::reflection::publicFieldName() == "major"); static_assert(cpp_core::reflection::public_field_name_v == "message"); static_assert(cpp_core::reflection::public_field_count_v == 2); static_assert(!cpp_core::reflection::hasPubliclyReflectableFields()); diff --git a/include/cpp_core/serial.h b/include/cpp_core/serial.h index 6218a2e..b5949a2 100644 --- a/include/cpp_core/serial.h +++ b/include/cpp_core/serial.h @@ -1,50 +1,50 @@ #pragma once +#include "serial_config.hpp" + // Aggregated interface headers -#include "interface/get_version.h" +#include "interface/meta.h" #include "interface/serial_abort_read.h" #include "interface/serial_abort_write.h" #include "interface/serial_clear_buffer_in.h" #include "interface/serial_clear_buffer_out.h" #include "interface/serial_close.h" -#include "interface/serial_drain.h" +#include "interface/serial_wait_for_drain.h" #include "interface/serial_in_bytes_total.h" #include "interface/serial_in_bytes_waiting.h" #include "interface/serial_list_ports.h" -#include "interface/serial_monitor_ports.h" #include "interface/serial_open.h" #include "interface/serial_out_bytes_total.h" #include "interface/serial_out_bytes_waiting.h" #include "interface/serial_read.h" -#include "interface/serial_read_line.h" -#include "interface/serial_read_until.h" #include "interface/serial_read_until_sequence.h" #include "interface/serial_set_error_callback.h" +#include "interface/serial_set_event_callback.h" #include "interface/serial_set_read_callback.h" #include "interface/serial_set_write_callback.h" #include "interface/serial_write.h" // Modem line control -#include "interface/serial_set_dtr.h" -#include "interface/serial_set_rts.h" #include "interface/serial_get_cts.h" -#include "interface/serial_get_dsr.h" #include "interface/serial_get_dcd.h" +#include "interface/serial_get_dsr.h" #include "interface/serial_get_ri.h" +#include "interface/serial_set_dtr.h" +#include "interface/serial_set_rts.h" // Line-setting getters #include "interface/serial_get_baudrate.h" #include "interface/serial_get_data_bits.h" +#include "interface/serial_get_flow_control.h" #include "interface/serial_get_parity.h" #include "interface/serial_get_stop_bits.h" -#include "interface/serial_get_flow_control.h" // Line-setting setters #include "interface/serial_set_baudrate.h" #include "interface/serial_set_data_bits.h" +#include "interface/serial_set_flow_control.h" #include "interface/serial_set_parity.h" #include "interface/serial_set_stop_bits.h" -#include "interface/serial_set_flow_control.h" // Extended control #include "interface/serial_send_break.h" diff --git a/include/cpp_core/serial_config.hpp b/include/cpp_core/serial_config.hpp index b695c9c..16a83ac 100644 --- a/include/cpp_core/serial_config.hpp +++ b/include/cpp_core/serial_config.hpp @@ -3,10 +3,12 @@ #include "result.hpp" #include "strong_types.hpp" -#include #include -#include +#include +#include +#include #include +#include namespace cpp_core { @@ -21,9 +23,9 @@ constexpr auto validateBaudrate(int baud) -> bool return baud >= 300; } -constexpr auto validateDataBits(int bits) -> bool +constexpr auto validateDataBits(DataBits bits) -> bool { - return bits >= 5 && bits <= 8; + return bits == DataBits::kFive || bits == DataBits::kSix || bits == DataBits::kSeven || bits == DataBits::kEight; } constexpr auto validateParity(Parity parity) -> bool @@ -36,48 +38,68 @@ constexpr auto validateStopBits(StopBits stop_bits) -> bool return stop_bits == StopBits::kOne || stop_bits == StopBits::kTwo; } +constexpr auto validateFlowControl(FlowControl flow_mode) -> bool +{ + return flow_mode == FlowControl::kNone || flow_mode == FlowControl::kRtsCts || flow_mode == FlowControl::kXonXoff; +} + +constexpr auto validateTimeout(int timeout_ms, int multiplier) -> bool +{ + return timeout_ms >= 0 && multiplier >= 0 && + (timeout_ms == 0 || multiplier <= std::numeric_limits::max() / timeout_ms); +} + } // namespace detail /** * Compile-time validated serial configuration. * Invalid configs are rejected at compile time - no runtime overhead. - * constexpr auto kCfg = SerialConfig::make<9600, 8, Parity::kNone, StopBits::kOne>(); + * constexpr auto kCfg = + * SerialConfig::make<9600, DataBits::kEight, Parity::kNone, StopBits::kOne, FlowControl::kNone>(); */ struct SerialConfig { - int baudrate; - int data_bits; - Parity parity; - StopBits stop_bits; + int baudrate; ///< Baud rate in bit/s (>= 300). + DataBits data_bits; ///< Number of data bits. + Parity parity; ///< Parity mode. + StopBits stop_bits; ///< Stop-bit mode. + FlowControl flow_mode; ///< None, hardware RTS/CTS, or software XON/XOFF flow control. - template + template static consteval auto make() -> SerialConfig { static_assert(detail::validateBaudrate(Baud), "Baudrate must be >= 300"); - static_assert(detail::validateDataBits(DataBitsVal), "DataBits must be 5-8"); + static_assert(detail::validateDataBits(D), "DataBits must be 5-8"); + static_assert(detail::validateParity(P), "Parity must be none, even, or odd"); + static_assert(detail::validateStopBits(S), "StopBits must be one or two"); + static_assert(detail::validateFlowControl(F), "FlowControl must be none, RTS/CTS, or XON/XOFF"); return SerialConfig{ .baudrate = Baud, - .data_bits = DataBitsVal, + .data_bits = D, .parity = P, .stop_bits = S, + .flow_mode = F, }; } [[nodiscard]] static constexpr auto tryMake(Baudrate baud, DataBits data_bits, Parity parity = Parity::kNone, - StopBits stop_bits = StopBits::kOne) -> Result + StopBits stop_bits = StopBits::kOne, + FlowControl flow_mode = FlowControl::kNone) -> Result { - return tryMake(baud.get(), data_bits.get(), parity, stop_bits); + return tryMake(baud.get(), data_bits, parity, stop_bits, flow_mode); } - [[nodiscard]] static constexpr auto tryMake(int baud, int data_bits_val, Parity parity = Parity::kNone, - StopBits stop_bits = StopBits::kOne) -> Result + [[nodiscard]] static constexpr auto tryMake(int baud, DataBits data_bits, Parity parity = Parity::kNone, + StopBits stop_bits = StopBits::kOne, + FlowControl flow_mode = FlowControl::kNone) -> Result { if (!detail::validateBaudrate(baud)) { return fail(StatusCode::Configuration::kSetBaudrateError); } - if (!detail::validateDataBits(data_bits_val)) + if (!detail::validateDataBits(data_bits)) { return fail(StatusCode::Configuration::kSetDataBitsError); } @@ -89,18 +111,24 @@ struct SerialConfig { return fail(StatusCode::Configuration::kSetStopBitsError); } + if (!detail::validateFlowControl(flow_mode)) + { + return fail(StatusCode::Configuration::kSetFlowControlError); + } return ok(SerialConfig{ .baudrate = baud, - .data_bits = data_bits_val, + .data_bits = data_bits, .parity = parity, .stop_bits = stop_bits, + .flow_mode = flow_mode, }); } [[nodiscard]] constexpr auto isValid() const noexcept -> bool { - return detail::validateBaudrate(baudrate) && detail::validateDataBits(data_bits) - && detail::validateParity(parity) && detail::validateStopBits(stop_bits); + return detail::validateBaudrate(baudrate) && detail::validateDataBits(data_bits) && + detail::validateParity(parity) && detail::validateStopBits(stop_bits) && + detail::validateFlowControl(flow_mode); } [[nodiscard]] constexpr auto baudrateValue() const noexcept -> Baudrate @@ -110,7 +138,7 @@ struct SerialConfig [[nodiscard]] constexpr auto dataBitsValue() const noexcept -> DataBits { - return DataBits{data_bits}; + return data_bits; } [[nodiscard]] constexpr auto parityInt() const noexcept -> int @@ -123,19 +151,95 @@ struct SerialConfig return toInt(stop_bits); } + [[nodiscard]] constexpr auto flowModeInt() const noexcept -> int + { + return toInt(flow_mode); + } + [[nodiscard]] constexpr auto withBaudrate(Baudrate baud) const -> Result { - return tryMake(baud, dataBitsValue(), parity, stop_bits); + return tryMake(baud, dataBitsValue(), parity, stop_bits, flow_mode); } [[nodiscard]] constexpr auto withDataBits(DataBits bits) const -> Result { - return tryMake(baudrateValue(), bits, parity, stop_bits); + return tryMake(baudrateValue(), bits, parity, stop_bits, flow_mode); + } + + [[nodiscard]] constexpr auto withParity(Parity new_parity) const -> Result + { + return tryMake(baudrateValue(), dataBitsValue(), new_parity, stop_bits, flow_mode); + } + + [[nodiscard]] constexpr auto withStopBits(StopBits new_stop_bits) const -> Result + { + return tryMake(baudrateValue(), dataBitsValue(), parity, new_stop_bits, flow_mode); + } + + [[nodiscard]] constexpr auto withFlowMode(FlowControl new_flow_mode) const -> Result + { + return tryMake(baudrateValue(), dataBitsValue(), parity, stop_bits, new_flow_mode); } [[nodiscard]] constexpr auto operator<=>(const SerialConfig &) const noexcept = default; }; +/** + * Timeout configuration shared by serial read and write operations. + * The base timeout applies to the first byte; subsequent bytes use + * `timeout_ms * multiplier`. + */ +struct SerialTimeoutConfig +{ + int timeout_ms; ///< Base timeout per byte in milliseconds. + int multiplier; ///< Factor applied to the timeout after the first byte. + + template static consteval auto make() -> SerialTimeoutConfig + { + static_assert(detail::validateTimeout(TimeoutMsVal, MultiplierVal), + "Timeout and multiplier must be non-negative and their product must fit into int"); + return SerialTimeoutConfig{ + .timeout_ms = TimeoutMsVal, + .multiplier = MultiplierVal, + }; + } + + [[nodiscard]] static constexpr auto tryMake(TimeoutMs timeout, Multiplier timeout_multiplier) + -> Result + { + return tryMake(timeout.get(), timeout_multiplier.get()); + } + + [[nodiscard]] static constexpr auto tryMake(int timeout, int timeout_multiplier) -> Result + { + if (!detail::validateTimeout(timeout, timeout_multiplier)) + { + return fail(StatusCode::Configuration::kSetTimeoutError); + } + return ok(SerialTimeoutConfig{ + .timeout_ms = timeout, + .multiplier = timeout_multiplier, + }); + } + + [[nodiscard]] constexpr auto isValid() const noexcept -> bool + { + return detail::validateTimeout(timeout_ms, multiplier); + } + + [[nodiscard]] constexpr auto timeoutValue() const noexcept -> TimeoutMs + { + return TimeoutMs{timeout_ms}; + } + + [[nodiscard]] constexpr auto multiplierValue() const noexcept -> Multiplier + { + return Multiplier{multiplier}; + } + + [[nodiscard]] constexpr auto operator<=>(const SerialTimeoutConfig &) const noexcept = default; +}; + // Concepts for serial port operations // clang-format off @@ -148,14 +252,14 @@ concept NativeHandle = (std::is_integral_v || std::is_pointer_v) // A type that can serve as a mutable byte buffer for read operations. template concept ByteBuffer = requires(B buf) { - { buf.data() } -> std::convertible_to; + { buf.data() } -> std::convertible_to; { buf.size() } -> std::convertible_to; }; // Read-only byte source for write operations. template concept ConstByteBuffer = requires(const B buf) { - { buf.data() } -> std::convertible_to; + { buf.data() } -> std::convertible_to; { buf.size() } -> std::convertible_to; }; diff --git a/include/cpp_core/serial_config.test.cpp b/include/cpp_core/serial_config.test.cpp index fe86b4d..d3eab65 100644 --- a/include/cpp_core/serial_config.test.cpp +++ b/include/cpp_core/serial_config.test.cpp @@ -1,45 +1,74 @@ -#include "cpp_core/reflection.hpp" #include "cpp_core/serial_config.hpp" +#include "cpp_core/reflection.hpp" namespace cpp_core::tests::serial_config { -constexpr auto kCompileTimeConfig = SerialConfig::make<115'200, 8, Parity::kEven, StopBits::kTwo>(); +constexpr auto kCompileTimeConfig = + SerialConfig::make<115'200, DataBits::kEight, Parity::kEven, StopBits::kTwo, FlowControl::kRtsCts>(); static_assert(kCompileTimeConfig.isValid()); static_assert(kCompileTimeConfig.baudrateValue() == Baudrate{115'200}); -static_assert(kCompileTimeConfig.dataBitsValue() == DataBits{8}); +static_assert(kCompileTimeConfig.dataBitsValue() == DataBits::kEight); static_assert(kCompileTimeConfig.parityInt() == 1); static_assert(kCompileTimeConfig.stopBitsInt() == 2); +static_assert(kCompileTimeConfig.flowModeInt() == 1); -constexpr auto kRuntimeLikeConfig = SerialConfig::tryMake(Baudrate{57'600}, DataBits{7}, Parity::kOdd, StopBits::kOne); +constexpr auto kRuntimeLikeConfig = + SerialConfig::tryMake(Baudrate{57'600}, DataBits::kSeven, Parity::kOdd, StopBits::kOne, FlowControl::kXonXoff); static_assert(kRuntimeLikeConfig.has_value()); static_assert(kRuntimeLikeConfig->baudrateValue() == Baudrate{57'600}); -static_assert(kRuntimeLikeConfig->dataBitsValue() == DataBits{7}); +static_assert(kRuntimeLikeConfig->dataBitsValue() == DataBits::kSeven); +static_assert(kRuntimeLikeConfig->flow_mode == FlowControl::kXonXoff); consteval auto rejectsBadBaudrate() -> bool { - return !SerialConfig::tryMake(Baudrate{299}, DataBits{8}).has_value(); + return !SerialConfig::tryMake(Baudrate{299}, DataBits::kEight).has_value(); } consteval auto rejectsBadDataBits() -> bool { - return !SerialConfig::tryMake(9'600, 4).has_value(); + return !SerialConfig::tryMake(9'600, static_cast(4)).has_value(); } consteval auto rejectsBadParity() -> bool { - return !SerialConfig::tryMake(9'600, 8, static_cast(77)).has_value(); + return !SerialConfig::tryMake(9'600, DataBits::kEight, static_cast(77)).has_value(); +} + +consteval auto rejectsBadFlowMode() -> bool +{ + return !SerialConfig::tryMake(9'600, DataBits::kEight, Parity::kNone, StopBits::kOne, static_cast(77)) + .has_value(); } static_assert(rejectsBadBaudrate()); static_assert(rejectsBadDataBits()); static_assert(rejectsBadParity()); +static_assert(rejectsBadFlowMode()); constexpr auto kRetunedConfig = kCompileTimeConfig.withBaudrate(Baudrate{230'400}); static_assert(kRetunedConfig.has_value()); static_assert(kRetunedConfig->baudrateValue() == Baudrate{230'400}); +constexpr auto kFlowModeConfig = kCompileTimeConfig.withFlowMode(FlowControl::kXonXoff); +static_assert(kFlowModeConfig.has_value()); +static_assert(kFlowModeConfig->flow_mode == FlowControl::kXonXoff); + +constexpr auto kTimeoutConfig = SerialTimeoutConfig::make<50, 2>(); +static_assert(kTimeoutConfig.isValid()); +static_assert(kTimeoutConfig.timeoutValue() == TimeoutMs{50}); +static_assert(kTimeoutConfig.multiplierValue() == Multiplier{2}); + +constexpr auto kRuntimeTimeoutConfig = SerialTimeoutConfig::tryMake(TimeoutMs{100}, Multiplier{3}); +static_assert(kRuntimeTimeoutConfig.has_value()); +static_assert(kRuntimeTimeoutConfig->timeout_ms == 100); + +static_assert(!SerialTimeoutConfig::tryMake(-1, 1).has_value()); +static_assert(!SerialTimeoutConfig::tryMake(1'000'000'000, 3).has_value()); + static_assert(cpp_core::reflection::publicFieldName() == "baudrate"); static_assert(cpp_core::reflection::publicFieldName() == "data_bits"); +static_assert(cpp_core::reflection::publicFieldName() == "flow_mode"); +static_assert(cpp_core::reflection::publicFieldName() == "timeout_ms"); } // namespace cpp_core::tests::serial_config diff --git a/include/cpp_core/serial_interface.test.cpp b/include/cpp_core/serial_interface.test.cpp new file mode 100644 index 0000000..53d2e49 --- /dev/null +++ b/include/cpp_core/serial_interface.test.cpp @@ -0,0 +1,73 @@ +#include "cpp_core/serial.h" +#include "cpp_core/serial_config.hpp" +#include "cpp_core/validation.hpp" + +#include +#include +#include + +namespace cpp_core::tests::serial_interface +{ + +using MetaFn = void (*)(Meta *); +using WaitForDrainFn = int (*)(int64_t, ErrorCallbackT); +using OpenFn = intptr_t (*)(const char *, const SerialConfig *, ErrorCallbackT); +using ReadFn = int (*)(int64_t, std::uint8_t *, int, const SerialTimeoutConfig *, ErrorCallbackT); +using ReadUntilSequenceFn = int (*)(int64_t, std::uint8_t *, int, const SerialTimeoutConfig *, const std::uint8_t *, + int, ErrorCallbackT); +using WriteFn = int (*)(int64_t, const std::uint8_t *, int, const SerialTimeoutConfig *, ErrorCallbackT); +using GetDataBitsFn = DataBits (*)(int64_t, ErrorCallbackT); +using GetParityFn = Parity (*)(int64_t, ErrorCallbackT); +using GetStopBitsFn = StopBits (*)(int64_t, ErrorCallbackT); +using GetFlowControlFn = FlowControl (*)(int64_t, ErrorCallbackT); +using SetDataBitsFn = int (*)(int64_t, DataBits, ErrorCallbackT); +using SetParityFn = int (*)(int64_t, Parity, ErrorCallbackT); +using SetStopBitsFn = int (*)(int64_t, StopBits, ErrorCallbackT); +using SetFlowControlFn = int (*)(int64_t, FlowControl, ErrorCallbackT); +using EventCallback = void (*)(PortEvent, const char *); +using SetEventCallbackFn = int (*)(EventCallback, ErrorCallbackT); + +static_assert(std::is_same_v); +static_assert(std::is_same_v); +static_assert(std::is_same_v); +static_assert(std::is_same_v); +static_assert(std::is_same_v); +static_assert(std::is_same_v); +static_assert(std::is_same_v); +static_assert(std::is_same_v); +static_assert(std::is_same_v); +static_assert(std::is_same_v); +static_assert(std::is_same_v); +static_assert(std::is_same_v); +static_assert(std::is_same_v); +static_assert(std::is_same_v); +static_assert(std::is_same_v); + +static_assert(std::is_standard_layout_v); +static_assert(std::is_trivially_copyable_v); +constexpr Meta kMeta{}; +static_assert(kMeta.commits_since_tag >= 0); +static_assert(kMeta.is_dirty == 0 || kMeta.is_dirty == 1); +static_assert(std::is_standard_layout_v); +static_assert(std::is_trivially_copyable_v); +static_assert(sizeof(SerialConfig) == 5 * sizeof(int)); +static_assert(std::is_standard_layout_v); +static_assert(std::is_trivially_copyable_v); +static_assert(sizeof(SerialTimeoutConfig) == 2 * sizeof(int)); +static_assert(ByteBuffer>); +static_assert(ConstByteBuffer>); +static_assert(!ByteBuffer>); +static_assert(!ConstByteBuffer>); + +inline constexpr char kPort[] = "/dev/ttyUSB0"; +constexpr auto kSerialConfig = SerialConfig::make<115'200, DataBits::kEight>(); +constexpr auto kTimeoutConfig = SerialTimeoutConfig::make<50, 1>(); +inline constexpr std::uint8_t kByte{}; + +static_assert(validateOpenParams(kPort, &kSerialConfig, nullptr) == StatusCode::kSuccess); +static_assert(validateOpenParams(kPort, nullptr, nullptr) == StatusCode::Control::kSetStateError); +static_assert(validateTimeoutConfig(&kTimeoutConfig, nullptr) == StatusCode::kSuccess); +static_assert(validateTimeoutConfig(nullptr, nullptr) == StatusCode::Configuration::kSetTimeoutError); +static_assert(validateBuffer(&kByte, 1, nullptr) == StatusCode::kSuccess); + +} // namespace cpp_core::tests::serial_interface diff --git a/include/cpp_core/strong_types.hpp b/include/cpp_core/strong_types.hpp index f2f2b81..768dc9a 100644 --- a/include/cpp_core/strong_types.hpp +++ b/include/cpp_core/strong_types.hpp @@ -61,9 +61,6 @@ template struct StrongInt struct BaudrateTag { }; -struct DataBitsTag -{ -}; struct TimeoutMsTag { }; @@ -72,30 +69,56 @@ struct MultiplierTag }; using Baudrate = StrongInt; -using DataBits = StrongInt; using TimeoutMs = StrongInt; using Multiplier = StrongInt; -// Parity & StopBits enums +/** + * Number of data bits contained in each serial frame. + */ +enum class DataBits : int +{ + kFive = 5, ///< Use five data bits. + kSix = 6, ///< Use six data bits. + kSeven = 7, ///< Use seven data bits. + kEight = 8, ///< Use eight data bits. +}; +/** + * Parity mode used for serial communication. + */ enum class Parity : int { - kNone = 0, - kEven = 1, - kOdd = 2, + kNone = 0, ///< Disable parity checking and generation. + kEven = 1, ///< Use even parity. + kOdd = 2, ///< Use odd parity. }; +/** + * Number of stop bits appended to each serial frame. + */ enum class StopBits : int { - kOne = 0, - kTwo = 2, + kOne = 0, ///< Use one stop bit. + kTwo = 2, ///< Use two stop bits. }; +/** + * Flow-control mode used by the serial port. + */ enum class FlowControl : int { - kNone = 0, - kRtsCts = 1, - kXonXoff = 2, + kNone = 0, ///< Disable flow control. + kRtsCts = 1, ///< Use hardware RTS/CTS flow control. + kXonXoff = 2, ///< Use software XON/XOFF flow control. +}; + +/** + * Port lifecycle event reported by serialSetEventCallback(). + */ +enum class PortEvent : int +{ + kDetached = 0, ///< A serial port was removed from the system. + kAttached = 1, ///< A serial port became available. }; template diff --git a/include/cpp_core/strong_types.test.cpp b/include/cpp_core/strong_types.test.cpp index 0ca321d..119e7e5 100644 --- a/include/cpp_core/strong_types.test.cpp +++ b/include/cpp_core/strong_types.test.cpp @@ -5,9 +5,10 @@ namespace cpp_core::tests::strong_types static_assert(Baudrate{}.get() == 0); static_assert((Baudrate{9'600} + Baudrate{115'200}).get() == 124'800); -static_assert((DataBits{8} - DataBits{3}).get() == 5); +static_assert(toInt(DataBits::kEight) == 8); static_assert(toInt(Parity::kOdd) == 2); static_assert(toInt(StopBits::kTwo) == 2); static_assert(toInt(FlowControl::kXonXoff) == 2); +static_assert(toInt(PortEvent::kAttached) == 1); } // namespace cpp_core::tests::strong_types diff --git a/include/cpp_core/validation.hpp b/include/cpp_core/validation.hpp index 2ff5c80..81f4aad 100644 --- a/include/cpp_core/validation.hpp +++ b/include/cpp_core/validation.hpp @@ -1,6 +1,7 @@ #pragma once #include "error_handling.hpp" +#include "serial_config.hpp" #include "status_code.h" #include @@ -31,7 +32,7 @@ constexpr auto validateHandle(int64_t handle, Callback &&error_callback) -> Ret * Returns kSuccess (0) if all params are valid, or the appropriate negative error code. */ template -constexpr auto validateOpenParams(void *port, int baudrate, int data_bits, Callback &&error_callback) -> Ret +constexpr auto validateOpenParams(const char *port, const SerialConfig *config, Callback &&error_callback) -> Ret { if (port == nullptr) { @@ -39,24 +40,69 @@ constexpr auto validateOpenParams(void *port, int baudrate, int data_bits, Callb static_cast(StatusCode::Connection::kNotFoundError), "Port parameter is nullptr"); } - if (baudrate < 300) + if (config == nullptr) { return failMsg(std::forward(error_callback), static_cast(StatusCode::Control::kSetStateError), + "Config parameter is nullptr"); + } + if (!detail::validateBaudrate(config->baudrate)) + { + return failMsg(std::forward(error_callback), + static_cast(StatusCode::Configuration::kSetBaudrateError), "Invalid baudrate: must be >= 300"); } - if (data_bits < 5 || data_bits > 8) + if (!detail::validateDataBits(config->data_bits)) { return failMsg(std::forward(error_callback), - static_cast(StatusCode::Control::kSetStateError), + static_cast(StatusCode::Configuration::kSetDataBitsError), "Invalid data bits: must be 5-8"); } + if (!detail::validateParity(config->parity)) + { + return failMsg(std::forward(error_callback), + static_cast(StatusCode::Configuration::kSetParityError), + "Invalid parity mode"); + } + if (!detail::validateStopBits(config->stop_bits)) + { + return failMsg(std::forward(error_callback), + static_cast(StatusCode::Configuration::kSetStopBitsError), + "Invalid stop bits mode"); + } + if (!detail::validateFlowControl(config->flow_mode)) + { + return failMsg(std::forward(error_callback), + static_cast(StatusCode::Configuration::kSetFlowControlError), + "Invalid flow-control mode"); + } + return static_cast(StatusCode::kSuccess); +} + +/** + * Shared timeout validation for serial read and write operations. + */ +template +constexpr auto validateTimeoutConfig(const SerialTimeoutConfig *config, Callback &&error_callback) -> Ret +{ + if (config == nullptr) + { + return failMsg(std::forward(error_callback), + static_cast(StatusCode::Configuration::kSetTimeoutError), + "Timeout config parameter is nullptr"); + } + if (!config->isValid()) + { + return failMsg(std::forward(error_callback), + static_cast(StatusCode::Configuration::kSetTimeoutError), + "Timeout and multiplier must be non-negative and their product must fit into int"); + } return static_cast(StatusCode::kSuccess); } // Validate buffer + size for read/write calls. template -constexpr auto validateBuffer(const void *buffer, int buffer_size, Callback &&error_callback) -> Ret +constexpr auto validateBuffer(const std::uint8_t *buffer, int buffer_size, Callback &&error_callback) -> Ret { if (buffer == nullptr || buffer_size <= 0) {