use proc_macro2::TokenStream as TokenStream2;
use quote::{format_ident, quote};
use rmk_config::{BoardConfig, KeyboardTomlConfig};
use syn::ItemMod;
pub(crate) fn expand_bind_interrupt(keyboard_config: &KeyboardTomlConfig, item_mod: &ItemMod) -> TokenStream2 {
if let Some((_, items)) = &item_mod.content {
items
.iter()
.find_map(|item| {
if let syn::Item::Fn(item_fn) = &item
&& item_fn.attrs.len() == 1
&& let Some(i) = item_fn.attrs[0].meta.path().get_ident()
&& i == "bind_interrupt"
{
let content = &item_fn.block.stmts;
return Some(quote! {
#(#content)*
});
}
None
})
.unwrap_or(bind_interrupt_default(keyboard_config, item_mod))
} else {
bind_interrupt_default(keyboard_config, item_mod)
}
}
pub(crate) fn find_extern_irqs(item_mod: &ItemMod) -> Vec<TokenStream2> {
let mut extern_irqs: Vec<TokenStream2> = Vec::new();
if let Some((_, items)) = &item_mod.content {
items.iter().for_each(|item| {
if let syn::Item::Macro(item_macro) = &item
&& item_macro.mac.path.is_ident("add_interrupt")
{
extern_irqs.push(item_macro.mac.tokens.clone());
}
});
}
extern_irqs
}
pub(crate) fn bind_interrupt_default(keyboard_config: &KeyboardTomlConfig, item_mod: &ItemMod) -> TokenStream2 {
let extern_irqs_vec = find_extern_irqs(item_mod);
let extern_irqs = if extern_irqs_vec.is_empty() {
quote! {}
} else {
quote! {
#(#extern_irqs_vec)*
}
};
let chip = keyboard_config.get_chip_model().unwrap();
let board = keyboard_config.get_board_config().unwrap();
let communication = keyboard_config.get_communication_config().unwrap();
match chip.series {
rmk_config::ChipSeries::Stm32 => {
if let Some(usb_info) = communication.get_usb_info() {
let interrupt_name = format_ident!("{}", usb_info.interrupt_name);
let peripheral_name = format_ident!("{}", usb_info.peripheral_name);
quote! {
use ::embassy_stm32::bind_interrupts;
bind_interrupts!(struct Irqs {
#interrupt_name => ::embassy_stm32::usb::InterruptHandler<::embassy_stm32::peripherals::#peripheral_name>;
#extern_irqs
});
}
} else {
quote! {
#extern_irqs
}
}
}
rmk_config::ChipSeries::Nrf52 => {
let usb_and_clock_interrupt = if let Some(usb_info) = communication.get_usb_info() {
let interrupt_name = format_ident!("{}", usb_info.interrupt_name);
let peripheral_name = format_ident!("{}", usb_info.peripheral_name);
quote! {
#interrupt_name => ::embassy_nrf::usb::InterruptHandler<::embassy_nrf::peripherals::#peripheral_name>;
CLOCK_POWER => ::nrf_sdc::mpsl::ClockInterruptHandler, ::embassy_nrf::usb::vbus_detect::InterruptHandler;
}
} else {
quote! { CLOCK_POWER => ::nrf_sdc::mpsl::ClockInterruptHandler; }
};
let ble_config = communication.get_ble_config().unwrap();
let tx_power = if let Some(pwr) = ble_config.default_tx_power {
quote! { .default_tx_power(#pwr)? }
} else {
quote! {}
};
let use_2m_phy = if ble_config.ble_use_2m_phy.unwrap_or(true) {
quote! { .support_le_2m_phy()? }
} else {
quote! {}
};
let nrf_sdc_config = match board {
BoardConfig::Split(_) => {
let num_peri = board.get_num_periphreal() as u8;
quote! {
::nrf_sdc::Builder::new()?
.support_scan()?
.support_central()?
.support_adv()?
.support_peripheral()?
.support_dle_peripheral()?
.support_dle_central()?
.support_phy_update_central()?
.support_phy_update_peripheral()?
#use_2m_phy
#tx_power
.central_count(#num_peri)?
.peripheral_count(1)?
.buffer_cfg(L2CAP_MTU as u16, L2CAP_MTU as u16, L2CAP_TXQ, L2CAP_RXQ)?
.build(p, rng, mpsl, mem)
}
}
BoardConfig::UniBody(_) => quote! {
::nrf_sdc::Builder::new()?
.support_adv()?
.support_peripheral()?
.support_dle_peripheral()?
.support_phy_update_peripheral()?
#use_2m_phy
#tx_power
.peripheral_count(1)?
.buffer_cfg(L2CAP_MTU as u16, L2CAP_MTU as u16, L2CAP_TXQ, L2CAP_RXQ)?
.build(p, rng, mpsl, mem)
},
};
quote! {
use ::embassy_nrf::bind_interrupts;
bind_interrupts!(struct Irqs {
#usb_and_clock_interrupt
RNG => ::embassy_nrf::rng::InterruptHandler<::embassy_nrf::peripherals::RNG>;
EGU0_SWI0 => ::nrf_sdc::mpsl::LowPrioInterruptHandler;
RADIO => ::nrf_sdc::mpsl::HighPrioInterruptHandler;
TIMER0 => ::nrf_sdc::mpsl::HighPrioInterruptHandler;
RTC0 => ::nrf_sdc::mpsl::HighPrioInterruptHandler;
#extern_irqs
});
#[::embassy_executor::task]
async fn mpsl_task(mpsl: &'static ::nrf_sdc::mpsl::MultiprotocolServiceLayer<'static>) -> ! {
mpsl.run().await
}
const L2CAP_TXQ: u8 = 3;
const L2CAP_RXQ: u8 = 3;
const L2CAP_MTU: usize = 251;
fn build_sdc<'d, const N: usize>(
p: ::nrf_sdc::Peripherals<'d>,
rng: &'d mut ::embassy_nrf::rng::Rng<::embassy_nrf::mode::Async>,
mpsl: &'d ::nrf_sdc::mpsl::MultiprotocolServiceLayer,
mem: &'d mut ::nrf_sdc::Mem<N>,
) -> Result<::nrf_sdc::SoftdeviceController<'d>, ::nrf_sdc::Error> {
#nrf_sdc_config
}
}
}
rmk_config::ChipSeries::Rp2040 => {
let usb_info = communication.get_usb_info().expect("no usb info for the chip");
let interrupt_name = format_ident!("{}", usb_info.interrupt_name);
let peripheral_name = format_ident!("{}", usb_info.peripheral_name);
let (pio0_irq_0, ble_task) = if communication.ble_enabled() {
(
quote! {
PIO0_IRQ_0 => ::embassy_rp::pio::InterruptHandler<::embassy_rp::peripherals::PIO0>;
},
quote! {
#[::embassy_executor::task]
async fn cyw43_task(runner: ::cyw43::Runner<'static, ::embassy_rp::gpio::Output<'static>, ::cyw43_pio::PioSpi<'static, ::embassy_rp::peripherals::PIO0, 0, ::embassy_rp::peripherals::DMA_CH0>>) -> ! {
runner.run().await
}
},
)
} else {
(quote! {}, quote! {})
};
quote! {
use ::embassy_rp::bind_interrupts;
bind_interrupts!(struct Irqs {
#interrupt_name => ::embassy_rp::usb::InterruptHandler<::embassy_rp::peripherals::#peripheral_name>;
#pio0_irq_0
});
#ble_task
}
}
rmk_config::ChipSeries::Esp32 => quote! {},
}
}