// 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, poll::read_poll_timeout}; /// use kernel::time::Delta; /// /// const HW_READY: u16 = 0x01; /// /// fn wait_for_hardware(io: &Io) -> 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( mut op: Op, mut cond: Cond, sleep_delta: Delta, timeout_delta: Delta, ) -> Result where Op: FnMut() -> Result, Cond: FnMut(&T) -> bool, { let start: Instant = 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::{poll::read_poll_timeout_atomic, Io}; /// use kernel::time::Delta; /// /// const HW_READY: u16 = 0x01; /// /// fn wait_for_hardware(io: &Io) -> 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( mut op: Op, mut cond: Cond, delay_delta: Delta, retry: usize, ) -> Result where Op: FnMut() -> Result, 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) }