FAQ
===

General
-------

How do I connect to a specific device?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Use serial-number-based connection when you know the adapter serial. Use HID path when you want to bind to a specific OS device path.

**C++ API**

.. code-block:: cpp

   auto sdk = std::make_unique<CosmicSDK>(2000);
   bool okSerial = sdk->connectWithSerialNumber("SN12345678");
   bool okPath = sdk->connect(R"(\\?\hid#vid_2e8a&pid_...)");

**C API**

.. code-block:: c

   CosmicSDK_Handle* h = CosmicSDK_Create(2000);
   bool ok_serial = CosmicSDK_ConnectWithSerialNumber(h, "SN12345678");
   bool ok_path = CosmicSDK_Connect(h, "\\\\?\\hid#vid_2e8a&pid_...");


I2C
---

Which I2C role is supported by the host adapters?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The host adapter works as I2C Controller (Master) only. Support for the Target (Slave) role is planned for a future release.

**C++ API**

.. code-block:: cpp

   // Controller/Master initialization entry point
   uint16_t rc = sdk->i2cControllerInit(I2C_BUS_A, 400000, I2C_PULLUP_2_2kOhm);

**C API**

.. code-block:: c

   // Controller/Master initialization entry point
   uint16_t rc = CosmicSDK_I2cControllerInit(h, I2C_BUS_A, 400000, I2C_PULLUP_2_2kOhm);

How many I2C buses are supported on Supernova and Pulsar?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Supernova supports one I2C bus (``I2C_BUS_A``). Pulsar supports two I2C buses (``I2C_BUS_A`` and ``I2C_BUS_B``).

**C++ API**

.. code-block:: cpp

   uint16_t rcA = sdk->i2cControllerInit(I2C_BUS_A, 400000, I2C_PULLUP_2_2kOhm);
   uint16_t rcB = sdk->i2cControllerInit(I2C_BUS_B, 400000, I2C_PULLUP_2_2kOhm); // Pulsar only

**C API**

.. code-block:: c

   uint16_t rcA = CosmicSDK_I2cControllerInit(h, I2C_BUS_A, 400000, I2C_PULLUP_2_2kOhm);
   uint16_t rcB = CosmicSDK_I2cControllerInit(h, I2C_BUS_B, 400000, I2C_PULLUP_2_2kOhm); // Pulsar only

How do I differentiate I2C buses in the API?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Use the ``bus`` argument (``I2C_BUS_A`` or ``I2C_BUS_B``) in all I2C methods.

**C++ API**

.. code-block:: cpp

   sdk->i2cWrite(I2C_BUS_A, 0x50, {0x00}, {0xAA, 0xBB});
   sdk->i2cWrite(I2C_BUS_B, 0x50, {0x00}, {0xCC, 0xDD});

**C API**

.. code-block:: c

   uint8_t sub[] = {0x00};
   uint8_t dataA[] = {0xAA, 0xBB};
   uint8_t dataB[] = {0xCC, 0xDD};
   CosmicSDK_I2cWrite(h, I2C_BUS_A, 0x50, sub, 1, dataA, 2, false);
   CosmicSDK_I2cWrite(h, I2C_BUS_B, 0x50, sub, 1, dataB, 2, false);

How do I initialize the I2C interface?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Set bus voltage (if needed), then call the I2C controller init method.

**C++ API**

.. code-block:: cpp

   sdk->i2cSetBusVoltage(3300);
   uint16_t rc = sdk->i2cControllerInit(I2C_BUS_A, 400000, I2C_PULLUP_2_2kOhm);

**C API**

.. code-block:: c

   CosmicSDK_I2cSetBusVoltage(h, 3300, false);
   uint16_t rc = CosmicSDK_I2cControllerInit(h, I2C_BUS_A, 400000, I2C_PULLUP_2_2kOhm);

How do I change I2C bus configuration after initialization?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Call the set-parameters method instead of calling init again.

**C++ API**

.. code-block:: cpp

   uint16_t rc = sdk->i2cSetParameters(I2C_BUS_A, 1000000, I2C_PULLUP_1kOhm);

