#ifndef MBUS_FFI_H
#define MBUS_FFI_H

#pragma once

/* Auto-generated by cbindgen - DO NOT EDIT */

#include <stdint.h>
#include <stdbool.h>
typedef struct MbusCoils MbusCoils;
typedef struct MbusDiscreteInputs MbusDiscreteInputs;
typedef struct MbusFifoQueue MbusFifoQueue;
typedef struct MbusRegisters MbusRegisters;


/**
 * C-compatible status code returned by every `mbus_*` function.
 *
 * `MBUS_OK` (0) means the request was successfully queued. Actual response data
 * is delivered later via the callbacks in [`MbusCallbacks`].
 */
typedef enum MbusStatusCode {
    /**
     * Success.
     */
    MbusOk = 0,
    /**
     * Frame parse error.
     */
    MbusErrParseError,
    /**
     * Basic parse error.
     */
    MbusErrBasicParseError,
    /**
     * Response timeout.
     */
    MbusErrTimeout,
    /**
     * Modbus exception response.
     */
    MbusErrModbusException,
    /**
     * I/O error.
     */
    MbusErrIoError,
    /**
     * Unexpected error.
     */
    MbusErrUnexpected,
    /**
     * Connection lost.
     */
    MbusErrConnectionLost,
    /**
     * Unsupported function.
     */
    MbusErrUnsupportedFunction,
    /**
     * Reserved sub-function.
     */
    MbusErrReservedSubFunction,
    /**
     * Invalid PDU length.
     */
    MbusErrInvalidPduLength,
    /**
     * Invalid ADU length.
     */
    MbusErrInvalidAduLength,
    /**
     * Connection failed.
     */
    MbusErrConnectionFailed,
    /**
     * Connection closed.
     */
    MbusErrConnectionClosed,
    /**
     * Buffer too small.
     */
    MbusErrBufferTooSmall,
    /**
     * Buffer length mismatch.
     */
    MbusErrBufferLenMismatch,
    /**
     * Send failed.
     */
    MbusErrSendFailed,
    /**
     * Invalid address.
     */
    MbusErrInvalidAddress,
    /**
     * Invalid offset.
     */
    MbusErrInvalidOffset,
    /**
     * Too many in-flight requests.
     */
    MbusErrTooManyRequests,
    /**
     * Invalid function code.
     */
    MbusErrInvalidFunctionCode,
    /**
     * No retries left.
     */
    MbusErrNoRetriesLeft,
    /**
     * Too many file read sub-requests.
     */
    MbusErrTooManyFileReadSubRequests,
    /**
     * File read PDU overflow.
     */
    MbusErrFileReadPduOverflow,
    /**
     * Unexpected response.
     */
    MbusErrUnexpectedResponse,
    /**
     * Invalid transport.
     */
    MbusErrInvalidTransport,
    /**
     * Invalid slave address.
     */
    MbusErrInvalidSlaveAddress,
    /**
     * Checksum error.
     */
    MbusErrChecksumError,
    /**
     * Invalid configuration.
     */
    MbusErrInvalidConfiguration,
    /**
     * Invalid number of expected responses.
     */
    MbusErrInvalidNumOfExpectedRsps,
    /**
     * Invalid data length.
     */
    MbusErrInvalidDataLen,
    /**
     * Invalid quantity.
     */
    MbusErrInvalidQuantity,
    /**
     * Invalid value.
     */
    MbusErrInvalidValue,
    /**
     * Invalid AND mask.
     */
    MbusErrInvalidAndMask,
    /**
     * Invalid OR mask.
     */
    MbusErrInvalidOrMask,
    /**
     * Invalid byte count.
     */
    MbusErrInvalidByteCount,
    /**
     * Invalid device identification.
     */
    MbusErrInvalidDeviceIdentification,
    /**
     * Invalid device ID code.
     */
    MbusErrInvalidDeviceIdCode,
    /**
     * Invalid MEI type.
     */
    MbusErrInvalidMeiType,
    /**
     * Invalid broadcast address.
     */
    MbusErrInvalidBroadcastAddress,
    /**
     * Broadcast not allowed.
     */
    MbusErrBroadcastNotAllowed,
    /**
     * Null pointer passed.
     */
    MbusErrNullPointer,
    /**
     * Invalid UTF-8 string.
     */
    MbusErrInvalidUtf8,
    /**
     * Invalid client ID (freed or never allocated).
     */
    MbusErrInvalidClientId,
    /**
     * Client pool is full — no free slots.
     */
    MbusErrPoolFull,
    /**
     * Client type mismatch (e.g. TCP call on serial ID).
     */
    MbusErrClientTypeMismatch,
    /**
     * Client is busy (indicates an illegal re-entrant call from a callback).
     */
    MbusErrBusy,
} MbusStatusCode;

/**
 * Serial framing mode.
 */
typedef enum MbusSerialMode {
    /**
     * Modbus RTU binary framing with CRC-16.
     */
    MbusSerialRtu = 0,
    /**
     * Modbus ASCII framing with LRC.
     */
    MbusSerialAscii,
} MbusSerialMode;

/**
 * Backoff strategy selector for retry logic.
 */
typedef enum MbusBackoffStrategy {
    /**
     * Retry immediately with no delay.
     */
    MbusBackoffImmediate = 0,
    /**
     * Retry after a fixed delay (`backoff_base_delay_ms`).
     */
    MbusBackoffFixed,
    /**
     * Retry with exponentially increasing delay, capped at `backoff_max_delay_ms`.
     */
    MbusBackoffExponential,
    /**
     * Retry with linearly increasing delay, capped at `backoff_max_delay_ms`.
     */
    MbusBackoffLinear,
} MbusBackoffStrategy;

