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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
|
// 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<const SIZE: usize>(io: &Io<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::{poll::read_poll_timeout_atomic, Io};
/// use kernel::time::Delta;
///
/// const HW_READY: u16 = 0x01;
///
/// fn wait_for_hardware<const SIZE: usize>(io: &Io<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)
}
|