**C API**

.. code-block:: c

   uint16_t rc = CosmicSDK_I2cSetParameters(h, I2C_BUS_A, 1000000, I2C_PULLUP_1kOhm);

How do I write to and read from an I2C target (slave)?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Use ``i2cWrite`` / ``CosmicSDK_I2cWrite`` to send bytes to the target address, and ``i2cRead`` / ``CosmicSDK_I2cRead`` to read bytes from that target.

**C++ API**

.. code-block:: cpp

   std::vector<uint8_t> tx = {0xAA, 0xBB, 0xCC};
   std::vector<uint8_t> rx;
   sdk->i2cWrite(I2C_BUS_A, 0x50, {0x00, 0x10}, tx, false);          // Write at subaddress 0x0010
   sdk->i2cRead(I2C_BUS_A, 0x50, {0x00, 0x10}, 8, rx, false);        // Read 8 bytes from subaddress 0x0010

**C API**

.. code-block:: c

   uint8_t subaddr[] = {0x00, 0x10};
   uint8_t tx[] = {0xAA, 0xBB, 0xCC};
   uint8_t rx[8] = {0};
   CosmicSDK_I2cWrite(h, I2C_BUS_A, 0x50, subaddr, 2, tx, 3, false);  // Write at subaddress 0x0010
   CosmicSDK_I2cRead(h, I2C_BUS_A, 0x50, subaddr, 2, 8, rx, false);    // Read 8 bytes from subaddress 0x0010

How can I scan the bus and discover the static address of the I2C targets connected to the USB host adapter?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Use ``i2cScanBus`` / ``CosmicSDK_I2cScanBus``. The discovered static addresses are returned in the output vector/buffer.

.. note::
   Depending on the bus characteristics, such as frequency, voltage, number of targets connected, and the length of the wires used in the 
   hardware setup, the performance of the I2C SCAN BUS may be affected, and as a result, it might return a wrong list of detected addresses, 
   an ``SDK_ERROR_TIMEOUT`` error, and the firmware might indeed get stuck, especially in the Pulsar. In order to avoid this scenario, consider 
   the following recommendations to run a smooth scan bus procedure:

   - Set the clock frequency to low values, such as 100KHz and 400 KHz.
   - Disable the pull-up resistors, or set their value to high values - weak pull-ups.

      - In the case of the Pulsar, suggested values are: ``I2C_PULLUP_DISABLE`` , ``I2C_PULLUP_4kOhm``, or ``I2C_PULLUP_2_2kOhm``
      - In the case of the Supernova, suggested values are: ``I2C_PULLUP_DISABLE`` , ``I2C_PULLUP_10kOhm``, or ``I2C_PULLUP_4_7kOhm``

**C++ API**

.. code-block:: cpp

   std::vector<uint8_t> addresses;
   uint8_t count7Bit = 0;
   uint8_t count10Bit = 0;
   uint16_t rc = sdk->i2cScanBus(addresses, &count7Bit, &count10Bit, I2C_BUS_A, true);

**C API**

.. code-block:: c

   uint8_t addresses[32] = {0};
   uint8_t count7Bit = 0;
   uint8_t count10Bit = 0;
   uint16_t rc = CosmicSDK_I2cScanBus(h, addresses, 32, &count7Bit, &count10Bit, I2C_BUS_A, true);

Does the API support I2C 10-bit addresses?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Only bus scan supports 10-bit addresses today. 10-bit transfer support already exists in firmware but is not yet exposed in the public API. We plan to add API support for 10-bit transfers in a future release.

Which I2C pull-up resistor values are valid for Supernova and which for Pulsar?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The two devices differ on the available pull-up resistor values. The constants from 150 Ω to 2.2 kΩ are shared; above 2.2 kΩ each device provides its own set. Passing an unsupported value returns ``FW_INVALID_PARAMETER``.