/**
 * Client ID type: an opaque `u8` index into one of the two sub-pools.
 * Use `MBUS_INVALID_CLIENT_ID` (0xFF) as the sentinel "no client" value.
 */
typedef uint8_t MbusClientId;

/**
 * Configuration for a Modbus Serial (RTU or ASCII) client.
 *
 * `port_name` must remain valid for the duration of `mbus_serial_client_new`.
 */
typedef struct MbusSerialConfig {
    /**
     * Null-terminated serial port path (e.g. `"/dev/ttyUSB0"` or `"COM3"`).
     */
    const char *port_name;
    /**
     * Baud rate (e.g. 9600, 19200, 115200).
     */
    uint32_t baud_rate;
    /**
     * Framing mode: RTU or ASCII.
     */
    enum MbusSerialMode mode;
    /**
     * Timeout waiting for a Modbus response, in milliseconds.
     */
    uint32_t response_timeout_ms;
    /**
     * Number of retry attempts.
     */
    uint8_t retries;
    /**
     * Backoff strategy between retries.
     */
    enum MbusBackoffStrategy backoff_strategy;
    /**
     * Base delay (ms).
     */
    uint32_t backoff_base_delay_ms;
    /**
     * Maximum delay cap (ms).
     */
    uint32_t backoff_max_delay_ms;
    /**
     * Jitter percentage (0–100).
     */
    uint8_t jitter_percent;
} MbusSerialConfig;

/**
 * C-provided transport callbacks.
 *
 * All five callbacks must be non-`NULL` for a valid transport.
 * `userdata` is threaded through every callback invocation.
 */
typedef struct MbusTransportCallbacks {
    /**
     * Opaque user context threaded to every callback.
     */
    void *userdata;
    /**
     * Called to open/connect the transport.
     */
    enum MbusStatusCode (*on_connect)(void *userdata);
    /**
     * Called to close/disconnect the transport.
     */
    enum MbusStatusCode (*on_disconnect)(void *userdata);
    /**
     * Called to send a frame.
     */
    enum MbusStatusCode (*on_send)(const uint8_t *data, uint16_t len, void *userdata);
    /**
     * Called to receive a frame.
     */
    enum MbusStatusCode (*on_recv)(uint8_t *buffer,
                                   uint16_t buffer_cap,
                                   uint16_t *out_len,
                                   void *userdata);
    /**
     * Called to query connection state.
     */
    uint8_t (*on_is_connected)(void *userdata);
} MbusTransportCallbacks;

/**
 * Context passed to the read-coils callback.
 */
typedef struct MbusReadCoilsCtx {
    /**
     * Transaction ID.
     */
    uint16_t txn_id;
    /**
     * Unit / slave ID.
     */
    uint8_t unit_id;
    /**
     * Opaque pointer to the coils data (valid during this callback only).
     */
    const MbusCoils *coils;
    /**
     * User-provided opaque pointer.
     */
    void *userdata;
} MbusReadCoilsCtx;

/**
 * Context passed to the write-single-coil callback.
 */
typedef struct MbusWriteSingleCoilCtx {
    /**
     * Transaction ID.
     */
    uint16_t txn_id;
    /**
     * Unit / slave ID.
     */
    uint8_t unit_id;
    /**
     * Coil address.
     */
    uint16_t address;
    /**
     * Written value (1 = ON, 0 = OFF).
     */
    uint8_t value;
    /**
     * User-provided opaque pointer.
     */
    void *userdata;
} MbusWriteSingleCoilCtx;

/**
 * Context passed to the write-multiple-coils callback.
 */
typedef struct MbusWriteMultipleCoilsCtx {
    /**
     * Transaction ID.
     */
    uint16_t txn_id;
    /**
     * Unit / slave ID.
     */
    uint8_t unit_id;
    /**
     * Starting coil address.
     */
    uint16_t address;
    /**
     * Number of coils written.
     */
    uint16_t quantity;
    /**
     * User-provided opaque pointer.
     */
    void *userdata;
} MbusWriteMultipleCoilsCtx;

/**
 * Context passed to the read-holding-registers callback.
 */
typedef struct MbusReadHoldingRegistersCtx {
    /**
     * Transaction ID.
     */
    uint16_t txn_id;
    /**
     * Unit / slave ID.
     */
    uint8_t unit_id;
    /**
     * Opaque pointer to registers data.
     */
    const MbusRegisters *registers;
    /**
     * User-provided opaque pointer.
     */
    void *userdata;
} MbusReadHoldingRegistersCtx;

/**
 * Context passed to the read-input-registers callback.
 */
typedef struct MbusReadInputRegistersCtx {
    /**
     * Transaction ID.
     */
    uint16_t txn_id;
    /**
     * Unit / slave ID.
     */
    uint8_t unit_id;
    /**
     * Opaque pointer to registers data.
     */
    const MbusRegisters *registers;
    /**
     * User-provided opaque pointer.
     */
    void *userdata;
} MbusReadInputRegistersCtx;

/**
 * Context passed to the read-write-multiple-registers callback.
 */
typedef struct MbusReadWriteMultipleRegistersCtx {
    /**
     * Transaction ID.
     */
    uint16_t txn_id;
    /**
     * Unit / slave ID.
     */
    uint8_t unit_id;
    /**
     * Opaque pointer to registers data.
     */
    const MbusRegisters *registers;
    /**
     * User-provided opaque pointer.
     */
    void *userdata;
} MbusReadWriteMultipleRegistersCtx;

/**
 * Context passed to the write-single-register callback.
 */
