use alloc::sync::Arc;
use axdevice_base::DeviceResult;
use super::{ActivityPermit, QueueNotifyOutcome};
use crate::pci::{InterruptTransition, VirtioPciInterruptCoordinator};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(super) enum InterruptPublicationKind {
Queue,
Configuration,
}
pub struct QueueNotification {
pub(super) outcome: QueueNotifyOutcome,
pub(super) publication: InterruptPublicationRequest,
}
impl QueueNotification {
pub const fn outcome(&self) -> QueueNotifyOutcome {
self.outcome
}
pub const fn requires_interrupt_publication(&self) -> bool {
self.publication.requires_irq_permit()
}
pub const fn generation(&self) -> Option<VirtioQueueGeneration> {
self.publication.generation()
}
pub fn complete(self) {
self.publication.cancel();
}
pub fn publish<F>(self, publish_transition: F) -> DeviceResult
where
F: FnMut(InterruptTransition) -> DeviceResult,
{
self.publication.publish(publish_transition)
}
pub fn into_interrupt_publication(self) -> InterruptPublicationRequest {
self.publication
}
}
pub struct InterruptPublicationRequest {
kind: Option<InterruptPublicationKind>,
activity: Option<ActivityPermit>,
interrupts: Arc<VirtioPciInterruptCoordinator>,
}
impl core::fmt::Debug for InterruptPublicationRequest {
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
formatter
.debug_struct("InterruptPublicationRequest")
.field("kind", &self.kind)
.field("has_activity", &self.activity.is_some())
.finish_non_exhaustive()
}
}
impl InterruptPublicationRequest {
pub(super) fn new(
interrupts: Arc<VirtioPciInterruptCoordinator>,
kind: Option<InterruptPublicationKind>,
activity: Option<ActivityPermit>,
) -> Self {
Self {
kind,
activity,
interrupts,
}
}
pub const fn requires_irq_permit(&self) -> bool {
self.kind.is_some()
}
pub const fn generation(&self) -> Option<VirtioQueueGeneration> {
match &self.activity {
Some(activity) => Some(activity.generation),
None => None,
}
}
pub fn publish<F>(mut self, mut publish_transition: F) -> DeviceResult
where
F: FnMut(InterruptTransition) -> DeviceResult,
{
let Some(kind) = self.kind.take() else {
self.activity.take();
return Ok(());
};
let mut transition = match kind {
InterruptPublicationKind::Queue => self.interrupts.record_queue_completion(true),
InterruptPublicationKind::Configuration => self.interrupts.record_config_change(),
};
loop {
if let Err(error) = publish_transition(transition) {
self.interrupts.complete_transition(transition, false);
self.activity.take();
return Err(error);
}
transition = self.interrupts.complete_transition(transition, true);
if transition == InterruptTransition::None {
self.activity.take();
return Ok(());
}
}
}
pub fn cancel(mut self) {
self.activity.take();
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct InterruptTransitionIntent {
transition: InterruptTransition,
generation: VirtioQueueGeneration,
}
impl InterruptTransitionIntent {
pub(super) const fn new(
transition: InterruptTransition,
generation: VirtioQueueGeneration,
) -> Self {
Self {
transition,
generation,
}
}
pub const fn transition(self) -> InterruptTransition {
self.transition
}
pub const fn generation(self) -> VirtioQueueGeneration {
self.generation
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct VirtioQueueGeneration(pub(super) u64);
impl VirtioQueueGeneration {
pub const fn from_value(value: u64) -> Self {
Self(value)
}
pub const fn value(self) -> u64 {
self.0
}
}
pub struct InterruptTransitionRequest {
transition: InterruptTransition,
activity: Option<ActivityPermit>,
interrupts: Arc<VirtioPciInterruptCoordinator>,
}
impl core::fmt::Debug for InterruptTransitionRequest {
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
formatter
.debug_struct("InterruptTransitionRequest")
.field("transition", &self.transition)
.field("has_activity", &self.activity.is_some())
.finish_non_exhaustive()
}
}
impl InterruptTransitionRequest {
pub(super) fn new(
interrupts: Arc<VirtioPciInterruptCoordinator>,
transition: InterruptTransition,
activity: Option<ActivityPermit>,
) -> Self {
Self {
transition,
activity,
interrupts,
}
}
pub(super) fn without_activity(
interrupts: Arc<VirtioPciInterruptCoordinator>,
transition: InterruptTransition,
) -> Self {
Self::new(interrupts, transition, None)
}
pub const fn transition(&self) -> InterruptTransition {
self.transition
}
}
impl Drop for InterruptTransitionRequest {
fn drop(&mut self) {
self.interrupts.cancel_transition(self.transition);
self.activity.take();
}
}