.. list-table:: I2C pull-up resistor support by device
   :header-rows: 1
   :widths: 40 15 15

   * - Constant
     - Supernova
     - Pulsar
   * - ``I2C_PULLUP_150Ohm``
     - Yes
     - Yes
   * - ``I2C_PULLUP_220Ohm``
     - Yes
     - Yes
   * - ``I2C_PULLUP_330Ohm``
     - Yes
     - Yes
   * - ``I2C_PULLUP_470Ohm``
     - Yes
     - Yes
   * - ``I2C_PULLUP_680Ohm``
     - Yes
     - Yes
   * - ``I2C_PULLUP_1kOhm``
     - Yes
     - Yes
   * - ``I2C_PULLUP_1_5kOhm``
     - Yes
     - Yes
   * - ``I2C_PULLUP_2_2kOhm``
     - Yes
     - Yes
   * - ``I2C_PULLUP_3_3kOhm``
     - Yes
     - No
   * - ``I2C_PULLUP_4kOhm``
     - No
     - Yes
   * - ``I2C_PULLUP_4_7kOhm``
     - Yes
     - No
   * - ``I2C_PULLUP_10kOhm``
     - Yes
     - No
   * - ``I2C_PULLUP_DISABLE``
     - Yes
     - Yes

SPI
---

Which SPI role is supported by the host adapters?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The host adapter works as SPI Controller (Master) only.

**C++ API**

.. code-block:: cpp

   uint16_t rc = sdk->spiControllerInit(
       SPI_BIT_ORDER_MSB, SPI_MODE_0, SPI_DATA_WIDTH_8_BITS,
       SPI_CHIP_SELECT_0, SPI_CS_ACTIVE_LOW, 1000000);

**C API**

.. code-block:: c

   uint16_t rc = CosmicSDK_SpiControllerInit(
       h, SPI_BIT_ORDER_MSB, SPI_MODE_0, SPI_DATA_WIDTH_8_BITS,
       SPI_CHIP_SELECT_0, SPI_CS_ACTIVE_LOW, 1000000);

How do I initialize the SPI interface?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Set SPI voltage (if needed), then call SPI controller init.

**C++ API**

.. code-block:: cpp

   sdk->spiSetBusVoltage(3300);
   uint16_t rc = sdk->spiControllerInit(
       SPI_BIT_ORDER_MSB, SPI_MODE_0, SPI_DATA_WIDTH_8_BITS,
       SPI_CHIP_SELECT_0, SPI_CS_ACTIVE_LOW, 10000000);

**C API**

.. code-block:: c

   CosmicSDK_SpiSetBusVoltage(h, 3300, false);
   uint16_t rc = CosmicSDK_SpiControllerInit(
       h, SPI_BIT_ORDER_MSB, SPI_MODE_0, SPI_DATA_WIDTH_8_BITS,
       SPI_CHIP_SELECT_0, SPI_CS_ACTIVE_LOW, 10000000);

How do I change SPI bus configuration after initialization?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Use SPI set-parameters to reconfigure without reinitializing.

**C++ API**

.. code-block:: cpp

   uint16_t rc = sdk->spiSetParameters(
       SPI_BIT_ORDER_MSB, SPI_MODE_3, SPI_DATA_WIDTH_8_BITS,
       SPI_CHIP_SELECT_1, SPI_CS_ACTIVE_LOW, 5000000);

**C API**

.. code-block:: c

   uint16_t rc = CosmicSDK_SpiSetParameters(
       h, SPI_BIT_ORDER_MSB, SPI_MODE_3, SPI_DATA_WIDTH_8_BITS,
       SPI_CHIP_SELECT_1, SPI_CS_ACTIVE_LOW, 5000000);

How do I transmit and receive data in SPI?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Current host adapters support 4-wire SPI. In this mode, TX (MOSI) and RX (MISO) run in parallel during a transfer clock window. For this reason, the API exposes a single method, ``spiTransfer`` / ``CosmicSDK_SpiTransfer``, for both transmit and receive. Provide TX data and transfer length, and read the received bytes from the output buffer/vector.

**C++ API**

.. code-block:: cpp

   std::vector<uint8_t> tx = {0x9F};  // JEDEC ID command
   std::vector<uint8_t> rx;
   uint16_t rc = sdk->spiTransfer(tx, rx, 4);  // 1 command byte + 3 response bytes