typedef struct MbusWriteSingleRegisterCtx {
    /**
     * Transaction ID.
     */
    uint16_t txn_id;
    /**
     * Unit / slave ID.
     */
    uint8_t unit_id;
    /**
     * Register address.
     */
    uint16_t address;
    /**
     * Written value.
     */
    uint16_t value;
    /**
     * User-provided opaque pointer.
     */
    void *userdata;
} MbusWriteSingleRegisterCtx;

/**
 * Context passed to the write-multiple-registers callback.
 */
typedef struct MbusWriteMultipleRegistersCtx {
    /**
     * Transaction ID.
     */
    uint16_t txn_id;
    /**
     * Unit / slave ID.
     */
    uint8_t unit_id;
    /**
     * Starting register address.
     */
    uint16_t address;
    /**
     * Number of registers written.
     */
    uint16_t quantity;
    /**
     * User-provided opaque pointer.
     */
    void *userdata;
} MbusWriteMultipleRegistersCtx;

/**
 * Context passed to the mask-write-register callback.
 */
typedef struct MbusMaskWriteRegisterCtx {
    /**
     * Transaction ID.
     */
    uint16_t txn_id;
    /**
     * Unit / slave ID.
     */
    uint8_t unit_id;
    /**
     * User-provided opaque pointer.
     */
    void *userdata;
} MbusMaskWriteRegisterCtx;

/**
 * Context passed to the read-discrete-inputs callback.
 */
typedef struct MbusReadDiscreteInputsCtx {
    /**
     * Transaction ID.
     */
    uint16_t txn_id;
    /**
     * Unit / slave ID.
     */
    uint8_t unit_id;
    /**
     * Opaque pointer to discrete inputs data.
     */
    const MbusDiscreteInputs *discrete_inputs;
    /**
     * User-provided opaque pointer.
     */
    void *userdata;
} MbusReadDiscreteInputsCtx;

/**
 * Context passed to the read-fifo-queue callback.
 */
typedef struct MbusReadFifoQueueCtx {
    /**
     * Transaction ID.
     */
    uint16_t txn_id;
    /**
     * Unit / slave ID.
     */
    uint8_t unit_id;
    /**
     * Opaque pointer to FIFO queue data.
     */
    const MbusFifoQueue *fifo_queue;
    /**
     * User-provided opaque pointer.
     */
    void *userdata;
} MbusReadFifoQueueCtx;

/**
 * A single sub-request result returned in the `on_read_file_record` callback.
 *
 * `data` points into Rust-owned memory and is **valid only during the callback
 * invocation**. Copy the slice if you need to retain it.
 */
typedef struct MbusFileRecordResult {
    /**
     * Record number for this sub-request.
     */
    uint16_t record_number;
    /**
     * Pointer to the register data for this sub-request chunk.
     */
    const uint16_t *data;
    /**
     * Number of u16 words in `data`.
     */
    uint16_t data_len;
} MbusFileRecordResult;

/**
 * Context passed to the read-file-record callback.
 */
typedef struct MbusReadFileRecordCtx {
    /**
     * Transaction ID.
     */
    uint16_t txn_id;
    /**
     * Unit / slave ID.
     */
    uint8_t unit_id;
    /**
     * Pointer to array of sub-request results.
     */
    const struct MbusFileRecordResult *results;
    /**
     * Number of sub-request entries in `results`.
     */
    uint16_t count;
    /**
     * User-provided opaque pointer.
     */
    void *userdata;
} MbusReadFileRecordCtx;

/**
 * Context passed to the write-file-record callback.
 */
typedef struct MbusWriteFileRecordCtx {
    /**
     * Transaction ID.
     */
    uint16_t txn_id;
    /**
     * Unit / slave ID.
     */
    uint8_t unit_id;
    /**
     * User-provided opaque pointer.
     */
    void *userdata;
} MbusWriteFileRecordCtx;

/**
 * Context passed to the read-exception-status callback.
 */
typedef struct MbusReadExceptionStatusCtx {
    /**
     * Transaction ID.
     */
    uint16_t txn_id;
    /**
     * Unit / slave ID.
     */
    uint8_t unit_id;
    /**
     * Exception status byte.
     */
    uint8_t status;
    /**
     * User-provided opaque pointer.
     */
    void *userdata;
} MbusReadExceptionStatusCtx;

/**
 * Context passed to the diagnostics callback.
 */
typedef struct MbusDiagnosticsCtx {
    /**
     * Transaction ID.
     */
    uint16_t txn_id;
    /**
     * Unit / slave ID.
     */
    uint8_t unit_id;
    /**
     * Diagnostic sub-function code.
     */
    uint16_t sub_fn;
    /**
     * Pointer to diagnostic data words.
     */
    const uint16_t *data;
    /**
     * Number of u16 words in `data`.
     */
    uint16_t data_len;
    /**
     * User-provided opaque pointer.
     */
    void *userdata;
} MbusDiagnosticsCtx;

/**
 * Context passed to the comm-event-counter callback.
 */
typedef struct MbusCommEventCounterCtx {
    /**
     * Transaction ID.
     */
    uint16_t txn_id;
    /**
     * Unit / slave ID.
     */
    uint8_t unit_id;
    /**
     * Status word.
     */
    uint16_t status;
    /**
     * Event count.
     */
    uint16_t event_count;
    /**
     * User-provided opaque pointer.
     */
    void *userdata;
} MbusCommEventCounterCtx;

/**
 * Context passed to the comm-event-log callback.
 */
