extern crate alloc;
use alloc::format;
use ax_driver_base::DeviceType;
use ax_driver_block::BlockDriverOps;
use ax_driver_virtio::Transport;
use ax_plat::mem::PhysAddr;
use rdrive::{
DriverGeneric, PlatformDevice, module_driver, probe::OnProbeError, register::FdtInfo,
};
use super::PlatformDeviceBlock;
use crate::drivers::{iomap, virtio::VirtIoHalImpl};
pub(super) type VirtIoBlkDevice<T> = ax_driver_virtio::VirtIoBlkDev<VirtIoHalImpl, T>;
module_driver!(
name: "Virtio Block",
level: ProbeLevel::PostKernel,
priority: ProbePriority::DEFAULT,
probe_kinds: &[
ProbeKind::Fdt {
compatibles: &["virtio,mmio"],
on_probe: probe
}
],
);
fn probe(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError> {
let base_reg = info
.node
.regs()
.into_iter()
.next()
.ok_or(OnProbeError::other(alloc::format!(
"[{}] has no reg",
info.node.name()
)))?;
let mmio_size = base_reg.size.unwrap_or(0x1000) as usize;
let mmio_base = PhysAddr::from_usize(base_reg.address as usize);
let mmio_base = iomap(mmio_base, mmio_size)?.as_ptr();
let (ty, transport) =
ax_driver_virtio::probe_mmio_device(mmio_base, mmio_size).ok_or(OnProbeError::NotMatch)?;
if ty != DeviceType::Block {
return Err(OnProbeError::NotMatch);
}
let dev = VirtIoBlkDevice::try_new(transport).map_err(|e| {
OnProbeError::other(format!(
"failed to initialize Virtio Block device at [PA:{mmio_base:?},): {e:?}"
))
})?;
register_virtio_block(plat_dev, dev);
debug!("virtio block device registered successfully");
Ok(())
}
pub(super) fn register_virtio_block<T: Transport + 'static>(
plat_dev: PlatformDevice,
dev: VirtIoBlkDevice<T>,
) {
plat_dev.register_block(BlockDevice { dev: Some(dev) });
}
struct BlockDevice<T: Transport + 'static> {
dev: Option<VirtIoBlkDevice<T>>,
}
struct BlockQueue<T: Transport + 'static> {
raw: VirtIoBlkDevice<T>,
}
impl<T: Transport + 'static> DriverGeneric for BlockDevice<T> {
fn name(&self) -> &str {
"virtio-blk"
}
}
impl<T: Transport + 'static> rd_block::Interface for BlockDevice<T> {
fn create_queue(&mut self) -> Option<alloc::boxed::Box<dyn rd_block::IQueue>> {
self.dev
.take()
.map(|dev| alloc::boxed::Box::new(BlockQueue { raw: dev }) as _)
}
fn enable_irq(&mut self) {
todo!()
}
fn disable_irq(&mut self) {
todo!()
}
fn is_irq_enabled(&self) -> bool {
false
}
fn handle_irq(&mut self) -> rd_block::Event {
rd_block::Event::none()
}
}
impl<T: Transport + 'static> rd_block::IQueue for BlockQueue<T> {
fn num_blocks(&self) -> usize {
self.raw.num_blocks() as _
}
fn block_size(&self) -> usize {
self.raw.block_size()
}
fn id(&self) -> usize {
0
}
fn buff_config(&self) -> rd_block::BuffConfig {
rd_block::BuffConfig {
dma_mask: u64::MAX,
align: 0x1000,
size: self.block_size(),
}
}
fn submit_request(
&mut self,
request: rd_block::Request<'_>,
) -> Result<rd_block::RequestId, rd_block::BlkError> {
let id = request.block_id;
match request.kind {
rd_block::RequestKind::Read(mut buffer) => {
self.raw
.read_block(id as _, &mut buffer)
.map_err(map_dev_err_to_blk_err)?;
Ok(rd_block::RequestId::new(0))
}
rd_block::RequestKind::Write(items) => {
self.raw
.write_block(id as _, items)
.map_err(map_dev_err_to_blk_err)?;
Ok(rd_block::RequestId::new(0))
}
}
}
fn poll_request(&mut self, _request: rd_block::RequestId) -> Result<(), rd_block::BlkError> {
Ok(())
}
}
fn map_dev_err_to_blk_err(err: ax_driver_base::DevError) -> rd_block::BlkError {
match err {
ax_driver_base::DevError::Again => rd_block::BlkError::Retry,
ax_driver_base::DevError::AlreadyExists => {
rd_block::BlkError::Other("Already exists".into())
}
ax_driver_base::DevError::BadState => {
rd_block::BlkError::Other("Bad internal state".into())
}
ax_driver_base::DevError::InvalidParam => {
rd_block::BlkError::Other("Invalid parameter".into())
}
ax_driver_base::DevError::Io => rd_block::BlkError::Other("I/O error".into()),
ax_driver_base::DevError::NoMemory => rd_block::BlkError::NoMemory,
ax_driver_base::DevError::ResourceBusy => rd_block::BlkError::Other("Resource busy".into()),
ax_driver_base::DevError::Unsupported => rd_block::BlkError::NotSupported,
}
}