embedded-mbedtls 0.2.0

no_std Rust wrapper for Mbed TLS
Documentation
// Copyright Open Logistics Foundation
//
// Licensed under the Open Logistics Foundation License 1.3.
// For details on the licensing terms, see the LICENSE file.
// SPDX-License-Identifier: OLFL-1.3

//! This modules implements the [`MbedtlsTimer`] type which is used as timer type for the C
//! library calls.

use core::time::Duration;

use core::ffi::{c_int, c_void};
use embedded_timers::{clock::Clock, timer::Timer, timer::TimerError};

/// Callback: set a pair of timers/delays to watch
///
/// param ctx: *mut [MbedtlsTimer<'a, CLOCK>]
///
/// ## Panics
/// Panics if a timer can't be started (due to a [Clock] error) or the context is null.
pub(crate) extern "C" fn set_timer<CLOCK: Clock>(ctx: *mut c_void, int_ms: u32, fin_ms: u32) {
    let timer_ctx = unsafe { (ctx as *mut MbedtlsTimer<'_, CLOCK>).as_mut().unwrap() };

    // Stop timer if fin_ms == 0, panic on clock errors, ignore others
    if fin_ms == 0 {
        if timer_ctx
            .int
            .cancel()
            .is_err_and(|e| e != TimerError::NotRunning)
        {
            panic!("Clock error")
        };
        if timer_ctx
            .fin
            .cancel()
            .is_err_and(|e| e != TimerError::NotRunning)
        {
            panic!("Clock error")
        };
        return;
    }

    timer_ctx
        .int
        .try_start(Duration::from_millis(int_ms as u64))
        .unwrap();
    timer_ctx
        .fin
        .try_start(Duration::from_millis(fin_ms as u64))
        .unwrap();
}

/// Callback: get status of timers/delays
///
/// param ctx: *mut [MbedtlsTimer<'a, CLOCK>]
pub(crate) extern "C" fn get_timer<CLOCK: Clock>(ctx: *mut c_void) -> c_int {
    let timer_ctx = unsafe { (ctx as *mut MbedtlsTimer<'_, CLOCK>).as_mut().unwrap() };

    // Check final delay first since the intermediate delay
    // has already passed if the final delay passes
    match timer_ctx.fin.is_expired() {
        Ok(true) => {
            // Return 2 if the final delay has passed
            return 2;
        }
        Ok(false) => {}
        Err(TimerError::NotRunning) => {
            // return -1 if cancelled (not running)
            return -1;
        }
        Err(e) => {
            // return -2 on all other errors
            log::error!("Error querying timer: {:?}", e);
            return -2;
        }
    }
    match timer_ctx.int.is_expired() {
        // Return 1 if only the intermediate delay has passed
        Ok(true) => 1,
        // Return 0 if none of the delays have passed
        Ok(false) => 0,
        Err(TimerError::NotRunning) => -1,
        Err(e) => {
            log::error!("Error querying timer: {:?}", e);
            -2
        }
    }
}

pub(crate) struct MbedtlsTimer<'a, CLOCK: Clock> {
    /// Intermediate delay
    int: Timer<'a, CLOCK>,
    /// Final delay
    fin: Timer<'a, CLOCK>,
}

impl<'a, CLOCK: Clock> MbedtlsTimer<'a, CLOCK> {
    /// Create a new MbedTLS timer context
    pub(crate) fn new(clock: &'a CLOCK) -> Self {
        MbedtlsTimer {
            int: Timer::new(clock),
            fin: Timer::new(clock),
        }
    }
}