**C API**

.. code-block:: c

   uint8_t tx[1] = {0x9F};  // JEDEC ID command
   uint8_t rx[4] = {0};
   uint16_t rc = CosmicSDK_SpiTransfer(h, tx, 1, rx, 4);  // 1 command byte + 3 response bytes


UART
----

How do I initialize the UART interface?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Configure the UART line settings with ``uartInit`` / ``CosmicSDK_UartInit`` before sending data.

**C++ API**

.. code-block:: cpp

   uint16_t rc = sdk->uartInit(
       UART_BAUD_115200, UART_HW_HANDSHAKE_DIS,
       UART_PARITY_NONE, UART_DATA_SIZE_8, UART_STOP_BIT_1);

**C API**

.. code-block:: c

   uint16_t rc = CosmicSDK_UartInit(
       h, UART_BAUD_115200, UART_HW_HANDSHAKE_DIS,
       UART_PARITY_NONE, UART_DATA_SIZE_8, UART_STOP_BIT_1);

How do I change UART configuration after initialization?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Use the UART set-parameters method.

**C++ API**

.. code-block:: cpp

   uint16_t rc = sdk->uartSetParameters(
       UART_BAUD_9600, UART_HW_HANDSHAKE_DIS,
       UART_PARITY_NONE, UART_DATA_SIZE_8, UART_STOP_BIT_1);

**C API**

.. code-block:: c

   uint16_t rc = CosmicSDK_UartSetParameters(
       h, UART_BAUD_9600, UART_HW_HANDSHAKE_DIS,
       UART_PARITY_NONE, UART_DATA_SIZE_8, UART_STOP_BIT_1);

How do I transmit data in UART?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Use ``uartSendMessage`` / ``CosmicSDK_UartSendMessage`` after UART initialization.

**C++ API**

.. code-block:: cpp

   std::vector<uint8_t> msg = {'H', 'e', 'l', 'l', 'o', '\n'};
   uint16_t rc = sdk->uartSendMessage(msg);

**C API**

.. code-block:: c

   uint8_t msg[] = "Hello\n";
   uint16_t rc = CosmicSDK_UartSendMessage(h, msg, 6);

How does UART reception work?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

UART RX is asynchronous only. Register a notification callback and parse UART notifications with helper accessors/functions. Polling is not supported.

**C++ API**

.. code-block:: cpp

   sdk->onNotification([](const Notification& n) {
     if (const UartRxNotification* rx = n.uartRx()) {
       // rx->data / rx->dataLength
     }
   });

**C API**

.. code-block:: c

   static void cb(const Notification* n) {
     UartRxNotification rx;
     if (CosmicSDK_notificationAsUartRx(n, &rx)) {
       // rx.data / rx.dataLength
     }
   }
   CosmicSDK_onNotification(h, cb);


GPIO
----

How do I configure GPIOs?
^^^^^^^^^^^^^^^^^^^^^^^^^^

Call GPIO configure pin with the pin number and desired mode (input or output).

**C++ API**

.. code-block:: cpp

   sdk->gpioConfigurePin(GPIO_1, GPIO_DIGITAL_INPUT);
   sdk->gpioConfigurePin(GPIO_3, GPIO_DIGITAL_OUTPUT, GPIO_LOW);

**C API**

.. code-block:: c

   CosmicSDK_GpioConfigurePin(h, GPIO_1, GPIO_DIGITAL_INPUT, false, GPIO_LOW);
   CosmicSDK_GpioConfigurePin(h, GPIO_3, GPIO_DIGITAL_OUTPUT, true, GPIO_LOW);

How do I enable GPIO interrupts?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

First configure the pin as digital input, then configure the interrupt trigger type.

**C++ API**

.. code-block:: cpp

   sdk->gpioConfigurePin(GPIO_1, GPIO_DIGITAL_INPUT);
   sdk->gpioConfigureInterrupt(GPIO_1, GPIO_TRIGGER_BOTH_EDGES);

**C API**

