blob: b87a95e0a8b349992f8d1196dc0e9898c2028c43 [file] [edit]
// Copyright 2026 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 super::{InterruptPolarity, InterruptTriggerMode};
use crate::console_rust::console::{CMD_AVAIL_NORMAL, CmdArgs, static_command};
use core::ffi::{CStr, c_int};
use debug::dprintf;
use zx_status::Status;
#[cfg(target_arch = "aarch64")]
const FIRST_VALID_IRQ: u32 = 32;
#[cfg(not(target_arch = "aarch64"))]
const FIRST_VALID_IRQ: u32 = 0;
const LAST_VALID_IRQ: u32 = super::MAX_INTERRUPTS as u32;
struct IrqInfo {
registered: bool,
trigger_mode: Option<InterruptTriggerMode>,
polarity: Option<InterruptPolarity>,
pending: Option<bool>,
enabled: Option<bool>,
}
impl IrqInfo {
#[allow(clippy::absurd_extreme_comparisons)]
fn valid_irq_num(irq_num: u32) -> bool {
(FIRST_VALID_IRQ..LAST_VALID_IRQ).contains(&irq_num)
}
fn get(irq_num: u32) -> Self {
let (tm, pol) = super::query_interrupt_config(irq_num);
let (pending, enabled) = super::query_interrupt_status(irq_num);
let registered = super::is_interrupt_registered(irq_num);
Self { registered, trigger_mode: tm, polarity: pol, pending, enabled }
}
fn is_valid(&self) -> bool {
self.trigger_mode.is_some() && self.polarity.is_some()
}
fn trigger_mode_str(&self) -> &'static str {
match self.trigger_mode {
Some(InterruptTriggerMode::Edge) => "Edge",
Some(InterruptTriggerMode::Level) => "Level",
None => "Unknown",
}
}
fn polarity_str(&self) -> &'static str {
match (self.trigger_mode, self.polarity) {
(Some(InterruptTriggerMode::Level), Some(InterruptPolarity::High)) => "High",
(Some(InterruptTriggerMode::Level), Some(InterruptPolarity::Low)) => "Low",
(Some(InterruptTriggerMode::Edge), Some(InterruptPolarity::High)) => "Rising",
(Some(InterruptTriggerMode::Edge), Some(InterruptPolarity::Low)) => "Falling",
_ => "Unknown",
}
}
fn pending_str(&self) -> &'static str {
match self.pending {
Some(true) => "True",
Some(false) => "False",
None => "Unknown",
}
}
fn enabled_str(&self) -> &'static str {
match self.enabled {
Some(true) => "True",
Some(false) => "False",
None => "Unknown",
}
}
}
fn usage(ret: Result<(), Status>) -> c_int {
dprintf!(ALWAYS, "Usage: irq <cmd>\n");
dprintf!(ALWAYS, "Valid commands are:\n");
dprintf!(ALWAYS, " help : show this message\n");
dprintf!(ALWAYS, " show (all | reg | pending | enabled | <N>)\n");
dprintf!(
ALWAYS,
" Shows the status of one or more IRQs, depending on the mandatory argument.\n"
);
dprintf!(ALWAYS, " all : Show the status of all IRQs in the system.\n");
dprintf!(
ALWAYS,
" reg : Show the status of all IRQs which have a registered handler.\n"
);
dprintf!(ALWAYS, " pending : Show the status of all IRQs which are currently pending.\n");
dprintf!(ALWAYS, " enabled : Show the status of all IRQs which are currently enabled.\n");
dprintf!(
ALWAYS,
" pendenb : Show the status of all IRQs which are currently pending OR enabled.\n"
);
dprintf!(ALWAYS, " <N> : Show the status IRQ #<N>.\n");
Status::result_into_raw(ret)
}
unsafe fn arg_cstr(arg: &CmdArgs) -> Option<&CStr> {
if arg.arg_str.is_null() { None } else { Some(unsafe { CStr::from_ptr(arg.arg_str) }) }
}
/// Console command handler for `irq`.
///
/// # Safety
///
/// If `argc > 0`, `argv` must point to a valid array of `CmdArgs` structures of length `argc`.
pub unsafe extern "C" fn irq_console_cmd(argc: c_int, argv: *const CmdArgs, _flags: u32) -> c_int {
if argc < 2 || argv.is_null() {
dprintf!(ALWAYS, "No command specified!\n");
return usage(Err(Status::INVALID_ARGS));
}
let slice = unsafe { core::slice::from_raw_parts(argv, argc as usize) };
let subcmd = match unsafe { arg_cstr(&slice[1]) } {
Some(cstr) => cstr,
None => {
dprintf!(ALWAYS, "No command specified!\n");
return usage(Err(Status::INVALID_ARGS));
}
};
if subcmd == c"help" {
return usage(Ok(()));
}
if subcmd != c"show" {
let subcmd_str = subcmd.to_str().unwrap_or("?");
dprintf!(ALWAYS, "Unrecognized command \"{}\"\n", subcmd_str);
return usage(Err(Status::INVALID_ARGS));
}
if argc < 3 {
dprintf!(ALWAYS, "No target specified for \"show\"\n");
return usage(Err(Status::INVALID_ARGS));
}
let target_arg = match unsafe { arg_cstr(&slice[2]) } {
Some(cstr) => cstr,
None => {
dprintf!(ALWAYS, "No target specified for \"show\"\n");
return usage(Err(Status::INVALID_ARGS));
}
};
if target_arg == c"all"
|| target_arg == c"reg"
|| target_arg == c"pending"
|| target_arg == c"enabled"
|| target_arg == c"pendenb"
{
dprintf!(ALWAYS, " ID | Registered | Trigger Mode | Polarity | Pending | Enabled |\n");
dprintf!(ALWAYS, "------+------------+--------------+----------+---------+---------+\n");
for i in FIRST_VALID_IRQ..LAST_VALID_IRQ {
let info = IrqInfo::get(i);
if !info.is_valid() {
continue;
}
let matches = if target_arg == c"all" {
true
} else if target_arg == c"reg" {
info.registered
} else if target_arg == c"pending" {
info.pending.unwrap_or(false)
} else if target_arg == c"enabled" {
info.enabled.unwrap_or(false)
} else if target_arg == c"pendenb" {
info.pending.unwrap_or(false) || info.enabled.unwrap_or(false)
} else {
false
};
if matches {
dprintf!(
ALWAYS,
" {:4} | {:3} | {:7} | {:7} | {:7} | {:7} |\n",
i,
if info.registered { "Yes" } else { "No" },
info.trigger_mode_str(),
info.polarity_str(),
info.pending_str(),
info.enabled_str()
);
}
}
zx_status::sys::ZX_OK
} else {
let target_num = slice[2].arg_uint as u32;
if !IrqInfo::valid_irq_num(target_num) {
let target_str = target_arg.to_str().unwrap_or("?");
dprintf!(ALWAYS, "Invalid IRQ target (\"{}\") for \"show\".\n", target_str);
return usage(Err(Status::INVALID_ARGS));
}
let info = IrqInfo::get(target_num);
dprintf!(ALWAYS, "IRQ : {}\n", target_num);
dprintf!(
ALWAYS,
"State : {}\n",
if info.registered { "Registered" } else { "Unregistered" }
);
dprintf!(ALWAYS, "Trigger Mode : {}\n", info.trigger_mode_str());
dprintf!(ALWAYS, "Polarity : {}\n", info.polarity_str());
dprintf!(ALWAYS, "Pending : {}\n", info.pending_str());
dprintf!(ALWAYS, "Enabled : {}\n", info.enabled_str());
zx_status::sys::ZX_OK
}
}
static_command!(
IRQ_CMD,
c"irq".as_ptr(),
c"IRQ related commands".as_ptr(),
irq_console_cmd,
CMD_AVAIL_NORMAL
);