1 // SPDX-License-Identifier: GPL-2.0 2 3 //! IO polling. 4 //! 5 //! C header: [`include/linux/iopoll.h`](srctree/include/linux/iopoll.h). 6 7 use crate::{ 8 error::{code::*, Result}, 9 processor::cpu_relax, 10 task::might_sleep, 11 time::{delay::fsleep, Delta, Instant, Monotonic}, 12 }; 13 14 /// Polls periodically until a condition is met, an error occurs, 15 /// or the timeout is reached. 16 /// 17 /// The function repeatedly executes the given operation `op` closure and 18 /// checks its result using the condition closure `cond`. 19 /// 20 /// If `cond` returns `true`, the function returns successfully with 21 /// the result of `op`. Otherwise, it waits for a duration specified 22 /// by `sleep_delta` before executing `op` again. 23 /// 24 /// This process continues until either `op` returns an error, `cond` 25 /// returns `true`, or the timeout specified by `timeout_delta` is 26 /// reached. 27 /// 28 /// This function can only be used in a nonatomic context. 29 /// 30 /// # Errors 31 /// 32 /// If `op` returns an error, then that error is returned directly. 33 /// 34 /// If the timeout specified by `timeout_delta` is reached, then 35 /// `Err(ETIMEDOUT)` is returned. 36 /// 37 /// # Examples 38 /// 39 /// ```no_run 40 /// use kernel::io::{Io, poll::read_poll_timeout}; 41 /// use kernel::time::Delta; 42 /// 43 /// const HW_READY: u16 = 0x01; 44 /// 45 /// fn wait_for_hardware<const SIZE: usize>(io: &Io<SIZE>) -> Result<()> { 46 /// match read_poll_timeout( 47 /// // The `op` closure reads the value of a specific status register. 48 /// || io.try_read16(0x1000), 49 /// // The `cond` closure takes a reference to the value returned by `op` 50 /// // and checks whether the hardware is ready. 51 /// |val: &u16| *val == HW_READY, 52 /// Delta::from_millis(50), 53 /// Delta::from_secs(3), 54 /// ) { 55 /// Ok(_) => { 56 /// // The hardware is ready. The returned value of the `op` closure 57 /// // isn't used. 58 /// Ok(()) 59 /// } 60 /// Err(e) => Err(e), 61 /// } 62 /// } 63 /// ``` 64 #[track_caller] 65 pub fn read_poll_timeout<Op, Cond, T>( 66 mut op: Op, 67 mut cond: Cond, 68 sleep_delta: Delta, 69 timeout_delta: Delta, 70 ) -> Result<T> 71 where 72 Op: FnMut() -> Result<T>, 73 Cond: FnMut(&T) -> bool, 74 { 75 let start: Instant<Monotonic> = Instant::now(); 76 77 // Unlike the C version, we always call `might_sleep()` unconditionally, 78 // as conditional calls are error-prone. We clearly separate 79 // `read_poll_timeout()` and `read_poll_timeout_atomic()` to aid 80 // tools like klint. 81 might_sleep(); 82 83 loop { 84 let val = op()?; 85 if cond(&val) { 86 // Unlike the C version, we immediately return. 87 // We know the condition is met so we don't need to check again. 88 return Ok(val); 89 } 90 91 if start.elapsed() > timeout_delta { 92 // Unlike the C version, we immediately return. 93 // We have just called `op()` so we don't need to call it again. 94 return Err(ETIMEDOUT); 95 } 96 97 if !sleep_delta.is_zero() { 98 fsleep(sleep_delta); 99 } 100 101 // `fsleep()` could be a busy-wait loop so we always call `cpu_relax()`. 102 cpu_relax(); 103 } 104 } 105