blob: c58ae599b80391ca9b6ee1a5b42763a44f3f921f [file] [log] [blame]
// Copyright 2019 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use super::event_ring::{Error as EventRingError, EventRing};
use super::xhci_abi::{
CommandCompletionEventTrb, Error as TrbError, PortStatusChangeEventTrb, TransferEventTrb, Trb,
TrbCast, TrbCompletionCode, TrbType,
};
use super::xhci_regs::*;
use crate::register_space::Register;
use std::fmt::{self, Display};
use sys_util::{Error as SysError, EventFd, GuestAddress, GuestMemory};
#[derive(Debug)]
pub enum Error {
CastTrb(TrbError),
AddEvent(EventRingError),
SetSegTableSize(EventRingError),
SetSegTableBaseAddr(EventRingError),
SendInterrupt(SysError),
}
type Result<T> = std::result::Result<T, Error>;
impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::Error::*;
match self {
CastTrb(e) => write!(f, "cannot cast trb: {}", e),
AddEvent(e) => write!(f, "cannot add event: {}", e),
SetSegTableSize(e) => write!(f, "cannot set seg table size: {}", e),
SetSegTableBaseAddr(e) => write!(f, "cannot set seg table base addr: {}", e),
SendInterrupt(e) => write!(f, "cannot send interrupt: {}", e),
}
}
}
/// See spec 4.17 for interrupters. Controller can send an event back to guest kernel driver
/// through interrupter.
pub struct Interrupter {
interrupt_fd: EventFd,
usbsts: Register<u32>,
iman: Register<u32>,
erdp: Register<u64>,
event_handler_busy: bool,
enabled: bool,
pending: bool,
moderation_interval: u16,
moderation_counter: u16,
event_ring: EventRing,
}
impl Interrupter {
/// Create a new interrupter.
pub fn new(mem: GuestMemory, irq_evt: EventFd, regs: &XhciRegs) -> Self {
Interrupter {
interrupt_fd: irq_evt,
usbsts: regs.usbsts.clone(),
iman: regs.iman.clone(),
erdp: regs.erdp.clone(),
event_handler_busy: false,
enabled: false,
pending: false,
moderation_interval: 0,
moderation_counter: 0,
event_ring: EventRing::new(mem),
}
}
/// Returns true if event ring is empty.
pub fn event_ring_is_empty(&self) -> bool {
self.event_ring.is_empty()
}
/// Add event to event ring.
fn add_event(&mut self, trb: Trb) -> Result<()> {
self.event_ring.add_event(trb).map_err(Error::AddEvent)?;
self.pending = true;
self.interrupt_if_needed()
}
/// Send port status change trb for port.
pub fn send_port_status_change_trb(&mut self, port_id: u8) -> Result<()> {
let mut trb = Trb::new();
let psctrb = trb
.cast_mut::<PortStatusChangeEventTrb>()
.map_err(Error::CastTrb)?;
psctrb.set_port_id(port_id);
psctrb.set_completion_code(TrbCompletionCode::Success);
psctrb.set_trb_type(TrbType::PortStatusChangeEvent);
self.add_event(trb)
}
/// Send command completion trb.
pub fn send_command_completion_trb(
&mut self,
completion_code: TrbCompletionCode,
slot_id: u8,
trb_addr: GuestAddress,
) -> Result<()> {
let mut trb = Trb::new();
let ctrb = trb
.cast_mut::<CommandCompletionEventTrb>()
.map_err(Error::CastTrb)?;
ctrb.set_trb_pointer(trb_addr.0);
ctrb.set_command_completion_parameter(0);
ctrb.set_completion_code(completion_code);
ctrb.set_trb_type(TrbType::CommandCompletionEvent);
ctrb.set_vf_id(0);
ctrb.set_slot_id(slot_id);
self.add_event(trb)
}
/// Send transfer event trb.
pub fn send_transfer_event_trb(
&mut self,
completion_code: TrbCompletionCode,
trb_pointer: u64,
transfer_length: u32,
event_data: bool,
slot_id: u8,
endpoint_id: u8,
) -> Result<()> {
let mut trb = Trb::new();
let event_trb = trb.cast_mut::<TransferEventTrb>().map_err(Error::CastTrb)?;
event_trb.set_trb_pointer(trb_pointer);
event_trb.set_trb_transfer_length(transfer_length);
event_trb.set_completion_code(completion_code);
event_trb.set_event_data(event_data.into());
event_trb.set_trb_type(TrbType::TransferEvent);
event_trb.set_endpoint_id(endpoint_id);
event_trb.set_slot_id(slot_id);
self.add_event(trb)
}
/// Enable/Disable this interrupter.
pub fn set_enabled(&mut self, enabled: bool) -> Result<()> {
usb_debug!("interrupter set enabled {}", enabled);
self.enabled = enabled;
self.interrupt_if_needed()
}
/// Set interrupt moderation.
pub fn set_moderation(&mut self, interval: u16, counter: u16) -> Result<()> {
// TODO(jkwang) Moderation is not implemented yet.
self.moderation_interval = interval;
self.moderation_counter = counter;
self.interrupt_if_needed()
}
/// Set event ring seg table size.
pub fn set_event_ring_seg_table_size(&mut self, size: u16) -> Result<()> {
usb_debug!("interrupter set seg table size {}", size);
self.event_ring
.set_seg_table_size(size)
.map_err(Error::SetSegTableSize)
}
/// Set event ring segment table base address.
pub fn set_event_ring_seg_table_base_addr(&mut self, addr: GuestAddress) -> Result<()> {
usb_debug!("interrupter set table base addr {:#x}", addr.0);
self.event_ring
.set_seg_table_base_addr(addr)
.map_err(Error::SetSegTableBaseAddr)
}
/// Set event ring dequeue pointer.
pub fn set_event_ring_dequeue_pointer(&mut self, addr: GuestAddress) -> Result<()> {
usb_debug!("interrupter set dequeue ptr addr {:#x}", addr.0);
self.event_ring.set_dequeue_pointer(addr);
if addr == self.event_ring.get_enqueue_pointer() {
self.pending = false;
}
self.interrupt_if_needed()
}
/// Set event hander busy.
pub fn set_event_handler_busy(&mut self, busy: bool) -> Result<()> {
usb_debug!("set event handler busy {}", busy);
self.event_handler_busy = busy;
self.interrupt_if_needed()
}
/// Send and interrupt.
pub fn interrupt(&mut self) -> Result<()> {
usb_debug!("sending interrupt");
self.event_handler_busy = true;
self.pending = false;
self.usbsts.set_bits(USB_STS_EVENT_INTERRUPT);
self.iman.set_bits(IMAN_INTERRUPT_PENDING);
self.erdp.set_bits(ERDP_EVENT_HANDLER_BUSY);
self.interrupt_fd.write(1).map_err(Error::SendInterrupt)
}
fn interrupt_if_needed(&mut self) -> Result<()> {
if self.enabled && self.pending && !self.event_handler_busy {
self.interrupt()?;
}
Ok(())
}
}