1use core::sync::atomic::{AtomicBool, Ordering};
6
7use crate::error::Result;
8use crate::hal;
9use crate::sync::atomic::AtomicU64;
10use crate::sync::once::OnceCell;
11use crate::sync::waiter::ParkedWaiter;
12
13use hal::Machinelike;
14use hal::device_tree::KeyRegistryEntry;
15use hal::gpio::{Edges, Level, Pin, Pull};
16
17pub struct KeyEvent {
18 pub code: u32,
19 pub pressed: bool,
20}
21
22struct KeyState {
23 entry: &'static KeyRegistryEntry,
24 waiter: ParkedWaiter,
25 last_event_mono: AtomicU64,
27 debounce_ticks: u64,
29 latched_pressed: AtomicBool,
31 initialized: AtomicBool,
33}
34
35impl KeyState {
36 fn pin(&self) -> Pin {
37 Pin {
38 port: self.entry.port,
39 line: self.entry.line,
40 }
41 }
42}
43
44static SLOTS: [OnceCell<KeyState>; hal::device_tree::KEY_REGISTRY.len()] =
47 [const { OnceCell::new() }; hal::device_tree::KEY_REGISTRY.len()];
48
49const _: () = {
50 let entries = hal::device_tree::KEY_REGISTRY;
51 let mut i = 0;
52 while i < entries.len() {
53 let slot_i = match hal::gpio::irq_slot_for_line(entries[i].line) {
54 Some(v) => v,
55 None => panic!("gpio-key DT entry references an unmapped GPIO line"),
56 };
57 assert!(
58 slot_i < 64,
59 "gpio-key IRQ slot exceeds u64 dedup-mask width"
60 );
61
62 let mut j = 0;
63 while j < i {
64 if let Some(slot_j) = hal::gpio::irq_slot_for_line(entries[j].line) {
65 if slot_i == slot_j {
66 assert!(
67 entries[i].irq_priority == entries[j].irq_priority,
68 "gpio-keys sharing an IRQ slot must declare the same `osiris,irq-priority`"
69 );
70 }
71 }
72 j += 1;
73 }
74 i += 1;
75 }
76};
77
78pub struct Key {
79 state: &'static KeyState,
80}
81
82impl Key {
83 pub fn open_by_alias(name: &str) -> Result<Self> {
84 let entry = hal::device_tree::key_by_alias(name)
85 .ok_or_else(|| kerr!(ENODEV, "key alias not found: {name}"))?;
86 Self::open_for_node(entry.node)
87 }
88
89 pub fn open_by_label(label: &str) -> Result<Self> {
90 let entry = hal::device_tree::key_by_label(label)
91 .ok_or_else(|| kerr!(ENODEV, "key label not found: {label}"))?;
92 Self::open_for_node(entry.node)
93 }
94
95 pub fn open_by_code(code: u32) -> Result<Self> {
96 let entry = hal::device_tree::key_by_code(code)
97 .ok_or_else(|| kerr!(ENODEV, "key code not found: {code}"))?;
98 Self::open_for_node(entry.node)
99 }
100
101 fn open_for_node(node: usize) -> Result<Self> {
102 for cell in SLOTS.iter() {
103 if let Some(state) = cell.get() {
104 if state.entry.node == node {
105 if !state.initialized.load(Ordering::Acquire) {
106 return Err(kerr!(EIO, "key node {node} init failed"));
107 }
108 return Ok(Self { state });
109 }
110 }
111 }
112 Err(kerr!(ENODEV, "key node {node} not registered"))
113 }
114
115 pub fn code(&self) -> u32 {
116 self.state.entry.code
117 }
118
119 pub fn label(&self) -> &'static str {
120 self.state.entry.label
121 }
122
123 pub fn poll(&self) -> Result<KeyEvent> {
125 let level = hal::gpio::read(self.state.pin())?;
126 Ok(KeyEvent {
127 code: self.state.entry.code,
128 pressed: pressed_from_level(self.state.entry, level),
129 })
130 }
131
132 pub fn wait(&self) -> Result<KeyEvent> {
135 self.state.waiter.park_current()?;
136 Ok(KeyEvent {
137 code: self.state.entry.code,
138 pressed: self.state.latched_pressed.load(Ordering::Acquire),
139 })
140 }
141}
142
143fn pressed_from_level(entry: &KeyRegistryEntry, level: Level) -> bool {
144 let active_low = entry.active_low != 0;
145 (level == Level::High) ^ active_low
146}
147
148extern "C" fn on_edge(_line: u8, ctx: *mut ()) {
149 if ctx.is_null() {
151 return;
152 }
153 let state = unsafe { &*(ctx as *const KeyState) };
155
156 if state.debounce_ticks > 0 {
157 let now = hal::Machine::monotonic_now();
158 let prev = state.last_event_mono.load(Ordering::Acquire);
159 if prev != 0 && now.saturating_sub(prev) < state.debounce_ticks {
162 return;
163 }
164 let stored = if now == 0 { 1 } else { now };
165 state.last_event_mono.store(stored, Ordering::Release);
166 }
167
168 if let Ok(level) = hal::gpio::read(state.pin()) {
169 state
170 .latched_pressed
171 .store(pressed_from_level(state.entry, level), Ordering::Release);
172 }
173
174 state.waiter.wake();
175}
176
177fn debounce_to_ticks(debounce_ms: u32) -> u64 {
178 if debounce_ms == 0 {
179 return 0;
180 }
181 let freq = hal::Machine::monotonic_freq();
182 (debounce_ms as u64).saturating_mul(freq) / 1000
183}
184
185pub fn init() {
186 let entries = hal::device_tree::KEY_REGISTRY;
187 kprintln!("Found {} gpio-key entries", entries.len());
188
189 let mut seen_slots: u64 = 0;
191
192 for (i, entry) in entries.iter().enumerate() {
193 if let Err(e) = init_entry(i, entry, &mut seen_slots) {
194 kprintln!(" Key {}: init failed: {:?}", entry.label, e);
195 }
196 }
197}
198
199fn init_entry(
200 slot_idx: usize,
201 entry: &'static KeyRegistryEntry,
202 seen_slots: &mut u64,
203) -> Result<()> {
204 let slot =
205 hal::gpio::irq_slot_for_line(entry.line).ok_or_else(|| kerr!(EINVAL, "invalid line"))?;
206
207 let state = KeyState {
208 entry,
209 waiter: ParkedWaiter::new(),
210 last_event_mono: AtomicU64::new(0),
211 debounce_ticks: debounce_to_ticks(entry.debounce_ms),
212 latched_pressed: AtomicBool::new(false),
213 initialized: AtomicBool::new(false),
214 };
215 let state_ref: &'static KeyState = SLOTS[slot_idx].set_or_get(state);
216 let pin = state_ref.pin();
217
218 let bit = 1u64 << slot;
219 if *seen_slots & bit == 0 {
220 unsafe { crate::irq::register_irq(slot, hal::gpio::dispatch, None) }?;
221 *seen_slots |= bit;
222 }
223
224 let pull = if entry.active_low != 0 {
225 Pull::Up
226 } else {
227 Pull::Down
228 };
229 hal::gpio::configure_input(pin, pull)?;
230
231 if let Ok(level) = hal::gpio::read(pin) {
232 state_ref
233 .latched_pressed
234 .store(pressed_from_level(entry, level), Ordering::Release);
235 }
236
237 let ctx = state_ref as *const KeyState as *mut ();
238 hal::gpio::register_edge_handler(pin, Edges::BOTH, on_edge, ctx, entry.irq_priority)?;
239
240 state_ref.initialized.store(true, Ordering::Release);
241 kprintln!(
242 " Initialized key {} on port 0x{:x} line {}",
243 entry.label,
244 entry.port,
245 entry.line
246 );
247 Ok(())
248}