acadia/rust/sys/voyageurs/src/xhci/registers/host_controller.rs

505 lines
27 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use core::ptr::NonNull;
use alloc::vec::Vec;
use bitfield_struct::bitfield;
use mammoth::{mem::map_direct_physical_and_leak, sync::Mutex};
use volatile::{VolatilePtr, VolatileRef, map_field};
use crate::xhci::registers::{
HostControllerCapabilities, HostControllerUsbPort, PortStatusAndControl,
};
#[bitfield(u32)]
pub struct UsbCommand {
/// Run/Stop (R/S) RW. Default = 0. 1 = Run. 0 = Stop. When set to a 1, the xHC proceeds with
/// execution of the schedule. The xHC continues execution as long as this bit is set to a 1. When
/// this bit is cleared to 0, the xHC completes any current or queued commands or TDs, and any
/// USB transactions associated with them, then halts.
///
/// Refer to section 5.4.1.1 for more information on how R/S shall be managed.
///
/// The xHC shall halt within 16 ms. after software clears the Run/Stop bit if the above conditions
/// have been met.
///
/// The HCHalted (HCH) bit in the USBSTS register indicates when the xHC has finished its pending
/// pipelined transactions and has entered the stopped state. Software shall not write a 1 to this
/// flag unless the xHC is in the Halted state (i.e. HCH in the USBSTS register is 1). Doing so may
/// yield undefined results. Writing a 0 to this flag when the xHC is in the Running state (i.e. HCH =
/// 0) and any Event Rings are in the Event Ring Full state (refer to section 4.9.4) may result in lost
/// events.
///
/// When this register is exposed by a Virtual Function (VF), this bit only controls the run state of
/// the xHC instance presented by the selected VF. Refer to section 8 for more information.
pub run_stop: bool,
/// Host Controller Reset (HCRST) RW. Default = 0. This control bit is used by software to reset
/// the host controller. The effects of this bit on the xHC and the Root Hub registers are similar to a
/// Chip Hardware Reset.
///
/// When software writes a 1 to this bit, the Host Controller resets its internal pipelines, timers,
/// counters, state machines, etc. to their initial value. Any transaction currently in progress on the
/// USB is immediately terminated. A USB reset shall not be driven on USB2 downstream ports,
/// however a Hot or Warm Reset79 shall be initiated on USB3 Root Hub downstream ports.
///
/// PCI Configuration registers are not affected by this reset. All operational registers, including port
/// registers and port state machines are set to their initial values. Software shall reinitialize the
/// host controller as described in Section 4.2 in order to return the host controller to an
/// operational state.
///
/// This bit is cleared to 0 by the Host Controller when the reset process is complete. Software
/// cannot terminate the reset process early by writing a 0 to this bit and shall not write any xHC
/// Operational or Runtime registers until while HCRST is 1. Note, the completion of the xHC reset
/// process is not gated by the Root Hub port reset process.
///
/// Software shall not set this bit to 1 when the HCHalted (HCH) bit in the USBSTS register is a 0.
/// Attempting to reset an actively running host controller may result in undefined behavior.
///
/// When this register is exposed by a Virtual Function (VF), this bit only resets the xHC instance
/// presented by the selected VF. Refer to section 8 for more information
pub host_controller_reset: bool,
/// Interrupter Enable (INTE) RW. Default = 0. This bit provides system software with a means of
/// enabling or disabling the host system interrupts generated by Interrupters. When this bit is a 1,
/// then Interrupter host system interrupt generation is allowed, e.g. the xHC shall issue an interrupt
/// at the next interrupt threshold if the host system interrupt mechanism (e.g. MSI, MSI-X, etc.) is
/// enabled. The interrupt is acknowledged by a host system interrupt specific mechanism.
///
/// When this register is exposed by a Virtual Function (VF), this bit only enables the set of
/// Interrupters assigned to the selected VF. Refer to section 7.7.2 for more information.
pub interrupter_enable: bool,
/// Host System Error Enable (HSEE) RW. Default = 0. When this bit is a 1, and the HSE bit in
/// the USBSTS register is a 1, the xHC shall assert out-of-band error signaling to the host. The
/// signaling is acknowledged by software clearing the HSE bit. Refer to section 4.10.2.6 for more
/// information.
/// When this register is exposed by a Virtual Function (VF), the effect of the assertion of this bit on
/// the Physical Function (PF0) is determined by the VMM. Refer to section 8 for more information
pub host_system_error_enable: bool,
#[bits(3)]
__: u8,
/// Light Host Controller Reset (LHCRST) RO or RW. Optional normative. Default = 0. If the Light
/// HC Reset Capability (LHRC) bit in the HCCPARAMS1 register is 1, then this flag allows the driver
/// to reset the xHC without affecting the state of the ports.
///
/// A system software read of this bit as 0 indicates the Light Host Controller Reset has completed
/// and it is safe for software to re-initialize the xHC. A software read of this bit as a 1 indicates the
/// Light Host Controller Reset has not yet completed.
///
/// If not implemented, a read of this flag shall always return a 0.
///
/// All registers in the Aux Power well shall maintain the values that had been asserted prior to the
/// Light Host Controller Reset. Refer to section 4.23.1 for more information.
///
/// When this register is exposed by a Virtual Function (VF), this bit only generates a Light Reset to
/// the xHC instance presented by the selected VF, e.g. Disable the VFs device slots and set the
/// associated VF Run bit to Stopped. Refer to section 8 for more information.
pub light_host_controller_reset: bool,
/// Controller Save State (CSS) - RW. Default = 0. When written by software with 1 and HCHalted
/// (HCH) = 1, then the xHC shall save any internal state (that may be restored by a subsequent
/// Restore State operation) and if FSC = '1' any cached Slot, Endpoint, Stream, or other Context
/// information (so that software may save it). When written by software with 1 and HCHalted
/// (HCH) = 0, or written with 0, no Save State operation shall be performed. This flag always
/// returns 0 when read. Refer to the Save State Status (SSS) flag in the USBSTS register for
/// information on Save State completion. Refer to section 4.23.2 for more information on xHC
///
/// Save/Restore operation. Note that undefined behavior may occur if a Save State operation is
/// initiated while Restore State Status (RSS) = 1.
///
/// When this register is exposed by a Virtual Function (VF), this bit only controls saving the state of
/// the xHC instance presented by the selected VF. Refer to section 8 for more information.
pub controller_save_state: bool,
/// Controller Restore State (CRS) - RW. Default = 0. When set to 1, and HCHalted (HCH) = 1,
/// then the xHC shall perform a Restore State operation and restore its internal state. When set to
/// 1 and Run/Stop (R/S) = 1 or HCHalted (HCH) = 0, or when cleared to 0, no Restore State
/// operation shall be performed. This flag always returns 0 when read. Refer to the Restore State
/// Status (RSS) flag in the USBSTS register for information on Restore State completion. Refer to
/// section 4.23.2 for more information. Note that undefined behavior may occur if a Restore State
/// operation is initiated while Save State Status (SSS) = 1.
/// When this register is exposed by a Virtual Function (VF), this bit only controls restoring the state
/// of the xHC instance presented by the selected VF. Refer to section 8 for more information.
pub controller_restore_state: bool,
/// Enable Wrap Event (EWE) - RW. Default = 0. When set to 1, the xHC shall generate a MFINDEX
/// Wrap Event every time the MFINDEX register transitions from 03FFFh to 0. When cleared to 0
/// no MFINDEX Wrap Events are generated. Refer to section 4.14.2 for more information.
///
/// When this register is exposed by a Virtual Function (VF), the generation of MFINDEX Wrap
/// Events to VFs shall be emulated by the VMM.
pub enable_wrap_event: bool,
/// Enable U3 MFINDEX Stop (EU3S) - RW. Default = 0. When set to 1, the xHC may stop the
/// MFINDEX counting action if all Root Hub ports are in the U3, Disconnected, Disabled, or
/// Powered-off state. When cleared to 0 the xHC may stop the MFINDEX counting action if all
/// Root Hub ports are in the Disconnected, Disabled, Training, or Powered-off state. Refer to
/// section 4.14.2 for more information
pub enable_u3_mfindex_stop: bool,
___: bool,
/// CEM Enable (CME) - RW. Default = '0'. When set to '1', a Max Exit Latency Too Large Capability
/// Error may be returned by a Configure Endpoint Command. When cleared to '0', a Max Exit
/// Latency Too Large Capability Error shall not be returned by a Configure Endpoint Command.
/// This bit is Reserved if CMC = 0. Refer to section 4.23.5.2.2 for more information.
pub cem_enable: bool,
/// Extended TBC Enable (ETE). This flag indicates that the host controller implementation is
/// enabled to support Transfer Burst Count (TBC) values greater that 4 in isoch TDs. When this bit
/// is 1, the Isoch TRB TD Size/TBC field presents the TBC value, and the TBC/RsvdZ field is RsvdZ.
/// When this bit is 0, the TDSize/TCB field presents the TD Size value, and the TBC/RsvdZ field
/// presents the TBC value. This bit may be set only if ETC = 1. Refer to section 4.11.2.3 for more
/// information.
pub extended_tbc_enable: bool,
/// Extended TBC TRB Status Enable (TSC_EN). This flag indicates that the host controller
/// implementation is enabled to support ETC_TSC capability. When this is 1, TRBSts field in the
/// TRB updated to indicate if it is last transfer TRB in the TD. This bit may be set only if
/// ETC_TSC=1. Refer to section 4.11.2.3 for more information.
pub extended_tbc_trb_status_enable: bool,
/// VTIO Enable (VTIOE) RW. Default = 0. When set to 1, XHCI HW will enable its VTIO
/// capability and begin to use the information provided via that VTIO Registers to determine its
/// DMA-ID. When cleared to 0, XHCI HW will use the Primary DMA-ID for all accesses. This bit
/// may be set only if VTC = 1.
pub vtio_enable: bool,
#[bits(15)]
____: u16,
}
#[bitfield(u32)]
pub struct UsbStatus {
/// HCHalted (HCH) RO. Default = 1. This bit is a 0 whenever the Run/Stop (R/S) bit is a 1. The
/// xHC sets this bit to 1 after it has stopped executing as a result of the Run/Stop (R/S) bit being
/// cleared to 0, either by software or by the xHC hardware (e.g. internal error).
///
/// If this bit is '1', then SOFs, microSOFs, or Isochronous Timestamp Packets (ITP) shall not be
/// generated by the xHC, and any received Transaction Packet shall be dropped.
///
/// When this register is exposed by a Virtual Function (VF), this bit only reflects the Halted state of
/// the xHC instance presented by the selected VF. Refer to section 8 for more information
#[bits(access=RO)]
pub host_controller_halted: bool,
__: bool,
/// Host System Error (HSE) RW1C. Default = 0. The xHC sets this bit to 1 when a serious error
/// is detected, either internal to the xHC or during a host system access involving the xHC module.
/// (In a PCI system, conditions that set this bit to 1 include PCI Parity error, PCI Master Abort, and
/// PCI Target Abort.) When this error occurs, the xHC clears the Run/Stop (R/S) bit in the USBCMD
/// register to prevent further execution of the scheduled TDs. If the HSEE bit in the USBCMD
/// register is a 1, the xHC shall also assert out-of-band error signaling to the host. Refer to section
/// 4.10.2.6 for more information.
/// When this register is exposed by a Virtual Function (VF), the assertion of this bit affects all VFs
/// and reflects the Host System Error state of the Physical Function (PF0). Refer to section 8 for
/// more information.
pub host_system_error: bool,
/// Event Interrupt (EINT) RW1C. Default = 0. The xHC sets this bit to 1 when the Interrupt
/// Pending (IP) bit of any Interrupter transitions from 0 to 1. Refer to section 7.1.2 for use.
/// Software that uses EINT shall clear it prior to clearing any IP flags. A race condition may occur if
/// software clears the IP flags then clears the EINT flag, and between the operations another IP 0
/// to '1' transition occurs. In this case the new IP transition shall be lost.
/// When this register is exposed by a Virtual Function (VF), this bit is the logical 'OR' of the IP bits
/// for the Interrupters assigned to the selected VF. And it shall be cleared to 0 when all associated
/// interrupter IP bits are cleared, i.e. all the VFs Interrupter Event Ring(s) are empty. Refer to
/// section 8 for more information
pub event_interrupt: bool,
/// Port Change Detect (PCD) RW1C. Default = 0. The xHC sets this bit to a 1 when any port has
/// a change bit transition from a 0 to a 1.
///
/// This bit is allowed to be maintained in the Aux Power well. Alternatively, it is also acceptable
/// that on a D3 to D0 transition of the xHC, this bit is loaded with the OR of all of the PORTSC
/// change bits. Refer to section 4.19.3.
///
/// This bit provides system software an efficient means of determining if there has been Root Hub
/// port activity. Refer to section 4.15.2.3 for more information.
///
/// When this register is exposed by a Virtual Function (VF), the VMM determines the state of this
/// bit as a function of the Root Hub Ports associated with the Device Slots assigned to the selected
/// VF. Refer to section 8 for more information.
pub port_change_detect: bool,
#[bits(3)]
__: u8,
/// Save State Status (SSS) - RO. Default = 0. When the Controller Save State (CSS) flag in the
/// USBCMD register is written with 1 this bit shall be set to 1 and remain 1 while the xHC saves
/// its internal state. When the Save State operation is complete, this bit shall be cleared to 0.
/// Refer to section 4.23.2 for more information.
///
/// When this register is exposed by a Virtual Function (VF), the VMM determines the state of this
/// bit as a function of the saving the state for the selected VF. Refer to section 8 for more
/// information.
#[bits(access=RO)]
pub save_state_status: bool,
/// Restore State Status (RSS) - RO. Default = 0. When the Controller Restore State (CRS) flag in
/// the USBCMD register is written with 1 this bit shall be set to 1 and remain 1 while the xHC
/// restores its internal state. When the Restore State operation is complete, this bit shall be
/// cleared to 0. Refer to section 4.23.2 for more information.
///
/// When this register is exposed by a Virtual Function (VF), the VMM determines the state of this
/// bit as a function of the restoring the state for the selected VF. Refer to section 8 for more
/// information.
#[bits(access=RO)]
pub restore_state_status: bool,
/// Save/Restore Error (SRE) - RW1C. Default = 0. If an error occurs during a Save or Restore
/// operation this bit shall be set to 1. This bit shall be cleared to 0 when a Save or Restore
/// operation is initiated or when written with 1. Refer to section 4.23.2 for more information.
/// When this register is exposed by a Virtual Function (VF), the VMM determines the state of this
/// bit as a function of the Save/Restore completion status for the selected VF. Refer to section 8
/// for more information.
pub save_restore_error: bool,
/// Controller Not Ready (CNR) RO. Default = 1. 0 = Ready and 1 = Not Ready. Software shall
/// not write any Doorbell or Operational register of the xHC, other than the USBSTS register, until
/// CNR = 0. This flag is set by the xHC after a Chip Hardware Reset and cleared when the xHC is
/// ready to begin accepting register writes. This flag shall remain cleared (0) until the next Chip
/// Hardware Reset.
#[bits(access=RO)]
pub controller_not_ready: bool,
/// Host Controller Error (HCE) RO. Default = 0. 0 = No internal xHC error conditions exist and 1
/// = Internal xHC error condition. This flag shall be set to indicate that an internal error condition
/// has been detected which requires software to reset and reinitialize the xHC. Refer to section
/// 4.24.1 for more information.
#[bits(access=RO)]
pub host_controller_error: bool,
#[bits(19)]
__: u32,
}
impl UsbStatus {
// Returns a copy of this object that can be written without overwritting flags that are RW1C.
fn preserving_flags(&self) -> UsbStatus {
self.with_host_system_error(false)
.with_event_interrupt(false)
.with_port_change_detect(false)
.with_save_restore_error(false)
}
}
/// Internal data structure to ensure 64 bit reads and writes.
#[bitfield(u64)]
struct CommandAndStatus {
#[bits(32)]
usb_command: UsbCommand,
#[bits(32)]
usb_status: UsbStatus,
}
impl CommandAndStatus {
fn update_command(&self, f: impl Fn(UsbCommand) -> UsbCommand) -> CommandAndStatus {
CommandAndStatus::new()
.with_usb_command(f(self.usb_command()))
.with_usb_status(self.usb_status().preserving_flags())
}
fn update_status(&self, f: impl Fn(UsbStatus) -> UsbStatus) -> CommandAndStatus {
self.with_usb_status(f(self.usb_status()).preserving_flags())
}
}
#[bitfield(u64)]
struct PageSize {
///Page Size RO. Default = Implementation defined. This field defines the page size supported by
/// the xHC implementation. This xHC supports a page size of 2^(n+12) if bit n is Set. For example, if
/// bit 0 is Set, the xHC supports 4k byte page sizes.
///
/// For a Virtual Function, this register reflects the page size selected in the System Page Size field
/// of the SR-IOV Extended Capability structure. For the Physical Function 0, this register reflects
/// the implementation dependent default xHC page size.
///
/// Various xHC resources reference PAGESIZE to describe their minimum alignment requirements.
///
/// The maximum possible page size is 128M.
#[bits(access=RO)]
page_size: u32,
__: u32,
}
#[bitfield(u64)]
struct DeviceNotificationControl {
__: u32,
/// This register is used by software to enable or disable the reporting of the
/// reception of specific USB Device Notification Transaction Packets. A Notification
/// Enable (Nx, where x = 0 to 15) flag is defined for each of the 16 possible de vice
/// notification types. If a flag is set for a specific notification type, a Device
/// Notification Event shall be generated when the respective notification packet is
/// received. After reset all notifications are disabled. Refer to section 6.4.2.7
device_notification_control: u32,
}
#[bitfield(u64)]
pub struct UsbConfigure {
/// Max Device Slots Enabled (MaxSlotsEn) RW. Default = 0. This field specifies the maximum
/// number of enabled Device Slots. Valid values are in the range of 0 to MaxSlots. Enabled Devices
/// Slots are allocated contiguously. e.g. A value of 16 specifies that Device Slots 1 to 16 are active.
///
/// A value of 0 disables all Device Slots. A disabled Device Slot shall not respond to Doorbell
/// Register references.
///
/// This field shall not be modified by software if the xHC is running (Run/Stop (R/S) = 1)
pub max_device_slots_enabled: u8,
/// U3 Entry Enable (U3E) RW. Default = '0'. When set to '1', the xHC shall assert the PLC flag ('1')
/// when a Root Hub port transitions to the U3 State. Refer to section 4.15.1 for more information.
pub u3_entry_enable: bool,
/// Configuration Information Enable (CIE) - RW. Default = '0'. When set to '1', the software shall
/// initialize the Configuration Value, Interface Number, and Alternate Setting fields in the Input
/// Control Context when it is associated with a Configure Endpoint Command. When this bit is '0',
/// the extended Input Control Context fields are not supported. Refer to section 6.2.5.1 for more
/// information.
pub configuration_information_enable: bool,
#[bits(22)]
__: u32,
// Pad to 64 bits for the purposes of reads and writes.
__: u32,
}
/// XHCI Spec Section 5.4
///
/// The base address of this register space is referred to as Operational Base. The
/// Operational Base shall be Dword aligned and is calculated by adding the value
/// of the Capability Registers Length (CAPLENGTH) register (refer to Section 5.3.1)
/// to the Capability Base address. All registers are multiples of 32 bits in length
#[repr(C)]
#[derive(Copy, Clone)]
pub struct HostControllerOperational {
command_and_status: CommandAndStatus,
page_size: PageSize,
device_notification_control: DeviceNotificationControl,
/// Bit 0: Ring Cycle State (RW)
/// Bit 1: Command Stop (RW1S)
/// Bit 2: Command Abort (RW1S)
/// Bit 3: Command Ring Running (RO)
command_ring_control: u64,
__: u64,
___: u64,
/// The Device Context Base Address Array Pointer Register identifies the base
/// address of the Device Context Base Address Array.
/// The memory structure referenced by this physical memory pointer is assumed to
/// be physically contiguous and 64-byte aligned.
device_context_base_address_array_pointer: u64,
configure: UsbConfigure,
}
const _: () = assert!(size_of::<HostControllerOperational>() == 0x40);
pub struct HostControllerOperationalWrapper {
operational: Mutex<VolatileRef<'static, HostControllerOperational>>,
// TODO: This should maybe be its own structure.
ports: Vec<Mutex<VolatileRef<'static, HostControllerUsbPort>>>,
}
#[allow(dead_code)]
impl HostControllerOperationalWrapper {
pub fn new(mmio_address: usize) -> (Self, HostControllerCapabilities) {
const MAP_SIZE: usize = 0x1000;
let caps_ptr: NonNull<HostControllerCapabilities> =
map_direct_physical_and_leak(mmio_address, MAP_SIZE);
// SAFETY:
// - The pointer is valid.
// - No other thread has access in this block.
let capabilities = unsafe { VolatilePtr::new(caps_ptr).read() };
assert!(
capabilities.cap_params_1.supports_64_bit(),
"We only support 64 bit XHCI"
);
// TODO: I don't think we acutally handle this properly.
// SAFETY: XHCI Spec says that this resides in a single page of memory which we mapped
// above.
//
// BAR0 Size Allocation
// If virtualization is supported, the Capability and Operational Register sets, and
// the Extended Capabilities may reside in a single page of virtual memory,
let cap_length_and_version = capabilities.cap_length_and_version;
let operational_ptr = unsafe {
(caps_ptr.as_ptr() as *mut u8).add(cap_length_and_version.cap_length() as usize)
as *mut HostControllerOperational
};
const PORT_OFFSET: usize = 0x400;
// FIXME: This calculation is cursed.
let ports_addr = unsafe { (operational_ptr as *mut u8).add(PORT_OFFSET) as usize };
let ports_space = MAP_SIZE - cap_length_and_version.cap_length() as usize - PORT_OFFSET;
let max_ports_we_support = ports_space / size_of::<HostControllerUsbPort>();
let max_ports = capabilities.params_1.max_ports();
assert!(
max_ports as usize <= max_ports_we_support,
"TODO: Support more ports."
);
let mut ports = Vec::new();
let ports_addr = ports_addr as *mut HostControllerUsbPort;
for port_index in 0..max_ports {
ports.push(unsafe {
Mutex::new(VolatileRef::new(
NonNull::new(ports_addr.add(port_index as usize)).unwrap(),
))
});
}
let operational = Self {
operational: Mutex::new(unsafe {
VolatileRef::new(NonNull::new(operational_ptr).unwrap())
}),
ports,
};
(operational, capabilities)
}
pub fn read_command(&self) -> UsbCommand {
let locked = self.operational.lock();
let op = locked.as_ptr();
map_field!(op.command_and_status).read().usb_command()
}
pub fn update_command(&self, f: impl Fn(UsbCommand) -> UsbCommand) {
let mut locked = self.operational.lock();
let op = locked.as_mut_ptr();
map_field!(op.command_and_status).update(|c_and_s| c_and_s.update_command(f));
}
pub fn read_status(&self) -> UsbStatus {
let locked = self.operational.lock();
let op = locked.as_ptr();
map_field!(op.command_and_status).read().usb_status()
}
pub fn update_status(&self, f: impl Fn(UsbStatus) -> UsbStatus) {
let mut locked = self.operational.lock();
let op = locked.as_mut_ptr();
map_field!(op.command_and_status).update(|c_and_s| c_and_s.update_status(f));
}
pub fn set_device_context_base_address_array_pointer(&self, pointer: usize) {
let mut locked = self.operational.lock();
let op = locked.as_mut_ptr();
map_field!(op.device_context_base_address_array_pointer).write(pointer as u64);
}
pub fn set_command_ring_dequeue_pointer(&self, pointer: usize, cycle_bit: bool) {
// TODO: Assert that the command ring is not running here.
let mut locked = self.operational.lock();
let op = locked.as_mut_ptr();
map_field!(op.command_ring_control).write(pointer as u64 | cycle_bit as u64);
}
pub fn read_configure(&self) -> UsbConfigure {
let locked = self.operational.lock();
let op = locked.as_ptr();
map_field!(op.configure).read()
}
pub fn update_configure(&self, f: impl Fn(UsbConfigure) -> UsbConfigure) {
let mut locked = self.operational.lock();
let op = locked.as_mut_ptr();
map_field!(op.configure).update(f);
}
pub fn get_port(&self, index: usize) -> HostControllerUsbPort {
self.ports[index].lock().as_ptr().read()
}
pub fn update_port_status(
&self,
index: usize,
f: impl Fn(PortStatusAndControl) -> PortStatusAndControl,
) {
let mut port_ref = self.ports[index].lock();
let ptr = port_ref.as_mut_ptr();
map_field!(ptr.status_and_control).update(f);
}
pub fn num_ports(&self) -> usize {
self.ports.len()
}
}