Remove the #[interrupt] macro (#1409)

This commit is contained in:
Björn Quentin 2024-04-09 12:11:57 +02:00 committed by GitHub
parent 41aa556764
commit 1fdfc9270e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 39 additions and 223 deletions

View File

@ -34,16 +34,6 @@
//!
//! ## Examples
//!
//! #### `interrupt` macro
//!
//! Requires the `interrupt` feature to be enabled.
//!
//! ```no_run
//! #[interrupt]
//! fn INTR_NAME() {
//! // Interrupt handling code here
//! }
//! ```
//!
//! #### `main` macro
//!
@ -184,187 +174,12 @@ pub fn ram(args: TokenStream, input: TokenStream) -> TokenStream {
output.into()
}
/// Marks a function as an interrupt handler
///
/// Used to handle on of the [interrupts](enum.Interrupt.html).
///
/// When specified between braces (`#[interrupt(example)]`) that interrupt will
/// be used and the function can have an arbitrary name. Otherwise the name of
/// the function must be the name of the interrupt.
///
/// Example usage:
///
/// ```rust
/// #[interrupt]
/// fn GPIO() {
/// // code
/// }
/// ```
///
/// The interrupt context can also be supplied by adding a argument to the
/// interrupt function for example, on Xtensa based chips:
///
/// ```rust
/// fn GPIO(context: &mut xtensa_lx_rt::exception::Context) {
/// // code
/// }
/// ```
#[cfg(feature = "interrupt")]
#[proc_macro_attribute]
pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream {
use std::iter;
use darling::{ast::NestedMeta, Error};
use proc_macro::Span;
use proc_macro2::Ident;
use proc_macro_crate::{crate_name, FoundCrate};
use proc_macro_error::abort;
use quote::quote;
use syn::{
parse::Error as ParseError,
spanned::Spanned,
ItemFn,
Meta,
ReturnType,
Type,
Visibility,
};
use self::interrupt::{check_attr_whitelist, extract_cfgs, WhiteListCaller};
let mut f: ItemFn = syn::parse(input).expect("`#[interrupt]` must be applied to a function");
let attr_args = match NestedMeta::parse_meta_list(args.into()) {
Ok(v) => v,
Err(e) => {
return TokenStream::from(Error::from(e).write_errors());
}
};
if attr_args.len() > 1 {
abort!(
Span::call_site(),
"This attribute accepts zero or 1 arguments"
)
}
let ident = f.sig.ident.clone();
let mut ident_s = &ident.clone();
if attr_args.len() == 1 {
if let NestedMeta::Meta(Meta::Path(x)) = &attr_args[0] {
ident_s = x.get_ident().unwrap();
} else {
abort!(
Span::call_site(),
format!(
"This attribute accepts a string attribute {:?}",
attr_args[0]
)
)
}
}
// XXX should we blacklist other attributes?
if let Err(error) = check_attr_whitelist(&f.attrs, WhiteListCaller::Interrupt) {
return error;
}
let valid_signature = f.sig.constness.is_none()
&& f.vis == Visibility::Inherited
&& f.sig.abi.is_none()
&& f.sig.generics.params.is_empty()
&& f.sig.generics.where_clause.is_none()
&& f.sig.variadic.is_none()
&& match f.sig.output {
ReturnType::Default => true,
ReturnType::Type(_, ref ty) => match **ty {
Type::Tuple(ref tuple) => tuple.elems.is_empty(),
Type::Never(..) => true,
_ => false,
},
}
&& f.sig.inputs.len() <= 1;
if !valid_signature {
return ParseError::new(
f.span(),
"`#[interrupt]` handlers must have signature `[unsafe] fn([&mut Context]) [-> !]`",
)
.to_compile_error()
.into();
}
f.sig.ident = Ident::new(
&format!("__esp_hal_internal_{}", f.sig.ident),
proc_macro2::Span::call_site(),
);
let hal_crate = if cfg!(any(feature = "is-lp-core", feature = "is-ulp-core")) {
crate_name("esp-lp-hal")
} else {
crate_name("esp-hal")
};
let interrupt_in_hal_crate = if let Ok(FoundCrate::Name(ref name)) = hal_crate {
let ident = Ident::new(&name, Span::call_site().into());
quote!( #ident::peripherals::Interrupt::#ident_s )
} else {
quote!( crate::peripherals::Interrupt::#ident_s )
};
f.block.stmts.extend(iter::once(
syn::parse2(quote! {{
// Check that this interrupt actually exists
#interrupt_in_hal_crate;
}})
.unwrap(),
));
let tramp_ident = Ident::new(
&format!("{}_trampoline", f.sig.ident),
proc_macro2::Span::call_site(),
);
let ident = &f.sig.ident;
let (ref cfgs, ref attrs) = extract_cfgs(f.attrs.clone());
let export_name = ident_s.to_string();
let trap_frame_in_hal_crate = if let Ok(FoundCrate::Name(ref name)) = hal_crate {
let ident = Ident::new(&name, Span::call_site().into());
quote!( #ident::trapframe::TrapFrame )
} else {
quote!(crate::trapframe::TrapFrame)
};
let context_call =
(f.sig.inputs.len() == 1).then(|| Ident::new("context", proc_macro2::Span::call_site()));
quote!(
#(#cfgs)*
#(#attrs)*
#[doc(hidden)]
#[export_name = #export_name]
pub unsafe extern "C" fn #tramp_ident(context: &mut #trap_frame_in_hal_crate) {
#ident(
#context_call
)
}
#[inline(always)]
#f
)
.into()
}
/// Mark a function as an interrupt handler.
///
/// Optionally a priority can be specified, e.g. `#[handler("Priority3")]`,
/// "min" and "max" are special values.
/// Optionally a priority can be specified, e.g. `#[handler(priority =
/// esp_hal::interrupt::Priority::Priority2)]`.
///
/// If no priority is given, "Priority::min()" is assumed
/// If no priority is given, `Priority::min()` is assumed
#[cfg(feature = "interrupt")]
#[proc_macro_error::proc_macro_error]
#[proc_macro_attribute]

View File

@ -1,54 +1,55 @@
//! # Interrupt support
//!
//! Interrupt support functionality depends heavily on the features enabled.
//! Usually peripheral drivers offer a mechanism to register your interrupt
//! handler. e.g. the systimer offers `set_interrupt_handler` to register a
//! handler for a specific alarm. Other drivers might take an interrupt handler
//! as an optional parameter to their constructor.
//!
//! The [`enable`] method will map interrupt to a CPU interrupt, and handle the
//! vectoring to the peripheral interrupt, for example `UART0`.
//! This is the preferred way to register handlers.
//!
//! It is also possible, but not recommended, to bind an interrupt directly to a
//! CPU interrupt. This can offer lower latency, at the cost of more complexity
//! in the interrupt handler.
//!
//! We reserve a number of CPU interrupts, which cannot be used; see
//! [`RESERVED_INTERRUPTS`].
//!
//! ## Example
//! ## Example using the peripheral driver to register an interrupt handler
//!
//! ```no_run
//! #[entry]
//! fn main() -> ! {
//! ...
//! critical_section::with(|cs| SWINT.borrow_ref_mut(cs).replace(sw_int));
//!
//! // enable the interrupt
//! interrupt::enable(
//! peripherals::Interrupt::FROM_CPU_INTR0,
//! interrupt::Priority::Priority1,
//! )
//! .unwrap();
//! let mut sw_int = system.software_interrupt_control;
//! critical_section::with(|cs| {
//! sw_int
//! .software_interrupt0
//! .set_interrupt_handler(swint0_handler);
//! SWINT0
//! .borrow_ref_mut(cs)
//! .replace(sw_int.software_interrupt0);
//! });
//!
//! // trigger the interrupt
//! SWINT
//! .borrow_ref_mut(cs)
//! .as_mut()
//! .unwrap()
//! .raise(SoftwareInterrupt::SoftwareInterrupt0);
//!
//! loop {}
//! critical_section::with(|cs| {
//! SWINT0.borrow_ref_mut(cs).as_mut().unwrap().raise();
//! });
//! ...
//! }
//!
//! #[interrupt]
//! fn FROM_CPU_INTR0() {
//! // use the `handler` macro to define a handler, optionally you can set a priority
//! #[handler(priority = esp_hal::interrupt::Priority::Priority2)]
//! fn swint0_handler() {
//! esp_println::println!("SW interrupt0");
//! critical_section::with(|cs| {
//! SWINT
//! .borrow_ref_mut(cs)
//! .as_mut()
//! .unwrap()
//! .reset(SoftwareInterrupt::SoftwareInterrupt0);
//! SWINT0.borrow_ref_mut(cs).as_mut().unwrap().reset();
//! });
//! }
//! ```
//!
//! There are additional ways to register interrupt handlers which are generally
//! only meant to be used in very special situations (mostly internal to the HAL
//! or the supporting libraries). Those are outside the scope of this
//! documentation.
//!
//! It is even possible, but not recommended, to bind an interrupt directly to a
//! CPU interrupt. This can offer lower latency, at the cost of more complexity
//! in the interrupt handler. See the `direct_vectoring.rs` example
//!
//! We reserve a number of CPU interrupts, which cannot be used; see
//! [`RESERVED_INTERRUPTS`].
#[cfg(riscv)]
pub use self::riscv::*;