.. code-block:: c

   CosmicSDK_GpioConfigurePin(h, GPIO_1, GPIO_DIGITAL_INPUT, false, GPIO_LOW);
   CosmicSDK_GpioConfigureInterrupt(h, GPIO_1, GPIO_TRIGGER_BOTH_EDGES);

How do I receive and handle asynchronous GPIO interrupt notifications?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Register a notification callback and parse GPIO notifications using helper accessors/functions.

**C++ API**

.. code-block:: cpp

   sdk->onNotification([](const Notification& n) {
     if (const GpioInterruptNotification* g = n.gpio()) {
       // g->pinNumber
     }
   });

**C API**

.. code-block:: c

   static void cb(const Notification* n) {
     GpioInterruptNotification gpio;
     if (CosmicSDK_notificationAsGpio(n, &gpio)) {
       // gpio.pinNumber
     }
   }
   CosmicSDK_onNotification(h, cb);


I3C
---

Which I3C role does Supernova support?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Supernova supports the I3C Controller role in the current API.

**C++ API**

.. code-block:: cpp

   uint16_t rc = sdk->i3cControllerInit(
       PUSH_PULL_10_MHz_DC_40, OPEN_DRAIN_400_KHz, FAST_MODE, FAST);

**C API**

.. code-block:: c

   uint16_t rc = CosmicSDK_I3cControllerInit(
       h, PUSH_PULL_10_MHz_DC_40, OPEN_DRAIN_400_KHz, FAST_MODE, FAST);

Which I3C modes do Supernova and the I3C API support?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The supported transfer modes are:

- I3C SDR
- Legacy I2C over the I3C bus
- I3C HDR-DDR

Write/read method mapping per mode:

- I3C SDR: ``i3cSdrWrite`` / ``i3cSdrRead``
- Legacy I2C over I3C: ``i3cLegacyI2cWrite`` / ``i3cLegacyI2cRead``
- I3C HDR-DDR: ``i3cHdrDdrWrite`` / ``i3cHdrDdrRead``

**C++ API**

.. code-block:: cpp

   std::vector<uint8_t> out;
   std::vector<uint8_t> in = {0x11, 0x22};

   // I3C SDR
   sdk->i3cSdrWrite(0x08, {0x00}, in, I3C_XFER_NONE);
   sdk->i3cSdrRead(0x08, {0x00}, 8, out, I3C_XFER_NONE);

   // Legacy I2C over I3C
   sdk->i3cLegacyI2cWrite(0x50, {0x00}, in, I3C_XFER_NONE);
   sdk->i3cLegacyI2cRead(0x50, {0x00}, 8, out, I3C_XFER_NONE);

   // I3C HDR-DDR
   sdk->i3cHdrDdrWrite(0x08, 0x20, in);
   sdk->i3cHdrDdrRead(0x08, 0x20, 8, out);

**C API**

.. code-block:: c

   uint8_t in[2] = {0x11, 0x22};
   uint8_t out[32];
   uint8_t out_len = 0;

   // I3C SDR
   CosmicSDK_I3cSdrWrite(h, 0x08, NULL, 0, in, 2, I3C_XFER_NONE);
   CosmicSDK_I3cSdrRead(h, 0x08, NULL, 0, 8, out, &out_len, I3C_XFER_NONE);

   // Legacy I2C over I3C
   CosmicSDK_I3cLegacyI2CWrite(h, 0x50, NULL, 0, in, 2, I3C_XFER_NONE);
   CosmicSDK_I3cLegacyI2CRead(h, 0x50, NULL, 0, 8, out, I3C_XFER_NONE);

   // I3C HDR-DDR
   CosmicSDK_I3cHdrDdrWrite(h, 0x08, 0x20, in, 2);
   CosmicSDK_I3cHdrDdrRead(h, 0x08, 0x20, 8, out);

How do I initialize the I3C interface?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Set I3C voltage and then initialize the controller.

**C++ API**

.. code-block:: cpp

   sdk->i3cSetBusVoltage(1200);
   uint16_t rc = sdk->i3cControllerInit(
       PUSH_PULL_10_MHz_DC_40, OPEN_DRAIN_400_KHz, FAST_MODE, FAST);

