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