1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
|
// SPDX-License-Identifier: GPL-2.0
//! IO polling.
//!
//! C header: [`include/linux/iopoll.h`](srctree/include/linux/iopoll.h).
use crate::{
error::{code::*, Result},
processor::cpu_relax,
task::might_sleep,
time::{delay::fsleep, 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<const SIZE: usize>(io: &Io<SIZE>) -> Result<()> {
/// match 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(_) => {
/// // The hardware is ready. The returned value of the `op` closure
/// // isn't used.
/// Ok(())
/// }
/// Err(e) => Err(e),
/// }
/// }
/// ```
#[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();
}
}
|