use std::sync::{Arc, Mutex};
use tokio::runtime;
use crate::device::{
bus::{BusDeviceRef, SingleThreadedBusDevice},
interrupt_line::InterruptLine,
pci::{
config_space::{ConfigSpace, ConfigSpaceBuilder},
constants::xhci::{offset, MAX_INTRS, RUN_BASE},
traits::PciDevice,
},
xhci::{
endpoint_launcher::EndpointLauncher,
port::{get_portli_id, get_portpmsc_id, get_portsc_id, HotplugControl, PortArray},
real_device::CompleteRealDevice,
registers::{UsbcmdRegister, UsbstsRegister},
slot_manager::SlotManager,
},
};
use crate::device::{pci::constants::xhci::capability, xhci::command_ring::CommandRing};
use crate::device::{pci::constants::xhci::OP_BASE, xhci::interrupter::Interrupter};
#[derive(Debug)]
pub struct XhciController<CRD: CompleteRealDevice> {
config_space: Mutex<ConfigSpace>,
interrupter: Interrupter,
port_array: PortArray<CRD>,
command_ring: CommandRing,
slot_manager: SlotManager,
usbcmd: UsbcmdRegister,
usbsts: UsbstsRegister,
}
impl<CRD: CompleteRealDevice> XhciController<CRD> {
pub fn new(dma_bus: BusDeviceRef, async_runtime: runtime::Handle) -> Self {
let usbcmd = UsbcmdRegister::new();
let usbsts = UsbstsRegister::new(usbcmd.value_reference());
let interrupter = Interrupter::new(dma_bus.clone(), &async_runtime);
let port_array = PortArray::new(interrupter.create_event_sender(), async_runtime.clone());
let ep_launch_requester = EndpointLauncher::start(
port_array.create_device_retriever(),
async_runtime.clone(),
dma_bus.clone(),
interrupter.create_event_sender(),
);
let slot_manager = SlotManager::new(dma_bus.clone(), &async_runtime, ep_launch_requester);
let command_ring = CommandRing::new(
dma_bus.clone(),
&async_runtime,
interrupter.create_event_sender(),
slot_manager.create_slot_worker_handle(),
usbcmd.value_reference(),
);
Self {
config_space: Mutex::new(Self::build_config_space()),
interrupter,
port_array,
command_ring,
slot_manager,
usbcmd,
usbsts,
}
}
fn build_config_space() -> ConfigSpace {
use crate::device::pci::constants::config_space::*;
ConfigSpaceBuilder::new(vendor::REDHAT, device::REDHAT_XHCI)
.class(class::SERIAL, subclass::SERIAL_USB, progif::USB_XHCI)
.mem32_nonprefetchable_bar(0, 4 * 0x1000)
.mem32_nonprefetchable_bar(3, 2 * 0x1000)
.msix_capability(MAX_INTRS.try_into().unwrap(), 3, 0, 3, 0x1000)
.config_space()
}
pub fn connect_irq(&self, irq: Arc<dyn InterruptLine>) {
self.interrupter
.set_interrupt_line(irq)
.expect("Interrupter should be alive");
}
pub fn hotplug_control(&self) -> HotplugControl<CRD> {
self.port_array.create_hotplug_control()
}
}
impl<CRD: CompleteRealDevice> PciDevice for XhciController<CRD> {
fn write_cfg(&self, req: crate::device::bus::Request, value: u64) {
self.config_space.lock().unwrap().write(req, value);
}
fn read_cfg(&self, req: crate::device::bus::Request) -> u64 {
self.config_space.lock().unwrap().read(req)
}
fn bar(&self, bar_no: u8) -> Option<super::config_space::BarInfo> {
self.config_space.lock().unwrap().bar(bar_no)
}
fn write_io(&self, region: u32, req: crate::device::bus::Request, value: u64) {
assert_eq!(region, 0);
match req.addr {
offset::USBCMD => self.usbcmd.write(value),
offset::DNCTL => assert_eq!(value, 2, "debug notifications not supported"),
offset::CRCR => self
.command_ring
.control(value)
.expect("command worker should be alive"),
offset::CRCR_HI => assert_eq!(value, 0, "no support for configuration above 4G"),
offset::DCBAAP => self.slot_manager.dcbaap.write(value),
offset::DCBAAP_HI => assert_eq!(value, 0, "no support for configuration above 4G"),
offset::CONFIG => self.slot_manager.config_reg.write(value as u32), offset::USBSTS => {}
offset::IMAN => self.interrupter.registers.interrupt_management.write(value),
offset::IMOD => self
.interrupter
.registers
.interrupt_moderation_interval
.write(value),
offset::ERSTSZ => {
self.interrupter.registers.erst_size.write(value);
}
offset::ERSTBA => self.interrupter.registers.erst_base_address.write(value),
offset::ERSTBA_HI => assert_eq!(value, 0, "no support for configuration above 4G"),
offset::ERDP => self
.interrupter
.registers
.eventring_dequeue_pointer
.write(value),
offset::ERDP_HI => assert_eq!(value, 0, "no support for configuration above 4G"),
offset::DOORBELL_CONTROLLER => self
.command_ring
.doorbell()
.expect("command worker should be alive"),
offset::DOORBELL_DEVICE..offset::DOORBELL_DEVICE_END => {
let slot_id = ((req.addr - offset::DOORBELL_CONTROLLER) / 4) as u8;
self.slot_manager
.doorbell(slot_id, value as u8)
.expect("slot worker should be alive");
}
addr if get_portsc_id(addr).is_some() => {
let port_id = get_portsc_id(addr).unwrap();
self.port_array
.write_portsc(port_id, value)
.expect("event_sender should be alive");
}
addr if get_portpmsc_id(addr).is_some() => {
let port_id = get_portpmsc_id(addr).unwrap();
self.port_array.write_portpmsc(port_id, value as u32);
}
addr => {
todo!("unknown write {}", addr);
}
}
}
fn read_io(&self, region: u32, req: crate::device::bus::Request) -> u64 {
assert_eq!(region, 0);
match req.addr {
offset::CAPLENGTH => OP_BASE,
offset::HCIVERSION => capability::HCIVERSION,
offset::HCSPARAMS1 => capability::HCSPARAMS1,
offset::HCSPARAMS2 => capability::HCSPARAMS2,
offset::HCSPARAMS3 => 0,
offset::HCCPARAMS1 => capability::HCCPARAMS1,
offset::DBOFF => offset::DOORBELL_CONTROLLER,
offset::RTSOFF => RUN_BASE,
offset::HCCPARAMS2 => 0,
offset::SUPPORTED_PROTOCOLS => capability::supported_protocols::CAP_INFO,
offset::SUPPORTED_PROTOCOLS_STRING => capability::USB_STRING,
offset::SUPPORTED_PROTOCOLS_CONFIG => capability::supported_protocols::CONFIG,
offset::SUPPORTED_PROTOCOLS_CONFIG_RESERVED => 0,
offset::SUPPORTED_PROTOCOLS_USB2 => capability::supported_protocols_usb2::CAP_INFO,
offset::SUPPORTED_PROTOCOLS_USB2_STRING => capability::USB_STRING,
offset::SUPPORTED_PROTOCOLS_USB2_CONFIG => capability::supported_protocols_usb2::CONFIG,
offset::SUPPORTED_PROTOCOLS_USB2_CONFIG_RESERVED => 0,
offset::USBCMD => self.usbcmd.read(),
offset::USBSTS => self.usbsts.read(),
offset::DNCTL => 2,
offset::CRCR => self.command_ring.status(),
offset::CRCR_HI => 0,
offset::DCBAAP => self.slot_manager.dcbaap.read(),
offset::DCBAAP_HI => 0,
offset::PAGESIZE => 0x1,
offset::CONFIG => self.slot_manager.config_reg.read() as u64,
offset::IMAN => self.interrupter.registers.interrupt_management.read(),
offset::IMOD => self
.interrupter
.registers
.interrupt_moderation_interval
.read(),
offset::ERSTSZ => self.interrupter.registers.erst_base_address.read(),
offset::ERSTBA => self.interrupter.registers.erst_size.read(),
offset::ERSTBA_HI => 0,
offset::ERDP => self.interrupter.registers.eventring_dequeue_pointer.read(),
offset::ERDP_HI => 0,
offset::DOORBELL_CONTROLLER => 0, offset::DOORBELL_DEVICE..offset::DOORBELL_DEVICE_END => 0,
offset::MFINDEX => 0x0,
addr if get_portsc_id(addr).is_some() => {
let port_id = get_portsc_id(addr).unwrap();
self.port_array.read_portsc(port_id)
}
addr if get_portpmsc_id(addr).is_some() => {
let port_id = get_portpmsc_id(addr).unwrap();
self.port_array.read_portpmsc(port_id) as u64
}
addr if get_portli_id(addr).is_some() => 0,
addr => {
todo!("unknown read {}", addr);
}
}
}
}