#![no_std]
#![allow(clippy::new_without_default)]
#![feature(iter_array_chunks, let_chains, maybe_uninit_slice)]

extern crate alloc;

use alloc::sync::Arc;
use controller::Xhci;
use device_api::{device::Device, dma::DmaAllocator, interrupt::InterruptAffinity};
use regs::Regs;
use ygg_driver_pci::{
    capability::{DevicePowerState, PowerManagementCapability},
    device::{PciDeviceInfo, PreferredInterruptMode},
    macros::pci_driver,
    PciCommandRegister, PciConfigurationSpace,
};
use yggdrasil_abi::error::Error;

mod context;
mod controller;
mod device;
mod pipe;
mod regs;
mod ring;
mod util;

// Only x86-64 supports xHCI currently, because only it has MSI-X implemented
pci_driver! {
    matches: [class (0x0C:0x03:0x30)],
    driver: {
        fn driver_name(&self) -> &str {
            "xhci"
        }

        fn probe(&self, info: &PciDeviceInfo, dma: &Arc<dyn DmaAllocator>) -> Result<Arc<dyn Device>, Error> {
            // TODO Chip Hardware Reset
            let bar0 = info
                .config_space
                .bar(0)
                .expect("xHCI doesn't have BAR0 configured")
                .as_memory()
                .expect("xHCI's BAR0 is not memory-type");

            if let Some(power) = info.config_space.capability::<PowerManagementCapability>() {
                log::info!("xHC has power management capability");
                let power_state = power.get_device_power_state();

                if power_state != DevicePowerState::D0 {
                    power.set_device_power_state(DevicePowerState::D0);
                }
                // Enable PME# signal generation
                power.set_pme_en(true);
            }

            let mut cmd = PciCommandRegister::from_bits_retain(info.config_space.command());
            cmd &= !(PciCommandRegister::DISABLE_INTERRUPTS | PciCommandRegister::ENABLE_IO);
            cmd |= PciCommandRegister::ENABLE_MEMORY | PciCommandRegister::BUS_MASTER;
            info.config_space.set_command(cmd.bits());

            info.init_interrupts(PreferredInterruptMode::Msi(true))?;

            let regs = Regs::map(bar0)?;
            let xhci = Arc::new(Xhci::new(dma.clone(), info.clone(), regs)?);

            info.map_interrupt(InterruptAffinity::Any, xhci.clone())?;

            Ok(xhci)
        }
    }
}