**C API**

.. code-block:: c

   CosmicSDK_I3cSetBusVoltage(h, 1200);
   uint16_t rc = CosmicSDK_I3cControllerInit(
       h, PUSH_PULL_10_MHz_DC_40, OPEN_DRAIN_400_KHz, FAST_MODE, FAST);

How do I change I3C bus configuration after initialization?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Use I3C set-parameters.

**C++ API**

.. code-block:: cpp

   uint16_t rc = sdk->i3cSetParameters(
       PUSH_PULL_2_5_MHz_DC_25, OPEN_DRAIN_100_KHz, STANDARD_MODE, STANDARD);

**C API**

.. code-block:: c

   uint16_t rc = CosmicSDK_I3cSetParameters(
       h, PUSH_PULL_2_5_MHz_DC_25, OPEN_DRAIN_100_KHz, STANDARD_MODE, STANDARD);

How do I initialize the I3C bus with or without a target table?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Call ``InitBus`` with an empty table for controller-driven discovery (RSTDAA + ENTDAA). Pass a target table when you need deterministic setup (mixed I3C/I2C, predefined addressing/configuration). The table uses ``I3cTargetInfo``, including ``I3cBCR`` and ``I3cTargetConfiguration``.

**C++ API**

.. code-block:: cpp

   // Discovery flow (no predefined table)
   sdk->i3cInitBus();

   // Deterministic flow with predefined targets
   I3cBCR bcrI3c{};
   bcrI3c.byte = 0x27;  // non-zero BCR
   I3cTargetConfiguration confI3c{};
   confI3c.bits.targetType = 0;      // I3C target
   confI3c.bits.daaUseENTDAA = true;
   confI3c.bits.IBIRequest = true;

   I3cBCR bcrI2c{};
   bcrI2c.byte = 0x00;
   I3cTargetConfiguration confI2c{};
   confI2c.bits.targetType = 1;      // Legacy I2C target
   confI2c.bits.daaUseENTDAA = false;

   std::vector<I3cTargetInfo> targets = {
     // Supernova can use this desired dynamic address during ENTDAA by matching PID/BCR/DCR.
     {0x69, 0x2A, {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC}, bcrI3c, 0x5D, 0, 0, 0, confI3c},
     {0x50, 0x00, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, bcrI2c, 0x00, 0, 0, 0, confI2c}
   };
   sdk->i3cInitBus(targets);

**C API**

.. code-block:: c

   // Discovery flow (no predefined table)
   CosmicSDK_I3cInitBus(h, NULL, 0);

   // Deterministic flow with predefined targets
   I3cBCR bcr_i3c = {0};
   bcr_i3c.byte = 0x27;  // non-zero BCR
   I3cTargetConfiguration conf_i3c = {0};
   conf_i3c.bits.targetType = 0;    // I3C target
   conf_i3c.bits.daaUseENTDAA = true;
   conf_i3c.bits.IBIRequest = true;

   I3cBCR bcr_i2c = {0};
   bcr_i2c.byte = 0x00;
   I3cTargetConfiguration conf_i2c = {0};
   conf_i2c.bits.targetType = 1;    // Legacy I2C target
   conf_i2c.bits.daaUseENTDAA = false;

   I3cTargetInfo targets[2] = {
     // Supernova can use this desired dynamic address during ENTDAA by matching PID/BCR/DCR.
     {0x69, 0x2A, {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC}, bcr_i3c, 0x5D, 0, 0, 0, conf_i3c},
     {0x50, 0x00, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, bcr_i2c, 0x00, 0, 0, 0, conf_i2c}
   };
   CosmicSDK_I3cInitBus(h, targets, 2);

How do I assign dynamic addresses?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Use ``InitBus`` for default assignment, or explicit CCCs: ``ENTDAA`` for dynamic discovery, ``SETDASA`` for static-to-dynamic mapping, and ``SETAASA`` for assigning from known static-address targets.

**C++ API**

.. code-block:: cpp

   sdk->i3cInitBus();
   sdk->i3cCccENTDAA();
   sdk->i3cCccSETDASA(0x69, 0x0B);
   sdk->i3cCccSETAASA({0x69, 0x6B});

