| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // ====================================================================== | ||
| 2 | // \title I2cDriverHardwareStub.cpp | ||
| 3 | // \author tumbar | ||
| 4 | // \brief Stub hardware implementation for the I2C peripheral (Linux/test builds) | ||
| 5 | // | ||
| 6 | // This file is compiled for Linux/test builds to enable unit testing. | ||
| 7 | // For SAMD21 target builds, I2cDriverHardware.cpp is used instead. | ||
| 8 | // | ||
| 9 | // The stub records the arguments of each hardware call and exposes injectable | ||
| 10 | // interrupt/bus status so unit tests can drive the component's state machine, | ||
| 11 | // ISR error dispatch and telemetry mapping without any real MCU. | ||
| 12 | // ====================================================================== | ||
| 13 | |||
| 14 | #include "fprime-samd/Drv/I2cDriver/I2cDriverHardware.hpp" | ||
| 15 | |||
| 16 | namespace Samd21 { | ||
| 17 | namespace I2cHardware { | ||
| 18 | |||
| 19 | static StubState s_state = {}; | ||
| 20 | |||
| 21 | ✗ | StubState& getStubState() { | |
| 22 | ✗ | return s_state; | |
| 23 | } | ||
| 24 | |||
| 25 | ✗ | void resetStubState() { | |
| 26 | // Assign a fresh value-initialized instance rather than memset: several | ||
| 27 | // members (e.g. SercomKind) are autocoded classes with vtables that | ||
| 28 | // memset would corrupt. | ||
| 29 | ✗ | s_state = StubState{}; | |
| 30 | ✗ | } | |
| 31 | |||
| 32 | ✗ | void fireIsr() { | |
| 33 | ✗ | if (s_state.isr_callback != nullptr) { | |
| 34 | ✗ | s_state.isr_callback(s_state.isr_data, s_state.isr_sercom); | |
| 35 | } | ||
| 36 | ✗ | } | |
| 37 | |||
| 38 | ✗ | void I2cHal::registerIsr(SercomKind sercom, | |
| 39 | void (*callback)(Fw::PassiveComponentBase*, SercomKind), | ||
| 40 | Fw::PassiveComponentBase* data) { | ||
| 41 | ✗ | s_state.isr_callback = callback; | |
| 42 | ✗ | s_state.isr_data = data; | |
| 43 | ✗ | s_state.isr_sercom = sercom; | |
| 44 | ✗ | } | |
| 45 | |||
| 46 | ✗ | void I2cHal::configure(SercomKind sercom, | |
| 47 | I2cDriver::SclLowTimeout scl_low_timeout, | ||
| 48 | I2cDriver::InactiveTimeout inactive_timeout, | ||
| 49 | I2cDriver::ClockStretchMode clock_stretch_mode, | ||
| 50 | I2cDriver::Frequency frequency, | ||
| 51 | I2cDriver::ClientSclLowTimeout client_scl_low_timeout, | ||
| 52 | I2cDriver::HostSclLowTimeout host_scl_low_timeout, | ||
| 53 | I2cDriver::SdaHold sda_hold, | ||
| 54 | I2cDriver::PinUsage pin_usage, | ||
| 55 | I2cDriver::RunInStandby run_in_standby) { | ||
| 56 | ✗ | s_state.configured = true; | |
| 57 | ✗ | s_state.configure_count++; | |
| 58 | ✗ | s_state.sercom = sercom; | |
| 59 | ✗ | s_state.scl_low_timeout = scl_low_timeout; | |
| 60 | ✗ | s_state.inactive_timeout = inactive_timeout; | |
| 61 | ✗ | s_state.clock_stretch_mode = clock_stretch_mode; | |
| 62 | ✗ | s_state.frequency = frequency; | |
| 63 | ✗ | s_state.client_scl_low_timeout = client_scl_low_timeout; | |
| 64 | ✗ | s_state.host_scl_low_timeout = host_scl_low_timeout; | |
| 65 | ✗ | s_state.sda_hold = sda_hold; | |
| 66 | ✗ | s_state.pin_usage = pin_usage; | |
| 67 | ✗ | s_state.run_in_standby = run_in_standby; | |
| 68 | ✗ | } | |
| 69 | |||
| 70 | ✗ | U32 I2cHal::getDataRegisterAddress(SercomKind sercom) { | |
| 71 | ✗ | return s_state.data_register_address; | |
| 72 | } | ||
| 73 | |||
| 74 | ✗ | I2cInterruptStatus I2cHal::readInterruptStatus(SercomKind sercom) { | |
| 75 | ✗ | return s_state.interrupt_status; | |
| 76 | } | ||
| 77 | |||
| 78 | ✗ | I2cBusStatus I2cHal::readBusStatus(SercomKind sercom) { | |
| 79 | ✗ | return s_state.bus_status; | |
| 80 | } | ||
| 81 | |||
| 82 | ✗ | I2cRawRegisters I2cHal::readRawRegisters(SercomKind sercom) { | |
| 83 | ✗ | return s_state.raw_registers; | |
| 84 | } | ||
| 85 | |||
| 86 | ✗ | void I2cHal::recoverBusToIdle(SercomKind sercom) { | |
| 87 | ✗ | s_state.recover_bus_count++; | |
| 88 | |||
| 89 | // Model the write-1-clear recovery: after recovery no interrupt/error flags | ||
| 90 | // remain pending and the bus is IDLE, so the driver can start fresh. | ||
| 91 | ✗ | s_state.interrupt_status = I2cInterruptStatus{}; | |
| 92 | ✗ | s_state.bus_status.busState = 0x1; // IDLE | |
| 93 | ✗ | s_state.bus_status.clockHold = false; | |
| 94 | ✗ | s_state.bus_status.masterOnBus = false; | |
| 95 | ✗ | s_state.bus_status.slaveOnBus = false; | |
| 96 | ✗ | } | |
| 97 | |||
| 98 | ✗ | void I2cHal::acknowledgeErrors(SercomKind sercom) { | |
| 99 | ✗ | s_state.acknowledge_errors_count++; | |
| 100 | // Model the write-1-clear: once acked, the pending error/flags go away so a | ||
| 101 | // subsequent readInterruptStatus() does not re-report the same error. | ||
| 102 | ✗ | s_state.interrupt_status.error = false; | |
| 103 | ✗ | s_state.interrupt_status.busError = false; | |
| 104 | ✗ | s_state.interrupt_status.arbLost = false; | |
| 105 | ✗ | s_state.interrupt_status.lowTimeout = false; | |
| 106 | ✗ | s_state.interrupt_status.masterExtTimeout = false; | |
| 107 | ✗ | s_state.interrupt_status.slaveExtTimeout = false; | |
| 108 | ✗ | s_state.interrupt_status.lengthError = false; | |
| 109 | ✗ | } | |
| 110 | |||
| 111 | ✗ | void I2cHal::acknowledgeMasterOnBus(SercomKind sercom) { | |
| 112 | ✗ | s_state.acknowledge_mb_count++; | |
| 113 | ✗ | s_state.interrupt_status.masterOnBus = false; | |
| 114 | ✗ | } | |
| 115 | |||
| 116 | ✗ | void I2cHal::enableMasterOnBusInterrupt(SercomKind sercom) { | |
| 117 | ✗ | s_state.enable_mb_count++; | |
| 118 | ✗ | } | |
| 119 | |||
| 120 | ✗ | void I2cHal::disableMasterOnBusInterrupt(SercomKind sercom) { | |
| 121 | ✗ | s_state.disable_mb_count++; | |
| 122 | ✗ | } | |
| 123 | |||
| 124 | ✗ | void I2cHal::beginRead(SercomKind sercom, U32 addr, U8 byteCount) { | |
| 125 | ✗ | s_state.begin_read_count++; | |
| 126 | ✗ | s_state.begin_read_addr = addr; | |
| 127 | ✗ | s_state.begin_read_len = byteCount; | |
| 128 | ✗ | } | |
| 129 | |||
| 130 | ✗ | void I2cHal::beginWrite(SercomKind sercom, U32 addr, U8 byteCount, bool generateStopCondition) { | |
| 131 | ✗ | s_state.begin_write_count++; | |
| 132 | ✗ | s_state.begin_write_addr = addr; | |
| 133 | ✗ | s_state.begin_write_len = byteCount; | |
| 134 | ✗ | s_state.begin_write_stop = generateStopCondition; | |
| 135 | ✗ | } | |
| 136 | |||
| 137 | } // namespace I2cHardware | ||
| 138 | } // namespace Samd21 | ||
| 139 |