blob: 0bffb72f7409107d826b314125f3297b256f6622 [file]
// Copyright 2016 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
use crate::object::{
HandleValue, InterruptDispatcher, InterruptEventDispatcher, PortDispatcher, ProcessDispatcher,
VirtualInterruptDispatcher, validate_resource_irq,
};
use crate::user_copy::UserOutPtr;
use debug::ltracef;
use syscalls_macro::syscall;
use zx_status::Status;
use zx_types::{
ZX_INTERRUPT_BIND, ZX_INTERRUPT_UNBIND, ZX_INTERRUPT_VIRTUAL, ZX_INTERRUPT_WAKE_VECTOR,
ZX_RIGHT_READ, ZX_RIGHT_SIGNAL, ZX_RIGHT_WAIT, ZX_RIGHT_WRITE, zx_time_t,
};
const LOCAL_TRACE: u32 = 0;
// zx_status_t zx_interrupt_create
#[syscall]
pub fn sys_interrupt_create(
src_obj: HandleValue,
src_num: u32,
options: u32,
out_handle: &mut HandleValue,
) -> Result<(), Status> {
ltracef!("options {:#x}\n", options);
// The IRQ resource is required for all non-virtual interrupts, and virtual
// interrupts which are configured as wake vectors.
if (options & ZX_INTERRUPT_VIRTUAL) == 0 || (options & ZX_INTERRUPT_WAKE_VECTOR) != 0 {
validate_resource_irq(src_obj, src_num)?;
}
let (handle, rights) = if (options & ZX_INTERRUPT_VIRTUAL) != 0 {
VirtualInterruptDispatcher::create(options)?
} else {
InterruptEventDispatcher::create(src_num, options, false)?
};
*out_handle = handle.make_and_add_handle(rights)?;
Ok(())
}
// zx_status_t zx_interrupt_bind
#[syscall]
pub fn sys_interrupt_bind(
handle: HandleValue,
port_handle: HandleValue,
key: u64,
options: u32,
) -> Result<(), Status> {
ltracef!("handle {:x}\n", handle.raw_value());
if options != ZX_INTERRUPT_BIND && options != ZX_INTERRUPT_UNBIND {
return Err(Status::INVALID_ARGS);
}
let up = ProcessDispatcher::get_current();
let interrupt = up.handle_table().get_dispatcher_with_rights::<InterruptDispatcher>(
&up,
handle,
ZX_RIGHT_READ,
)?;
let port = up.handle_table().get_dispatcher_with_rights::<PortDispatcher>(
&up,
port_handle,
ZX_RIGHT_WRITE,
)?;
if !port.can_bind_to_interrupt() {
return Err(Status::WRONG_TYPE);
}
if options == ZX_INTERRUPT_BIND { interrupt.bind(port, key) } else { interrupt.unbind(&port) }
}
// zx_status_t zx_interrupt_ack
#[syscall]
pub fn sys_interrupt_ack(inth: HandleValue) -> Result<(), Status> {
ltracef!("handle {:x}\n", inth.raw_value());
let up = ProcessDispatcher::get_current();
let interrupt = up.handle_table().get_dispatcher_with_rights::<InterruptDispatcher>(
&up,
inth,
ZX_RIGHT_WRITE,
)?;
interrupt.ack()
}
// zx_status_t zx_interrupt_wait
#[syscall]
pub fn sys_interrupt_wait(
handle: HandleValue,
out_timestamp: UserOutPtr<zx_time_t>,
) -> Result<(), Status> {
ltracef!("handle {:x}\n", handle.raw_value());
let up = ProcessDispatcher::get_current();
let interrupt = up.handle_table().get_dispatcher_with_rights::<InterruptDispatcher>(
&up,
handle,
ZX_RIGHT_WAIT,
)?;
let timestamp = interrupt.wait_for_interrupt()?;
if !out_timestamp.is_null() {
out_timestamp.write(timestamp)?;
}
Ok(())
}
// zx_status_t zx_interrupt_destroy
#[syscall]
pub fn sys_interrupt_destroy(handle: HandleValue) -> Result<(), Status> {
ltracef!("handle {:x}\n", handle.raw_value());
let up = ProcessDispatcher::get_current();
let interrupt = up.handle_table().get_dispatcher::<InterruptDispatcher>(&up, handle)?;
interrupt.destroy()
}
// zx_status_t zx_interrupt_trigger
#[syscall]
pub fn sys_interrupt_trigger(
handle: HandleValue,
options: u32,
timestamp: zx_time_t,
) -> Result<(), Status> {
ltracef!("handle {:x}\n", handle.raw_value());
if options != 0 {
return Err(Status::INVALID_ARGS);
}
let up = ProcessDispatcher::get_current();
let interrupt = up.handle_table().get_dispatcher_with_rights::<InterruptDispatcher>(
&up,
handle,
ZX_RIGHT_SIGNAL,
)?;
interrupt.trigger(timestamp)
}