typedef struct MbusCommEventLogCtx {
    /**
     * Transaction ID.
     */
    uint16_t txn_id;
    /**
     * Unit / slave ID.
     */
    uint8_t unit_id;
    /**
     * Status word.
     */
    uint16_t status;
    /**
     * Event count.
     */
    uint16_t event_count;
    /**
     * Message count.
     */
    uint16_t message_count;
    /**
     * Pointer to event bytes.
     */
    const uint8_t *events;
    /**
     * Number of bytes in `events`.
     */
    uint16_t events_len;
    /**
     * User-provided opaque pointer.
     */
    void *userdata;
} MbusCommEventLogCtx;

/**
 * Context passed to the report-server-id callback.
 */
typedef struct MbusReportServerIdCtx {
    /**
     * Transaction ID.
     */
    uint16_t txn_id;
    /**
     * Unit / slave ID.
     */
    uint8_t unit_id;
    /**
     * Server ID byte.
     */
    uint8_t server_id;
    /**
     * Run indicator (0xFF = ON, 0x00 = OFF).
     */
    uint8_t run_indicator;
    /**
     * Pointer to additional device identifier data.
     */
    const uint8_t *device_identifier;
    /**
     * Length of `device_identifier` data.
     */
    uint16_t identifier_len;
    /**
     * User-provided opaque pointer.
     */
    void *userdata;
} MbusReportServerIdCtx;

/**
 * Context passed to the read-device-id callback.
 */
typedef struct MbusReadDeviceIdCtx {
    /**
     * Transaction ID.
     */
    uint16_t txn_id;
    /**
     * Unit / slave ID.
     */
    uint8_t unit_id;
    /**
     * Read Device ID code that was requested.
     */
    uint8_t read_device_id_code;
    /**
     * Conformity level reported by the device.
     */
    uint8_t conformity_level;
    /**
     * 1 if more objects follow, 0 if this is the last.
     */
    uint8_t more_follows;
    /**
     * Pointer to raw MEI response bytes.
     */
    const uint8_t *objects;
    /**
     * Number of bytes in `objects`.
     */
    uint16_t objects_len;
    /**
     * User-provided opaque pointer.
     */
    void *userdata;
} MbusReadDeviceIdCtx;

/**
 * Context passed to the request-failed callback.
 */
typedef struct MbusRequestFailedCtx {
    /**
     * Transaction ID of the failed request.
     */
    uint16_t txn_id;
    /**
     * Unit / slave ID the request was targeting.
     */
    uint8_t unit_id;
    /**
     * Error code describing the failure.
     */
    enum MbusStatusCode error;
    /**
     * User-provided opaque pointer.
     */
    void *userdata;
} MbusRequestFailedCtx;

/**
 * Table of C function-pointer callbacks.
 *
 * Set a field to `NULL` to ignore that response type. All non-null callbacks
 * will be invoked synchronously from within `mbus_tcp_poll()` /
 * `mbus_serial_poll()`.
 *
 * `userdata` is passed verbatim to every callback invocation; use it to
 * thread your own application context through.
 */
typedef struct MbusCallbacks {
    /**
     * Opaque application context pointer threaded through every callback.
     */
    void *userdata;
    /**
     * Required callback used for all timekeeping in the client state machine.
     *
     * If this callback is NULL, client construction fails.
     */
    uint64_t (*on_current_millis)(void *userdata);
    /**
     * Called when a Read Coils response is received.
     */
    void (*on_read_coils)(const struct MbusReadCoilsCtx *ctx);
    /**
     * Called when a Write Single Coil response is received.
     */
    void (*on_write_single_coil)(const struct MbusWriteSingleCoilCtx *ctx);
    /**
     * Called when a Write Multiple Coils response is received.
     */
    void (*on_write_multiple_coils)(const struct MbusWriteMultipleCoilsCtx *ctx);
    /**
     * Called when a Read Holding Registers response is received.
     */
    void (*on_read_holding_registers)(const struct MbusReadHoldingRegistersCtx *ctx);
    /**
     * Called when a Read Input Registers response is received.
     */
    void (*on_read_input_registers)(const struct MbusReadInputRegistersCtx *ctx);
    /**
     * Called when a Read/Write Multiple Registers response is received.
     */
    void (*on_read_write_multiple_registers)(const struct MbusReadWriteMultipleRegistersCtx *ctx);
    /**
     * Called when a Write Single Register response is received.
     */
    void (*on_write_single_register)(const struct MbusWriteSingleRegisterCtx *ctx);
    /**
     * Called when a Write Multiple Registers response is received.
     */
    void (*on_write_multiple_registers)(const struct MbusWriteMultipleRegistersCtx *ctx);
    /**
     * Called when a Mask Write Register response is received.
     */
    void (*on_mask_write_register)(const struct MbusMaskWriteRegisterCtx *ctx);
    /**
     * Called when a Read Discrete Inputs response is received.
     */
    void (*on_read_discrete_inputs)(const struct MbusReadDiscreteInputsCtx *ctx);
    /**
     * Called when a Read FIFO Queue response is received.
     */
    void (*on_read_fifo_queue)(const struct MbusReadFifoQueueCtx *ctx);
    /**
     * Called when a Read File Record response is received.
     */
    void (*on_read_file_record)(const struct MbusReadFileRecordCtx *ctx);
    /**
     * Called when a Write File Record response is received.
     */
    void (*on_write_file_record)(const struct MbusWriteFileRecordCtx *ctx);
    /**
     * Called when a Read Exception Status response is received.
     */
    void (*on_read_exception_status)(const struct MbusReadExceptionStatusCtx *ctx);
    /**
     * Called when a Diagnostics response is received.
     */
    void (*on_diagnostics)(const struct MbusDiagnosticsCtx *ctx);
    /**
     * Called when a Comm Event Counter response is received.
     */
    void (*on_comm_event_counter)(const struct MbusCommEventCounterCtx *ctx);
    /**
     * Called when a Comm Event Log response is received.
     */
    void (*on_comm_event_log)(const struct MbusCommEventLogCtx *ctx);
    /**
     * Called when a Report Server ID response is received.
     */
    void (*on_report_server_id)(const struct MbusReportServerIdCtx *ctx);
    /**
     * Called when a Read Device Identification response is received.
     */
    void (*on_read_device_id)(const struct MbusReadDeviceIdCtx *ctx);
    /**
     * Called when a request fails (timeout, exception, etc.).
     */
    void (*on_request_failed)(const struct MbusRequestFailedCtx *ctx);
} MbusCallbacks;