**C API**

.. code-block:: c

   CosmicSDK_I3cInitBus(h, NULL, 0);
   CosmicSDK_I3cCccENTDAA(h);
   CosmicSDK_I3cCccSETDASA(h, 0x69, 0x0B);
   uint8_t static_addresses[] = {0x69, 0x6B};
   CosmicSDK_I3cCccSETAASA(h, static_addresses, 2);

Which CCCs are supported by the API?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The API supports common controller-side CCC operations, including:

- Address assignment and discovery: ``RSTDAA``, ``ENTDAA``, ``SETDASA``, ``SETAASA``, ``SETNEWDA``
- Target identification and capabilities: ``GETPID``, ``GETBCR``, ``GETDCR``, ``GETSTATUS``, ``GETCAPS``
- Event and reset control: ``ENEC`` (broadcast/direct), ``DISEC`` (broadcast/direct), ``RSTACT`` (broadcast/direct)
- Transfer limits: ``GETMWL``, ``SETMWL`` (broadcast/direct), ``GETMRL``, ``SETMRL`` (broadcast/direct)

**C++ API**

.. code-block:: cpp

   sdk->i3cCccRSTDAA();
   sdk->i3cCccENTDAA();
   sdk->i3cCccSETDASA(0x69, 0x0B);
   sdk->i3cCccSETAASA({0x69, 0x6B});
   sdk->i3cCccSETNEWDA(0x08, 0x09);

   std::vector<uint8_t> pid, status, caps;
   uint8_t bcr = 0, dcr = 0;
   uint16_t mwl = 0, mrl = 0;
   std::optional<uint8_t> ibiLen;

   sdk->i3cCccGETPID(0x08, pid);
   sdk->i3cCccGETBCR(0x08, bcr);
   sdk->i3cCccGETDCR(0x08, dcr);
   sdk->i3cCccGETSTATUS(0x08, status, ibiLen);
   sdk->i3cCccGETCAPS(0x08, caps, ibiLen);

   sdk->i3cCccENECBroadcast(IBI);
   sdk->i3cCccENECDirect(0x08, IBI);
   sdk->i3cCccDISECBroadcast(IBI);
   sdk->i3cCccDISECDirect(0x08, IBI);

   sdk->i3cCccRSTACTBroadcast(RESET_I3C_PERIPHERAL);
   uint8_t resetTime = 0;
   sdk->i3cCccRSTACTDirect(0x08, RETURN_TIME_RESET_PERIPHERAL, resetTime);

   sdk->i3cCccGETMWL(0x08, mwl);
   sdk->i3cCccSETMWLBroadcast(256);
   sdk->i3cCccSETMWLDirect(0x08, 256);
   sdk->i3cCccGETMRL(0x08, mrl, ibiLen);
   sdk->i3cCccSETMRLBroadcast(256);
   sdk->i3cCccSETMRLDirect(0x08, 256);

**C API**

