181 lines · 4.9 KB · Rust
RawDownload
// SPDX-License-Identifier: GPL-2.0 //! IO polling.//!//! C header: [`include/linux/iopoll.h`](srctree/include/linux/iopoll.h). use crate::{    prelude::*,    processor::cpu_relax,    task::might_sleep,    time::{        delay::{            fsleep,            udelay, //        },        Delta,        Instant,        Monotonic, //    },}; /// Polls periodically until a condition is met, an error occurs,/// or the timeout is reached.////// The function repeatedly executes the given operation `op` closure and/// checks its result using the condition closure `cond`.////// If `cond` returns `true`, the function returns successfully with/// the result of `op`. Otherwise, it waits for a duration specified/// by `sleep_delta` before executing `op` again.////// This process continues until either `op` returns an error, `cond`/// returns `true`, or the timeout specified by `timeout_delta` is/// reached.////// This function can only be used in a nonatomic context.////// # Errors////// If `op` returns an error, then that error is returned directly.////// If the timeout specified by `timeout_delta` is reached, then/// `Err(ETIMEDOUT)` is returned.////// # Examples////// ```no_run/// use kernel::io::{///     Io,///     Mmio,///     poll::read_poll_timeout, ///// };/// use kernel::time::Delta;////// const HW_READY: u16 = 0x01;////// fn wait_for_hardware<const SIZE: usize>(io: &Mmio<SIZE>) -> Result {///     read_poll_timeout(///         // The `op` closure reads the value of a specific status register.///         || io.try_read16(0x1000),///         // The `cond` closure takes a reference to the value returned by `op`///         // and checks whether the hardware is ready.///         |val: &u16| *val == HW_READY,///         Delta::from_millis(50),///         Delta::from_secs(3),///     )?;///     Ok(())/// }/// ```#[track_caller]pub fn read_poll_timeout<Op, Cond, T>(    mut op: Op,    mut cond: Cond,    sleep_delta: Delta,    timeout_delta: Delta,) -> Result<T>where    Op: FnMut() -> Result<T>,    Cond: FnMut(&T) -> bool,{    let start: Instant<Monotonic> = Instant::now();     // Unlike the C version, we always call `might_sleep()` unconditionally,    // as conditional calls are error-prone. We clearly separate    // `read_poll_timeout()` and `read_poll_timeout_atomic()` to aid    // tools like klint.    might_sleep();     loop {        let val = op()?;        if cond(&val) {            // Unlike the C version, we immediately return.            // We know the condition is met so we don't need to check again.            return Ok(val);        }         if start.elapsed() > timeout_delta {            // Unlike the C version, we immediately return.            // We have just called `op()` so we don't need to call it again.            return Err(ETIMEDOUT);        }         if !sleep_delta.is_zero() {            fsleep(sleep_delta);        }         // `fsleep()` could be a busy-wait loop so we always call `cpu_relax()`.        cpu_relax();    }} /// Polls periodically until a condition is met, an error occurs,/// or the attempt limit is reached.////// The function repeatedly executes the given operation `op` closure and/// checks its result using the condition closure `cond`.////// If `cond` returns `true`, the function returns successfully with the result of `op`./// Otherwise, it performs a busy wait for a duration specified by `delay_delta`/// before executing `op` again.////// This process continues until either `op` returns an error, `cond`/// returns `true`, or the attempt limit specified by `retry` is reached.////// # Errors////// If `op` returns an error, then that error is returned directly.////// If the attempt limit specified by `retry` is reached, then/// `Err(ETIMEDOUT)` is returned.////// # Examples////// ```no_run/// use kernel::io::{///     Io,///     Mmio,///     poll::read_poll_timeout_atomic, ///// };/// use kernel::time::Delta;////// const HW_READY: u16 = 0x01;////// fn wait_for_hardware<const SIZE: usize>(io: &Mmio<SIZE>) -> Result {///     read_poll_timeout_atomic(///         // The `op` closure reads the value of a specific status register.///         || io.try_read16(0x1000),///         // The `cond` closure takes a reference to the value returned by `op`///         // and checks whether the hardware is ready.///         |val: &u16| *val == HW_READY,///         Delta::from_micros(50),///         1000,///     )?;///     Ok(())/// }/// ```pub fn read_poll_timeout_atomic<Op, Cond, T>(    mut op: Op,    mut cond: Cond,    delay_delta: Delta,    retry: usize,) -> Result<T>where    Op: FnMut() -> Result<T>,    Cond: FnMut(&T) -> bool,{    for _ in 0..retry {        let val = op()?;        if cond(&val) {            return Ok(val);        }         if !delay_delta.is_zero() {            udelay(delay_delta);        }         cpu_relax();    }     Err(ETIMEDOUT)}