/**
 * Configuration for a Modbus TCP client.
 *
 * All pointer fields (`host`) must remain valid for the duration of the
 * `mbus_tcp_client_new` call. They are copied internally and do not need to
 * outlive the call.
 */
typedef struct MbusTcpConfig {
    /**
     * Null-terminated hostname or IPv4/IPv6 address string (max 63 bytes excl. NUL).
     */
    const char *host;
    /**
     * TCP port (default Modbus port is 502).
     */
    uint16_t port;
    /**
     * Timeout waiting for the TCP connection to be established, in milliseconds.
     */
    uint32_t connection_timeout_ms;
    /**
     * Timeout waiting for a Modbus response, in milliseconds.
     */
    uint32_t response_timeout_ms;
    /**
     * Number of retry attempts before reporting failure via the error callback.
     */
    uint8_t retries;
    /**
     * Backoff strategy between retries.
     */
    enum MbusBackoffStrategy backoff_strategy;
    /**
     * Base delay (ms) used by `MbusBackoffFixed`, `MbusBackoffExponential`, and
     * `MbusBackoffLinear`.
     */
    uint32_t backoff_base_delay_ms;
    /**
     * Maximum delay cap (ms) used by `MbusBackoffExponential` and `MbusBackoffLinear`.
     */
    uint32_t backoff_max_delay_ms;
    /**
     * Jitter percentage (0 = no jitter, 1–100 = ±N% random spread on top of backoff).
     */
    uint8_t jitter_percent;
} MbusTcpConfig;

/**
 * A single sub-request passed to [`mbus_tcp_read_file_record`] /
 * [`mbus_tcp_write_file_record`] (and their serial equivalents).
 *
 * For **read** operations: populate `file_number`, `record_number`, and
 * `data_len` (number of registers to read); set `data` to NULL.
 *
 * For **write** operations: populate all four fields; `data` must point to
 * at least `data_len` valid `u16` words.
 */
typedef struct MbusSubRequest {
    /**
     * File number (1–65535).
     */
    uint16_t file_number;
    /**
     * Starting record number within the file.
     */
    uint16_t record_number;
    /**
     * Number of 16-bit registers (length). For reads: how many to read.
     * For writes: must equal `data_len`.
     */
    uint16_t record_length;
    /**
     * Pointer to write data (NULL for reads). Valid only during the call.
     */
    const uint16_t *data;
    /**
     * Number of valid words pointed to by `data` (0 for reads).
     */
    uint16_t data_len;
} MbusSubRequest;

/**
 * Sentinel value meaning "no valid client".
 */
#define MBUS_INVALID_CLIENT_ID 255

/**
 * Returns a static null-terminated C string describing the status code.
 *
 * The returned pointer is always valid (points to a static string literal).
 * The caller must NOT free it.
 */
const char *mbus_status_str(enum MbusStatusCode code);

/**
 * Returns the starting address of the coils range.
 */
uint16_t mbus_coils_from_address(const MbusCoils *coils);

/**
 * Returns the number of coils.
 */
uint16_t mbus_coils_quantity(const MbusCoils *coils);

/**
 * Reads a single coil value by address into `out_value`.
 */
enum MbusStatusCode mbus_coils_value(const MbusCoils *coils, uint16_t address, bool *out_value);

/**
 * Reads a single coil value by 0-based index into `out_value`.
 *
 * `index` must be less than [`mbus_coils_quantity`]; otherwise returns
 * `MBUS_ERR_INVALID_ADDRESS`. Unlike [`mbus_coils_value`], no knowledge of
 * the Modbus starting address is required.
 */
enum MbusStatusCode mbus_coils_value_at_index(const MbusCoils *coils,
                                              uint16_t index,
                                              bool *out_value);

/**
 * Returns a raw pointer to the packed coil bit-values. Valid during callback only.
 */
const uint8_t *mbus_coils_values_ptr(const MbusCoils *coils);

/**
 * Returns the starting address of the discrete inputs range.
 */
uint16_t mbus_discrete_inputs_from_address(const MbusDiscreteInputs *discrete_inputs);

/**
 * Returns the number of discrete inputs.
 */
uint16_t mbus_discrete_inputs_quantity(const MbusDiscreteInputs *discrete_inputs);

/**
 * Reads a single discrete input value by address into `out_value`.
 */
enum MbusStatusCode mbus_discrete_inputs_value(const MbusDiscreteInputs *discrete_inputs,
                                               uint16_t address,
                                               bool *out_value);

/**
 * Reads a single discrete input value by 0-based index into `out_value`.
 *
 * `index` must be less than [`mbus_discrete_inputs_quantity`]; otherwise returns
 * `MBUS_ERR_INVALID_ADDRESS`. Unlike [`mbus_discrete_inputs_value`], no knowledge
 * of the Modbus starting address is required.
 */
enum MbusStatusCode mbus_discrete_inputs_value_at_index(const MbusDiscreteInputs *discrete_inputs,
                                                        uint16_t index,
                                                        bool *out_value);