.. code-block:: c

   CosmicSDK_I3cCccRSTDAA(h);
   CosmicSDK_I3cCccENTDAA(h);
   CosmicSDK_I3cCccSETDASA(h, 0x69, 0x0B);
   uint8_t static_addresses[] = {0x69, 0x6B};
   CosmicSDK_I3cCccSETAASA(h, static_addresses, 2);
   CosmicSDK_I3cCccSETNEWDA(h, 0x08, 0x09);

   uint8_t pid[6] = {0};
   uint8_t bcr = 0, dcr = 0;
   uint8_t status[8] = {0}, caps[8] = {0};
   uint8_t status_len = 0, caps_len = 0;
   uint16_t mwl = 0, mrl = 0;
   bool has_ibi_len = false;
   uint8_t ibi_len = 0;

   CosmicSDK_I3cCccGETPID(h, 0x08, pid);
   CosmicSDK_I3cCccGETBCR(h, 0x08, &bcr);
   CosmicSDK_I3cCccGETDCR(h, 0x08, &dcr);
   CosmicSDK_I3cCccGETSTATUS(h, 0x08, status, sizeof(status), &status_len, &has_ibi_len, &ibi_len);
   CosmicSDK_I3cCccGETCAPS(h, 0x08, caps, sizeof(caps), &caps_len, &has_ibi_len, &ibi_len);

   CosmicSDK_I3cCccENECBroadcast(h, IBI);
   CosmicSDK_I3cCccENECDirect(h, 0x08, IBI);
   CosmicSDK_I3cCccDISECBroadcast(h, IBI);
   CosmicSDK_I3cCccDISECDirect(h, 0x08, IBI);

   CosmicSDK_I3cCccRSTACTBroadcast(h, RESET_I3C_PERIPHERAL);
   uint8_t reset_time = 0;
   CosmicSDK_I3cCccRSTACTDirect(h, 0x08, RETURN_TIME_RESET_PERIPHERAL, &reset_time);

   CosmicSDK_I3cCccGETMWL(h, 0x08, &mwl);
   CosmicSDK_I3cCccSETMWLBroadcast(h, 256);
   CosmicSDK_I3cCccSETMWLDirect(h, 0x08, 256);
   CosmicSDK_I3cCccGETMRL(h, 0x08, &mrl, &has_ibi_len, &ibi_len);
   CosmicSDK_I3cCccSETMRLBroadcast(h, 256, false, 0);
   CosmicSDK_I3cCccSETMRLDirect(h, 0x08, 256, false, 0);

How can I know the actual transfer length in an I3C SDR read?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

An I3C target can terminate a read early using End-Of-Data (EOD, T-bit = 0), so the actual returned length may be shorter than requested.

- In C++, check ``outData.size()`` after ``i3cSdrRead``.
- In C, check ``outDataLength`` from ``CosmicSDK_I3cSdrRead``.

**C++ API**

.. code-block:: cpp

   std::vector<uint8_t> outData;
   uint16_t rc = sdk->i3cSdrRead(0x08, {0x00}, 32, outData, I3C_XFER_NONE);
   if (rc == SUCCESS) {
     size_t actualLen = outData.size();  // May be < 32 when target signals EOD
   }

**C API**

.. code-block:: c

   uint8_t out[32];
   uint8_t actual_len = 0;
   uint16_t rc = CosmicSDK_I3cSdrRead(h, 0x08, NULL, 0, 32, out, &actual_len, I3C_XFER_NONE);
   if (rc == SUCCESS) {
     // actual_len may be < 32 when target signals EOD
   }

How do I handle I3C IBIs?
^^^^^^^^^^^^^^^^^^^^^^^^^

IBIs are asynchronous notifications. Register a callback and decode notifications using the helper accessors/functions.

**C++ API**

.. code-block:: cpp

   sdk->onNotification([](const Notification& n) {
     if (const I3cIbiNotification* ibi = n.i3cIbi()) {
       // ibi->address, ibi->payload, ibi->payloadLength
     }
   });

**C API**

.. code-block:: c

   static void cb(const Notification* n) {
     I3cIbiNotification ibi;
     if (CosmicSDK_notificationAsI3cIbi(n, &ibi)) {
       // ibi.address, ibi.payload, ibi.payloadLength
     }
   }
   CosmicSDK_onNotification(h, cb);

How do I handle I3C Hot-Join notifications?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Hot-Join is also asynchronous. Register a callback and parse the notification using the helper accessors/functions.

**C++ API**

.. code-block:: cpp

   sdk->onNotification([](const Notification& n) {
     if (const I3cHotJoinNotification* hj = n.i3cHotJoin()) {
       // hj->dynamicAddress, hj->bcr, hj->dcr
       // PID bytes: hj->pid[0] ... hj->pid[5]
     }
   });

**C API**

.. code-block:: c

   static void cb(const Notification* n) {
     I3cHotJoinNotification hj;
     if (CosmicSDK_notificationAsI3cHotJoin(n, &hj)) {
       // hj.dynamicAddress, hj.bcr, hj.dcr
       // PID bytes: hj.pid[0] ... hj.pid[5]
     }
   }
   CosmicSDK_onNotification(h, cb);
