blob: 760e3c571256e66e33002de4be50c31b25e7a16e [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! Type-safe bindings for Zircon interrupts.
use crate::{
ok, sys, AsHandleRef, BootTimeline, Handle, HandleBased, HandleRef, Instant, MonotonicTimeline,
Port, Status, Timeline,
};
use std::marker::PhantomData;
/// An object representing a Zircon interrupt.
///
/// As essentially a subtype of `Handle`, it can be freely interconverted.
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[repr(transparent)]
pub struct Interrupt<K = RealInterruptKind, T = BootTimeline>(Handle, PhantomData<(K, T)>);
pub trait InterruptKind: private::Sealed {}
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct VirtualInterruptKind;
impl InterruptKind for VirtualInterruptKind {}
impl private::Sealed for VirtualInterruptKind {}
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct RealInterruptKind;
impl InterruptKind for RealInterruptKind {}
impl private::Sealed for RealInterruptKind {}
pub type VirtualInterrupt = Interrupt<VirtualInterruptKind>;
impl<K: InterruptKind, T: Timeline> Interrupt<K, T> {
/// Bind the given port with the given key.
///
/// Wraps [zx_interrupt_bind](https://fuchsia.dev/reference/syscalls/interrupt_bind).
pub fn bind_port(&self, port: &Port, key: u64) -> Result<(), Status> {
let options = sys::ZX_INTERRUPT_BIND;
// SAFETY: This is a basic FFI call.
let status =
unsafe { sys::zx_interrupt_bind(self.raw_handle(), port.raw_handle(), key, options) };
ok(status)
}
/// Acknowledge the interrupt.
///
/// Wraps [zx_interrupt_ack](https://fuchsia.dev/reference/syscalls/interrupt_ack).
pub fn ack(&self) -> Result<(), Status> {
// SAFETY: This is a basic FFI call.
let status = unsafe { sys::zx_interrupt_ack(self.raw_handle()) };
ok(status)
}
}
pub trait InterruptTimeline: Timeline {
const CREATE_FLAGS: u32;
}
impl InterruptTimeline for MonotonicTimeline {
const CREATE_FLAGS: u32 = sys::ZX_INTERRUPT_TIMESTAMP_MONO;
}
impl InterruptTimeline for BootTimeline {
const CREATE_FLAGS: u32 = 0;
}
impl<T: InterruptTimeline> Interrupt<VirtualInterruptKind, T> {
/// Create a virtual interrupt.
///
/// Wraps [zx_interrupt_create](https://fuchsia.dev/reference/syscalls/interrupt_create).
pub fn create_virtual() -> Result<Self, Status> {
// SAFETY: We are sure that the handle has a valid address.
let handle = unsafe {
let mut handle = sys::ZX_HANDLE_INVALID;
ok(sys::zx_interrupt_create(
sys::ZX_HANDLE_INVALID,
T::CREATE_FLAGS,
sys::ZX_INTERRUPT_VIRTUAL,
&mut handle,
))?;
Handle::from_raw(handle)
};
Ok(Interrupt(handle, PhantomData))
}
/// Triggers a virtual interrupt object.
///
/// Wraps [zx_interrupt_trigger](https://fuchsia.dev/reference/syscalls/interrupt_trigger).
pub fn trigger(&self, time: Instant<T>) -> Result<(), Status> {
// SAFETY: this is a basic FFI call.
let status = unsafe { sys::zx_interrupt_trigger(self.raw_handle(), 0, time.into_nanos()) };
ok(status)
}
}
impl<K: InterruptKind, T: Timeline> AsHandleRef for Interrupt<K, T> {
fn as_handle_ref(&self) -> HandleRef<'_> {
self.0.as_handle_ref()
}
}
impl<K: InterruptKind, T: Timeline> From<Handle> for Interrupt<K, T> {
fn from(handle: Handle) -> Self {
Interrupt::<K, T>(handle, PhantomData)
}
}
impl<K: InterruptKind, T: Timeline> From<Interrupt<K, T>> for Handle {
fn from(x: Interrupt<K, T>) -> Handle {
x.0
}
}
impl<K: InterruptKind> HandleBased for Interrupt<K> {}
mod private {
pub trait Sealed {}
}
#[cfg(test)]
mod tests {
use crate::{
BootInstant, Handle, Interrupt, MonotonicInstant, MonotonicTimeline, Port, PortOptions,
Status, VirtualInterrupt, VirtualInterruptKind,
};
#[test]
fn bind() {
let interrupt: Interrupt = Handle::invalid().into();
let port = Port::create_with_opts(PortOptions::BIND_TO_INTERRUPT);
let key = 1;
let result = interrupt.bind_port(&port, key);
assert_eq!(result.err(), Some(Status::BAD_HANDLE));
}
#[test]
fn ack() {
let interrupt: Interrupt = Handle::invalid().into();
let result = interrupt.ack();
assert_eq!(result.err(), Some(Status::BAD_HANDLE));
}
#[test]
fn trigger() {
let interrupt = VirtualInterrupt::create_virtual().unwrap();
let result = interrupt.trigger(BootInstant::from_nanos(10));
assert_eq!(result, Ok(()));
}
#[test]
fn trigger_monotimeline() {
let interrupt =
Interrupt::<VirtualInterruptKind, MonotonicTimeline>::create_virtual().unwrap();
let result = interrupt.trigger(MonotonicInstant::from_nanos(10));
assert_eq!(result, Ok(()));
}
}