/**
 * Returns a raw pointer to the discrete input bit-values. Valid during callback only.
 */
const uint8_t *mbus_discrete_inputs_values_ptr(const MbusDiscreteInputs *discrete_inputs);

/**
 * Returns the FIFO pointer address.
 */
uint16_t mbus_fifo_queue_ptr_address(const MbusFifoQueue *fifo_queue);

/**
 * Returns the number of values in the FIFO queue.
 */
uint16_t mbus_fifo_queue_count(const MbusFifoQueue *fifo_queue);

/**
 * Reads a FIFO value by index.
 */
enum MbusStatusCode mbus_fifo_queue_value(const MbusFifoQueue *fifo_queue,
                                          uint16_t index,
                                          uint16_t *out_value);

/**
 * Returns a raw pointer to the FIFO values. Valid during callback only.
 */
const uint16_t *mbus_fifo_queue_values_ptr(const MbusFifoQueue *fifo_queue);

/**
 * Returns the starting address of the registers range.
 */
uint16_t mbus_registers_from_address(const MbusRegisters *registers);

/**
 * Returns the number of registers.
 */
uint16_t mbus_registers_quantity(const MbusRegisters *registers);

/**
 * Reads a single register value by address into `out_value`.
 */
enum MbusStatusCode mbus_registers_value(const MbusRegisters *registers,
                                         uint16_t address,
                                         uint16_t *out_value);

/**
 * Reads a single register value by 0-based index into `out_value`.
 *
 * `index` must be less than [`mbus_registers_quantity`]; otherwise returns
 * `MBUS_ERR_INVALID_ADDRESS`. Unlike [`mbus_registers_value`], no knowledge of
 * the Modbus starting address is required.
 */
enum MbusStatusCode mbus_registers_value_at_index(const MbusRegisters *registers,
                                                  uint16_t index,
                                                  uint16_t *out_value);

/**
 * Returns a raw pointer to the register values. Valid during callback only.
 */
const uint16_t *mbus_registers_values_ptr(const MbusRegisters *registers);

/**
 * Lock the global pool (used only during client creation/destruction).
 */
extern void mbus_pool_lock(void);

/**
 * Unlock the global pool.
 */
extern void mbus_pool_unlock(void);

/**
 * Lock a specific client instance (used continuously during polling and requests).
 */
extern void mbus_client_lock(MbusClientId id);

/**
 * Unlock a specific client instance.
 */
extern void mbus_client_unlock(MbusClientId id);

/**
 * Create a new Modbus Serial client.
 *
 * Returns a `MbusClientId` on success, or `MBUS_INVALID_CLIENT_ID` on failure.
 *
 * # Safety
 * `config`, `transport_callbacks`, and `callbacks` must be valid non-null
 * pointers for the duration of this call. They are not retained after the
 * call returns.
 */
MbusClientId mbus_serial_client_new(const struct MbusSerialConfig *config,
                                    const struct MbusTransportCallbacks *transport_callbacks,
                                    const struct MbusCallbacks *callbacks);

/**
 * Free a Modbus Serial client created by [`mbus_serial_client_new`].
 *
 * After this call the ID is invalid and must not be used.
 * Passing `MBUS_INVALID_CLIENT_ID` is a no-op.
 */
void mbus_serial_client_free(MbusClientId id);

/**
 * Open the serial port with the configured parameters.
 */
enum MbusStatusCode mbus_serial_connect(MbusClientId id);

/**
 * Close the serial port.
 *
 * Pending in-flight requests are failed immediately with `MBUS_ERR_CONNECTION_LOST`.
 * The client ID remains valid; call [`mbus_serial_connect`] to reopen the port.
 */
enum MbusStatusCode mbus_serial_disconnect(MbusClientId id);

/**
 * Returns `1` if the serial port is currently open, `0` otherwise.
 */
uint8_t mbus_serial_is_connected(MbusClientId id);

/**
 * Drive the serial Modbus state machine once.
 *
 * Call periodically from your application loop. All registered callbacks are
 * invoked synchronously from within this call.
 */
void mbus_serial_poll(MbusClientId id);

/**
 * Disconnect then reconnect the serial port.
 */
enum MbusStatusCode mbus_serial_reconnect(MbusClientId id);

/**
 * Create a new Modbus TCP client.
 *
 * - `config`    — Must be a valid, non-null pointer to an initialised [`MbusTcpConfig`].
 * - `transport_callbacks` — Must define connect/disconnect/send/recv/is_connected.
 * - `callbacks` — Must be a valid, non-null pointer to an initialised [`MbusCallbacks`].
 *
 * Returns a numeric `MbusClientId` on success, or `MBUS_INVALID_CLIENT_ID`
 * (0xFF) on failure (e.g. invalid config, pool full).
 *
 * # Safety
 * `config`, `transport_callbacks`, and `callbacks` must be valid pointers for
 * the duration of this call. They are not retained after the call returns.
 */
MbusClientId mbus_tcp_client_new(const struct MbusTcpConfig *config,
                                 const struct MbusTransportCallbacks *transport_callbacks,
                                 const struct MbusCallbacks *callbacks);

/**
 * Free a Modbus TCP client created by [`mbus_tcp_client_new`].
 *
 * After this call the ID is invalid and must not be used.
 * Passing `MBUS_INVALID_CLIENT_ID` is a no-op.
 */
void mbus_tcp_client_free(MbusClientId id);

/**
 * Open the TCP connection to the configured host:port.
 *
 * Returns `MBUS_OK` on success or a specific error code on failure.
 */
enum MbusStatusCode mbus_tcp_connect(MbusClientId id);

