use alloc::{boxed::Box, vec::Vec};
use crate::{
BlkError, DeviceInfo, DriverGeneric, IrqHandler, IrqSourceList, OwnedRequest, PollError,
QueueInfo, QueueLimits, Request, RequestId, RequestPoll, RequestStatus, SubmitError,
};
pub type BInterface = Box<dyn Interface>;
pub type BQueue = Box<dyn IQueue>;
pub type BOwnedQueue = Box<dyn IQueueOwned>;
pub type BIrqHandler = Box<dyn IrqHandler>;
pub trait Interface: DriverGeneric {
fn device_info(&self) -> DeviceInfo;
fn queue_limits(&self) -> QueueLimits;
fn create_queue(&mut self) -> Option<BQueue>;
fn create_owned_queue(&mut self) -> Option<QueueHandle> {
None
}
fn enable_irq(&self) {}
fn disable_irq(&self) {}
fn is_irq_enabled(&self) -> bool {
false
}
fn irq_sources(&self) -> IrqSourceList {
Vec::new()
}
fn take_irq_handler(&mut self, _source_id: usize) -> Option<BIrqHandler> {
None
}
}
pub trait CompletionSink {
fn complete(&mut self, request: RequestId, result: Result<(), BlkError>);
}
pub trait IQueueOwned: Send + 'static {
fn id(&self) -> usize;
fn info(&self) -> QueueInfo;
fn submit_request(&mut self, request: OwnedRequest) -> Result<RequestId, SubmitError>;
fn poll_request(&mut self, request: RequestId) -> Result<RequestPoll, PollError>;
fn cancel_request(&mut self, request: RequestId) -> Result<RequestPoll, PollError>;
fn shutdown(&mut self) {}
}
pub struct QueueHandle {
queue: Option<BOwnedQueue>,
}
impl QueueHandle {
pub fn new(queue: BOwnedQueue) -> Self {
Self { queue: Some(queue) }
}
pub fn id(&self) -> usize {
self.queue
.as_ref()
.expect("owned queue handle must contain queue")
.id()
}
pub fn info(&self) -> QueueInfo {
self.queue
.as_ref()
.expect("owned queue handle must contain queue")
.info()
}
pub fn submit_request(&mut self, request: OwnedRequest) -> Result<RequestId, SubmitError> {
self.queue
.as_mut()
.expect("owned queue handle must contain queue")
.submit_request(request)
}
pub fn poll_request(&mut self, request: RequestId) -> Result<RequestPoll, PollError> {
self.queue
.as_mut()
.expect("owned queue handle must contain queue")
.poll_request(request)
}
pub fn cancel_request(&mut self, request: RequestId) -> Result<RequestPoll, PollError> {
self.queue
.as_mut()
.expect("owned queue handle must contain queue")
.cancel_request(request)
}
pub fn shutdown(&mut self) {
if let Some(queue) = self.queue.as_mut() {
queue.shutdown();
}
}
}
impl Drop for QueueHandle {
fn drop(&mut self) {
self.shutdown();
}
}
pub unsafe trait IQueue: Send + 'static {
fn id(&self) -> usize;
fn info(&self) -> QueueInfo;
fn submit_request(&mut self, request: Request<'_>) -> Result<RequestId, BlkError>;
fn poll_request(&mut self, request: RequestId) -> Result<RequestStatus, BlkError>;
fn poll_completions(
&mut self,
requests: &[RequestId],
sink: &mut dyn CompletionSink,
) -> Result<(), BlkError> {
for &request in requests {
match self.poll_request(request) {
Ok(RequestStatus::Pending) => {}
Ok(RequestStatus::Complete) => sink.complete(request, Ok(())),
Err(err) => sink.complete(request, Err(err)),
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use alloc::vec::Vec;
use super::*;
use crate::{DeviceInfo, Event, QueueLimits, RequestOp};
struct NoopIrq {
calls: usize,
}
impl IrqHandler for NoopIrq {
fn handle_irq(&mut self) -> Event {
self.calls += 1;
let mut event = Event::none();
event.queues.insert(1);
event
}
}
struct Queue;
unsafe impl IQueue for Queue {
fn id(&self) -> usize {
1
}
fn info(&self) -> QueueInfo {
QueueInfo {
id: 1,
device: DeviceInfo::new(8, 512),
limits: QueueLimits::simple(512, u64::MAX),
}
}
fn submit_request(&mut self, request: Request<'_>) -> Result<RequestId, BlkError> {
assert!(matches!(request.op, RequestOp::Read | RequestOp::Write));
Ok(RequestId::new(1))
}
fn poll_request(&mut self, _request: RequestId) -> Result<RequestStatus, BlkError> {
Ok(RequestStatus::Complete)
}
}
#[test]
fn block_api_uses_unified_queue_and_irq_events() {
fn assert_queue<T: IQueue>() {}
fn assert_irq_handler<T: IrqHandler>() {}
assert_queue::<Queue>();
assert_irq_handler::<NoopIrq>();
let mut irq = NoopIrq { calls: 0 };
let event = irq.handle_irq();
assert!(event.queues.contains(1));
assert_eq!(irq.calls, 1);
}
#[derive(Default)]
struct RecordingSink {
completions: Vec<(RequestId, Result<(), BlkError>)>,
}
impl CompletionSink for RecordingSink {
fn complete(&mut self, request_id: RequestId, result: Result<(), BlkError>) {
self.completions.push((request_id, result));
}
}
struct BatchQueue;
unsafe impl IQueue for BatchQueue {
fn id(&self) -> usize {
0
}
fn info(&self) -> QueueInfo {
QueueInfo {
id: 0,
device: DeviceInfo::new(8, 512),
limits: QueueLimits::simple(512, u64::MAX),
}
}
fn submit_request(&mut self, _request: Request<'_>) -> Result<RequestId, BlkError> {
Ok(RequestId::new(0))
}
fn poll_request(&mut self, request: RequestId) -> Result<RequestStatus, BlkError> {
match usize::from(request) {
1 => Ok(RequestStatus::Complete),
2 => Ok(RequestStatus::Pending),
3 => Err(BlkError::Io),
_ => Err(BlkError::InvalidRequest),
}
}
}
#[test]
fn default_batch_completion_polls_pending_ids_and_reports_terminal_results() {
let mut queue = BatchQueue;
let mut sink = RecordingSink::default();
let ids = [RequestId::new(1), RequestId::new(2), RequestId::new(3)];
queue.poll_completions(&ids, &mut sink).unwrap();
assert_eq!(
sink.completions,
[
(RequestId::new(1), Ok(())),
(RequestId::new(3), Err(BlkError::Io)),
]
);
}
#[test]
fn simple_queue_limits_are_single_inflight() {
let limits = QueueLimits::simple(512, u64::MAX);
assert_eq!(limits.max_inflight, 1);
}
struct CountingIrq {
count: usize,
}
impl IrqHandler for CountingIrq {
fn handle_irq(&mut self) -> Event {
self.count += 1;
Event::from_queue_bits(self.count as u64)
}
}
#[test]
fn boxed_irq_handler_is_move_only_and_mutable() {
let mut handler: BIrqHandler = Box::new(CountingIrq { count: 0 });
assert_eq!(handler.handle_irq().queues.bits(), 1);
assert_eq!(handler.handle_irq().queues.bits(), 2);
}
struct OwnedQueue;
impl IQueueOwned for OwnedQueue {
fn id(&self) -> usize {
2
}
fn info(&self) -> QueueInfo {
QueueInfo {
id: 2,
device: DeviceInfo::new(8, 512),
limits: QueueLimits::simple(512, u64::MAX),
}
}
fn submit_request(&mut self, request: OwnedRequest) -> Result<RequestId, SubmitError> {
Err(SubmitError::new(BlkError::Retry, request))
}
fn poll_request(&mut self, _request: RequestId) -> Result<RequestPoll, PollError> {
Ok(RequestPoll::Pending)
}
fn cancel_request(&mut self, _request: RequestId) -> Result<RequestPoll, PollError> {
Err(PollError::UnknownRequest)
}
}
#[test]
fn owned_queue_submit_error_returns_request_ownership() {
let mut handle = QueueHandle::new(Box::new(OwnedQueue));
let request = OwnedRequest {
op: RequestOp::Flush,
lba: 0,
block_count: 0,
data: None,
flags: Default::default(),
};
let err = handle.submit_request(request).unwrap_err();
assert_eq!(err.error, BlkError::Retry);
assert!(matches!(err.request().op, RequestOp::Flush));
}
}