Skip to main content

zx/
interrupt.rs

1// Copyright 2018 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Type-safe bindings for Zircon interrupts.
6
7use crate::{
8    AsHandleRef, BootTimeline, HandleBased, HandleRef, Instant, MonotonicTimeline, NullableHandle,
9    Port, Status, Timeline, ok, sys,
10};
11use std::marker::PhantomData;
12
13/// An object representing a Zircon interrupt.
14///
15/// As essentially a subtype of `NullableHandle`, it can be freely interconverted.
16#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
17#[repr(transparent)]
18pub struct Interrupt<K = RealInterruptKind, T = BootTimeline>(NullableHandle, PhantomData<(K, T)>);
19
20pub trait InterruptKind: private::Sealed {}
21
22#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
23pub struct VirtualInterruptKind;
24
25impl InterruptKind for VirtualInterruptKind {}
26impl private::Sealed for VirtualInterruptKind {}
27
28#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
29pub struct RealInterruptKind;
30
31impl InterruptKind for RealInterruptKind {}
32impl private::Sealed for RealInterruptKind {}
33
34pub type VirtualInterrupt = Interrupt<VirtualInterruptKind>;
35
36impl<K: InterruptKind, T: Timeline> Interrupt<K, T> {
37    /// Bind the given port with the given key.
38    ///
39    /// Wraps [zx_interrupt_bind](https://fuchsia.dev/reference/syscalls/interrupt_bind).
40    pub fn bind_port(&self, port: &Port, key: u64) -> Result<(), Status> {
41        let options = sys::ZX_INTERRUPT_BIND;
42        // SAFETY: This is a basic FFI call.
43        let status =
44            unsafe { sys::zx_interrupt_bind(self.raw_handle(), port.raw_handle(), key, options) };
45        ok(status)
46    }
47
48    /// Unbinds from a previously bound port.
49    ///
50    /// Wraps [zx_interrupt_bind](https://fuchsia.dev/reference/syscalls/interrupt_bind) with
51    /// ZX_INTERRUPT_UNBIND set.
52    pub fn unbind_port(&self, port: &Port) -> Result<(), Status> {
53        let options = sys::ZX_INTERRUPT_UNBIND;
54        // SAFETY: This is a basic FFI call.
55        // Per the documentation, when unbinding, key is ignored.
56        let status =
57            unsafe { sys::zx_interrupt_bind(self.raw_handle(), port.raw_handle(), 0, options) };
58        ok(status)
59    }
60
61    /// Synchronously wait for the interrupt to be triggered.
62    ///
63    /// Wraps [zx_interrupt_wait](https://fuchsia.dev/reference/syscalls/interrupt_wait).
64    pub fn wait(&self) -> Result<Instant<T>, Status> {
65        let mut timestamp = 0;
66        // SAFETY: We're sure that `timestamp` has a valid address.
67        let status = unsafe { sys::zx_interrupt_wait(self.raw_handle(), &mut timestamp) };
68        ok(status)?;
69        Ok(Instant::from_nanos(timestamp))
70    }
71
72    /// Acknowledge the interrupt.
73    ///
74    /// Wraps [zx_interrupt_ack](https://fuchsia.dev/reference/syscalls/interrupt_ack).
75    pub fn ack(&self) -> Result<(), Status> {
76        // SAFETY: This is a basic FFI call.
77        let status = unsafe { sys::zx_interrupt_ack(self.raw_handle()) };
78        ok(status)
79    }
80
81    /// Destroy the interrupt.
82    ///
83    /// Wraps [zx_interrupt_destroy](https://fuchsia.dev/reference/syscalls/interrupt_destroy).
84    pub fn destroy(self) -> Result<(), Status> {
85        // SAFETY: This is a basic FFI call.
86        let status = unsafe { sys::zx_interrupt_destroy(self.raw_handle()) };
87        ok(status)
88    }
89
90    delegated_concrete_handle_based_impls!(|h| Self(h, PhantomData));
91}
92
93pub trait InterruptTimeline: Timeline {
94    const CREATE_FLAGS: u32;
95}
96
97impl InterruptTimeline for MonotonicTimeline {
98    const CREATE_FLAGS: u32 = sys::ZX_INTERRUPT_TIMESTAMP_MONO;
99}
100
101impl InterruptTimeline for BootTimeline {
102    const CREATE_FLAGS: u32 = 0;
103}
104
105impl<T: InterruptTimeline> Interrupt<VirtualInterruptKind, T> {
106    /// Create a virtual interrupt.
107    ///
108    /// Wraps [zx_interrupt_create](https://fuchsia.dev/reference/syscalls/interrupt_create).
109    pub fn create_virtual() -> Result<Self, Status> {
110        // SAFETY: We are sure that the handle has a valid address.
111        let handle = unsafe {
112            let mut handle = sys::ZX_HANDLE_INVALID;
113            ok(sys::zx_interrupt_create(
114                sys::ZX_HANDLE_INVALID,
115                T::CREATE_FLAGS,
116                sys::ZX_INTERRUPT_VIRTUAL,
117                &mut handle,
118            ))?;
119            NullableHandle::from_raw(handle)
120        };
121        Ok(Interrupt(handle, PhantomData))
122    }
123
124    /// Triggers a virtual interrupt object.
125    ///
126    /// Wraps [zx_interrupt_trigger](https://fuchsia.dev/reference/syscalls/interrupt_trigger).
127    pub fn trigger(&self, time: Instant<T>) -> Result<(), Status> {
128        // SAFETY: this is a basic FFI call.
129        let status = unsafe { sys::zx_interrupt_trigger(self.raw_handle(), 0, time.into_nanos()) };
130        ok(status)
131    }
132}
133
134impl<K: InterruptKind, T: Timeline> AsHandleRef for Interrupt<K, T> {
135    fn as_handle_ref(&self) -> HandleRef<'_> {
136        self.0.as_handle_ref()
137    }
138}
139
140impl<K: InterruptKind, T: Timeline> From<NullableHandle> for Interrupt<K, T> {
141    fn from(handle: NullableHandle) -> Self {
142        Interrupt::<K, T>(handle, PhantomData)
143    }
144}
145
146impl<K: InterruptKind, T: Timeline> From<Interrupt<K, T>> for NullableHandle {
147    fn from(x: Interrupt<K, T>) -> NullableHandle {
148        x.0
149    }
150}
151
152impl<K: InterruptKind, T: Timeline> HandleBased for Interrupt<K, T> {}
153
154mod private {
155    pub trait Sealed {}
156}
157
158#[cfg(test)]
159mod tests {
160    use zx_status::Status;
161
162    use crate::{
163        BootInstant, Interrupt, MonotonicInstant, MonotonicTimeline, Port, PortOptions,
164        VirtualInterrupt, VirtualInterruptKind,
165    };
166
167    #[test]
168    fn bind() {
169        let interrupt = VirtualInterrupt::create_virtual().unwrap();
170        let port = Port::create_with_opts(PortOptions::BIND_TO_INTERRUPT);
171        let key = 1;
172        let result = interrupt.bind_port(&port, key);
173        assert_eq!(result, Ok(()));
174
175        // Can't bind twice.
176        let result = interrupt.bind_port(&port, key);
177        assert_eq!(result, Err(Status::ALREADY_BOUND));
178
179        // ...Unless we unbind first.
180        let result = interrupt.unbind_port(&port);
181        assert_eq!(result, Ok(()));
182        let result = interrupt.bind_port(&port, key);
183        assert_eq!(result, Ok(()));
184    }
185
186    #[test]
187    fn ack() {
188        let interrupt = VirtualInterrupt::create_virtual().unwrap();
189        let result = interrupt.ack();
190        assert_eq!(result.err(), Some(Status::BAD_STATE));
191    }
192
193    #[test]
194    fn trigger() {
195        let interrupt = VirtualInterrupt::create_virtual().unwrap();
196        let result = interrupt.trigger(BootInstant::from_nanos(10));
197        assert_eq!(result, Ok(()));
198    }
199
200    #[test]
201    fn trigger_monotimeline() {
202        let interrupt =
203            Interrupt::<VirtualInterruptKind, MonotonicTimeline>::create_virtual().unwrap();
204        let result = interrupt.trigger(MonotonicInstant::from_nanos(10));
205        assert_eq!(result, Ok(()));
206    }
207
208    #[test]
209    fn wait() {
210        let interrupt = VirtualInterrupt::create_virtual().unwrap();
211        let result = interrupt.trigger(BootInstant::from_nanos(10));
212        assert_eq!(result, Ok(()));
213        let instant = interrupt.wait().expect("wait failed");
214        assert_eq!(instant.into_nanos(), 10);
215    }
216
217    #[test]
218    fn wait_monotimeline() {
219        let interrupt =
220            Interrupt::<VirtualInterruptKind, MonotonicTimeline>::create_virtual().unwrap();
221        let result = interrupt.trigger(MonotonicInstant::from_nanos(10));
222        assert_eq!(result, Ok(()));
223        let instant = interrupt.wait().expect("wait failed");
224        assert_eq!(instant.into_nanos(), 10);
225    }
226
227    #[test]
228    fn destroy() {
229        let interrupt =
230            Interrupt::<VirtualInterruptKind, MonotonicTimeline>::create_virtual().unwrap();
231        assert_eq!(interrupt.destroy(), Ok(()));
232    }
233}