/**
 * Close the TCP connection.
 *
 * Pending in-flight requests are failed immediately with `MBUS_ERR_CONNECTION_LOST`.
 * The client ID remains valid; call [`mbus_tcp_connect`] to reconnect.
 */
enum MbusStatusCode mbus_tcp_disconnect(MbusClientId id);

/**
 * Returns `1` if the TCP connection is currently open, `0` otherwise.
 */
uint8_t mbus_tcp_is_connected(MbusClientId id);

/**
 * Drive the Modbus state machine: receive pending frames, match responses to
 * outstanding requests, fire any ready callbacks, and handle timeouts / retries.
 *
 * Call this function periodically (e.g. every 5–20 ms) from your application
 * loop. All registered callbacks are invoked synchronously from within this call.
 */
void mbus_tcp_poll(MbusClientId id);

/**
 * Disconnect then reconnect. Useful after a `MBUS_ERR_CONNECTION_LOST` callback.
 */
enum MbusStatusCode mbus_tcp_reconnect(MbusClientId id);

/**
 * Queue a Read Coils (FC 0x01) request.
 *
 * The response is delivered via [`MbusCallbacks::on_read_coils`].
 */
enum MbusStatusCode mbus_tcp_read_coils(MbusClientId id,
                                        uint16_t txn_id,
                                        uint8_t unit_id,
                                        uint16_t address,
                                        uint16_t quantity);

/**
 * Queue a Read Coils (FC 0x01) request on a serial client.
 */
enum MbusStatusCode mbus_serial_read_coils(MbusClientId id,
                                           uint16_t txn_id,
                                           uint8_t unit_id,
                                           uint16_t address,
                                           uint16_t quantity);

/**
 * Queue a Read Single Coil request (reads FC 0x01 with quantity=1).
 */
enum MbusStatusCode mbus_tcp_read_single_coil(MbusClientId id,
                                              uint16_t txn_id,
                                              uint8_t unit_id,
                                              uint16_t address);

/**
 * Queue a Read Single Coil request on a serial client.
 */
enum MbusStatusCode mbus_serial_read_single_coil(MbusClientId id,
                                                 uint16_t txn_id,
                                                 uint8_t unit_id,
                                                 uint16_t address);

/**
 * Queue a Write Single Coil (FC 0x05) request. `value`: 1 = ON, 0 = OFF.
 */
enum MbusStatusCode mbus_tcp_write_single_coil(MbusClientId id,
                                               uint16_t txn_id,
                                               uint8_t unit_id,
                                               uint16_t address,
                                               uint8_t value);

/**
 * Queue a Write Single Coil (FC 0x05) request on a serial client.
 */
enum MbusStatusCode mbus_serial_write_single_coil(MbusClientId id,
                                                  uint16_t txn_id,
                                                  uint8_t unit_id,
                                                  uint16_t address,
                                                  uint8_t value);

/**
 * Queue a Write Multiple Coils (FC 0x0F) request.
 *
 * `values` must point to at least `ceil(quantity / 8)` bytes of bit-packed coil
 * data (LSB-first). Valid for the duration of this call only.
 *
 * # Safety
 * `values` must be a valid non-null pointer.
 */
enum MbusStatusCode mbus_tcp_write_multiple_coils(MbusClientId id,
                                                  uint16_t txn_id,
                                                  uint8_t unit_id,
                                                  uint16_t address,
                                                  const uint8_t *values,
                                                  uint16_t quantity);

/**
 * Queue a Write Multiple Coils (FC 0x0F) request on a serial client.
 *
 * # Safety
 * `values` must be a valid non-null pointer.
 */
enum MbusStatusCode mbus_serial_write_multiple_coils(MbusClientId id,
                                                     uint16_t txn_id,
                                                     uint8_t unit_id,
                                                     uint16_t address,
                                                     const uint8_t *values,
                                                     uint16_t quantity);

/**
 * Queue a Diagnostics (FC 0x08) request.
 *
 * # Safety
 * If `data_len > 0`, `data` must be valid for that many `u16` words.
 */
enum MbusStatusCode mbus_tcp_diagnostics(MbusClientId id,
                                         uint16_t txn_id,
                                         uint8_t unit_id,
                                         uint16_t sub_fn,
                                         const uint16_t *data,
                                         uint16_t data_len);

/**
 * Queue a Diagnostics (FC 0x08) request on a serial client.
 *
 * # Safety
 * If `data_len > 0`, `data` must be valid for that many `u16` words.
 */
enum MbusStatusCode mbus_serial_diagnostics(MbusClientId id,
                                            uint16_t txn_id,
                                            uint8_t unit_id,
                                            uint16_t sub_fn,
                                            const uint16_t *data,
                                            uint16_t data_len);

/**
 * Queue a Read Device Identification (FC 0x2B/0x0E) request.
 *
 * `dev_id_code`: 1=Basic, 2=Regular, 3=Extended, 4=Specific.
 * `object_id`: starting object ID (0x00–0xFF).
 */
enum MbusStatusCode mbus_tcp_read_device_identification(MbusClientId id,
                                                        uint16_t txn_id,
                                                        uint8_t unit_id,
                                                        uint8_t dev_id_code,
                                                        uint8_t object_id);

/**
 * Queue a Read Device Identification request on a serial client.
 */
enum MbusStatusCode mbus_serial_read_device_identification(MbusClientId id,
                                                           uint16_t txn_id,
                                                           uint8_t unit_id,
                                                           uint8_t dev_id_code,
                                                           uint8_t object_id);

/**
 * Queue a Read Discrete Inputs (FC 0x02) request.
 *
 * The response is delivered via [`MbusCallbacks::on_read_discrete_inputs`].
 */
