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 /// 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 /// } 57 /// ``` 58 #[track_caller] 59 pub fn read_poll_timeout<Op, Cond, T>( 60 mut op: Op, 61 mut cond: Cond, 62 sleep_delta: Delta, 63 timeout_delta: Delta, 64 ) -> Result<T> 65 where 66 Op: FnMut() -> Result<T>, 67 Cond: FnMut(&T) -> bool, 68 { 69 let start: Instant<Monotonic> = Instant::now(); 70 71 // Unlike the C version, we always call `might_sleep()` unconditionally, 72 // as conditional calls are error-prone. We clearly separate 73 // `read_poll_timeout()` and `read_poll_timeout_atomic()` to aid 74 // tools like klint. 75 might_sleep(); 76 77 loop { 78 let val = op()?; 79 if cond(&val) { 80 // Unlike the C version, we immediately return. 81 // We know the condition is met so we don't need to check again. 82 return Ok(val); 83 } 84 85 if start.elapsed() > timeout_delta { 86 // Unlike the C version, we immediately return. 87 // We have just called `op()` so we don't need to call it again. 88 return Err(ETIMEDOUT); 89 } 90 91 if !sleep_delta.is_zero() { 92 fsleep(sleep_delta); 93 } 94 95 // `fsleep()` could be a busy-wait loop so we always call `cpu_relax()`. 96 cpu_relax(); 97 } 98 } 99