// Copyright 2022 The ChromiumOS Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. use base::AsRawDescriptor; use base::AsRawDescriptors; use base::Event; use base::RawDescriptor; use base::Result; /// A structure suitable for implementing edge triggered interrupts in device backends. pub struct IrqEdgeEvent(Event); impl IrqEdgeEvent { pub fn new() -> Result { Event::new().map(IrqEdgeEvent) } pub fn try_clone(&self) -> Result { self.0.try_clone().map(IrqEdgeEvent) } /// Creates an instance of IrqLevelEvent from an existing event. pub fn from_event(trigger_evt: Event) -> IrqEdgeEvent { IrqEdgeEvent(trigger_evt) } pub fn get_trigger(&self) -> &Event { &self.0 } pub fn trigger(&self) -> Result<()> { self.0.signal() } pub fn clear_trigger(&self) { let _ = self.0.wait(); } } /// A structure suitable for implementing level triggered interrupts in device backends. /// /// Level-triggered interrupts require the device to monitor a resample event from the IRQ chip, /// which can be retrieved with [`IrqLevelEvent::get_resample()`]. When the guest OS acknowledges /// the interrupt with an End of Interrupt (EOI) command, the IRQ chip will signal the resample /// event. Each time the resample event is signalled, the device should re-check its state and call /// [`IrqLevelEvent::trigger()`] again if the interrupt should still be asserted. pub struct IrqLevelEvent { /// An event used by the device backend to signal hypervisor/VM about data or new unit /// of work being available. trigger_evt: Event, /// An event used by the hypervisor to signal device backend that it completed processing a unit /// of work and that device should re-raise `trigger_evt` if additional work needs to be done. resample_evt: Event, } impl IrqLevelEvent { pub fn new() -> Result { let trigger_evt = Event::new()?; let resample_evt = Event::new()?; Ok(IrqLevelEvent { trigger_evt, resample_evt, }) } pub fn try_clone(&self) -> Result { let trigger_evt = self.trigger_evt.try_clone()?; let resample_evt = self.resample_evt.try_clone()?; Ok(IrqLevelEvent { trigger_evt, resample_evt, }) } /// Creates an instance of IrqLevelEvent from an existing pair of events. pub fn from_event_pair(trigger_evt: Event, resample_evt: Event) -> IrqLevelEvent { IrqLevelEvent { trigger_evt, resample_evt, } } pub fn get_trigger(&self) -> &Event { &self.trigger_evt } pub fn get_resample(&self) -> &Event { &self.resample_evt } /// Allows backend to inject interrupt (typically into guest). pub fn trigger(&self) -> Result<()> { self.trigger_evt.signal() } /// Allows code servicing interrupt to consume or clear the event. pub fn clear_trigger(&self) { let _ = self.trigger_evt.wait(); } /// Allows code servicing interrupt to signal that processing is done and that the backend /// should go ahead and re-trigger it if there is more work needs to be done. /// Note that typically resampling is signalled not by individual backends, but rather /// by the code implementing interrupt controller. pub fn trigger_resample(&self) -> Result<()> { self.resample_evt.signal() } /// Allows backend to consume or clear the resample event. pub fn clear_resample(&self) { let _ = self.resample_evt.wait(); } } impl AsRawDescriptors for IrqEdgeEvent { fn as_raw_descriptors(&self) -> Vec { vec![self.0.as_raw_descriptor()] } } impl AsRawDescriptors for IrqLevelEvent { fn as_raw_descriptors(&self) -> Vec { vec![ self.trigger_evt.as_raw_descriptor(), self.resample_evt.as_raw_descriptor(), ] } }