enum MbusStatusCode mbus_tcp_read_discrete_inputs(MbusClientId id,
                                                  uint16_t txn_id,
                                                  uint8_t unit_id,
                                                  uint16_t address,
                                                  uint16_t quantity);

/**
 * Queue a Read Discrete Inputs (FC 0x02) request on a serial client.
 */
enum MbusStatusCode mbus_serial_read_discrete_inputs(MbusClientId id,
                                                     uint16_t txn_id,
                                                     uint8_t unit_id,
                                                     uint16_t address,
                                                     uint16_t quantity);

/**
 * Queue a Read Single Discrete Input request (FC 0x02 with quantity=1).
 */
enum MbusStatusCode mbus_tcp_read_single_discrete_input(MbusClientId id,
                                                        uint16_t txn_id,
                                                        uint8_t unit_id,
                                                        uint16_t address);

/**
 * Queue a Read Single Discrete Input request on a serial client.
 */
enum MbusStatusCode mbus_serial_read_single_discrete_input(MbusClientId id,
                                                           uint16_t txn_id,
                                                           uint8_t unit_id,
                                                           uint16_t address);

/**
 * Queue a Read FIFO Queue (FC 0x18) request.
 *
 * The response is delivered via [`MbusCallbacks::on_read_fifo_queue`].
 */
enum MbusStatusCode mbus_tcp_read_fifo_queue(MbusClientId id,
                                             uint16_t txn_id,
                                             uint8_t unit_id,
                                             uint16_t address);

/**
 * Queue a Read FIFO Queue (FC 0x18) request on a serial client.
 */
enum MbusStatusCode mbus_serial_read_fifo_queue(MbusClientId id,
                                                uint16_t txn_id,
                                                uint8_t unit_id,
                                                uint16_t address);

/**
 * Queue a Read File Record (FC 0x14) request.
 *
 * # Safety
 * `sub_reqs` must be a valid non-null pointer to `count` items.
 */
enum MbusStatusCode mbus_tcp_read_file_record(MbusClientId id,
                                              uint16_t txn_id,
                                              uint8_t unit_id,
                                              const struct MbusSubRequest *sub_reqs,
                                              uint16_t count);

/**
 * Queue a Read File Record (FC 0x14) request on a serial client.
 *
 * # Safety
 * `sub_reqs` must be a valid non-null pointer to `count` items.
 */
enum MbusStatusCode mbus_serial_read_file_record(MbusClientId id,
                                                 uint16_t txn_id,
                                                 uint8_t unit_id,
                                                 const struct MbusSubRequest *sub_reqs,
                                                 uint16_t count);

/**
 * Queue a Write File Record (FC 0x15) request.
 *
 * # Safety
 * `sub_reqs` must be valid. Each `sub_reqs[i].data` must be valid for
 * `sub_reqs[i].data_len` words.
 */
enum MbusStatusCode mbus_tcp_write_file_record(MbusClientId id,
                                               uint16_t txn_id,
                                               uint8_t unit_id,
                                               const struct MbusSubRequest *sub_reqs,
                                               uint16_t count);

/**
 * Queue a Write File Record (FC 0x15) request on a serial client.
 *
 * # Safety
 * `sub_reqs` must be valid. Each `sub_reqs[i].data` must be valid for
 * `sub_reqs[i].data_len` words.
 */
enum MbusStatusCode mbus_serial_write_file_record(MbusClientId id,
                                                  uint16_t txn_id,
                                                  uint8_t unit_id,
                                                  const struct MbusSubRequest *sub_reqs,
                                                  uint16_t count);

/**
 * Queue a Write Multiple Registers (FC 0x10) request.
 *
 * `values` must point to at least `quantity` `u16` words, valid for this call only.
 *
 * # Safety
 * `values` must be a valid non-null pointer.
 */
enum MbusStatusCode mbus_tcp_write_multiple_registers(MbusClientId id,
                                                      uint16_t txn_id,
                                                      uint8_t unit_id,
                                                      uint16_t address,
                                                      const uint16_t *values,
                                                      uint16_t quantity);

/**
 * Queue a Write Multiple Registers (FC 0x10) request on a serial client.
 *
 * # Safety
 * `values` must be a valid non-null pointer.
 */
enum MbusStatusCode mbus_serial_write_multiple_registers(MbusClientId id,
                                                         uint16_t txn_id,
                                                         uint8_t unit_id,
                                                         uint16_t address,
                                                         const uint16_t *values,
                                                         uint16_t quantity);

/**
 * Queue a Read/Write Multiple Registers (FC 0x17) request.
 *
 * `write_values` must point to at least `write_qty` valid `u16` words.
 *
 * # Safety
 * `write_values` must be a valid non-null pointer.
 */
enum MbusStatusCode mbus_tcp_read_write_multiple_registers(MbusClientId id,
                                                           uint16_t txn_id,
                                                           uint8_t unit_id,
                                                           uint16_t read_address,
                                                           uint16_t read_qty,
                                                           uint16_t write_address,
                                                           const uint16_t *write_values,
                                                           uint16_t write_qty);

/**
 * Queue a Read/Write Multiple Registers (FC 0x17) request on a serial client.
 *
 * # Safety
 * `write_values` must be a valid non-null pointer.
 */
enum MbusStatusCode mbus_serial_read_write_multiple_registers(MbusClientId id,
                                                              uint16_t txn_id,
                                                              uint8_t unit_id,
                                                              uint16_t read_address,
                                                              uint16_t read_qty,
                                                              uint16_t write_address,
                                                              const uint16_t *write_values,
                                                              uint16_t write_qty);

#endif  /* MBUS_FFI_H */
