Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cspell.yml
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ words:
- mtrrs
- nestedfv
- nocapture
- noerror
- nologo
- nomem
- noopt
Expand Down
2 changes: 2 additions & 0 deletions sdk/patina/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ pub mod serial;
pub mod tpl_mutex;
#[cfg(any(test, feature = "alloc"))]
pub mod uefi_protocol;
#[cfg(any(test, feature = "alloc"))]
pub mod vendor_protocols;

/// Re-export of the [`safe-mmio`](https://crates.io/crates/safe-mmio) crate.
///
Expand Down
1 change: 1 addition & 0 deletions sdk/patina/src/uefi_protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
pub mod decompress;
pub mod performance_measurement;
pub mod status_code;
pub mod usb_io;

use crate::BinaryGuid;

Expand Down
145 changes: 145 additions & 0 deletions sdk/patina/src/uefi_protocol/usb_io.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
//! USB IO protocol FFI types and descriptor structures.
//!
//! Defines the `EFI_USB_IO_PROTOCOL` interface and associated USB descriptor
//! types needed to interact with USB devices.
//!
//! The underlying protocol is defined in the UEFI Specification version 2.11,
//! Chapter 17.2.4 (USB I/O Protocol).
//!
//! ## License
//!
//! Copyright (c) Microsoft Corporation.
//!
//! SPDX-License-Identifier: Apache-2.0
//!
pub mod types;

use core::ffi::c_void;

use r_efi::efi;

use self::types::{
EfiUsbConfigDescriptor, EfiUsbDataDirection, EfiUsbDeviceDescriptor, EfiUsbDeviceRequest, EfiUsbEndpointDescriptor,
EfiUsbInterfaceDescriptor,
};

/// USB IO protocol GUID: 2B2F68D6-0CD2-44CF-8E8B-BBA20B1B5B75
pub const USB_IO_PROTOCOL_GUID: crate::BinaryGuid =
crate::BinaryGuid::from_string("2B2F68D6-0CD2-44CF-8E8B-BBA20B1B5B75");

/// Callback for async USB transfers (interrupt and isochronous).
pub type EfiAsyncUsbTransferCallback =
unsafe extern "efiapi" fn(data: *mut c_void, data_length: usize, context: *mut c_void, status: u32) -> efi::Status;

/// The EFI_USB_IO_PROTOCOL FFI interface.
///
/// Provides services to manage and communicate with USB devices.
#[repr(C)]
pub struct EfiUsbIoProtocol {
/// Manages a USB device with a control transfer pipe. A control transfer is
/// typically used to perform device initialization and configuration.
pub usb_control_transfer: unsafe extern "efiapi" fn(
this: *const EfiUsbIoProtocol,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the way this is defined in the UEFI Specification, I don't think we can consider it *const here.

typedef
EFI_STATUS
(EFIAPI *EFI_USB_IO_CONTROL_TRANSFER) (
  IN EFI_USB_IO_PROTOCOL           *This,
  IN EFI_USB_DEVICE_REQUEST        *Request,
  IN EFI_USB_DATA_DIRECTION        Direction,
  IN UINT32                        Timeout,
  IN OUT VOID                      *Data OPTIONAL,
  IN UINTN                         DataLength OPTIONAL,
  OUT UINT32                       *Status
  );

request: *const EfiUsbDeviceRequest,
direction: EfiUsbDataDirection,
timeout: u32,
data: *mut c_void,
data_length: usize,
status: *mut u32,
) -> efi::Status,
/// Manages a USB device with the bulk transfer pipe. Bulk Transfers are
/// typically used to transfer large amounts of data to/from USB devices.
pub usb_bulk_transfer: unsafe extern "efiapi" fn(
this: *const EfiUsbIoProtocol,
device_endpoint: u8,
data: *mut c_void,
data_length: *mut usize,
timeout: usize,
status: *mut u32,
) -> efi::Status,
/// Manages a USB device with an asynchronous interrupt transfer pipe.
/// Typically used to query a device's status at a fixed rate. For example,
/// keyboard, mouse, and hub devices use this type of transfer to query
/// their interrupt endpoints at a fixed rate.
pub usb_async_interrupt_transfer: unsafe extern "efiapi" fn(
this: *const EfiUsbIoProtocol,
device_endpoint: u8,
is_new_transfer: efi::Boolean,
polling_interval: usize,
data_length: usize,
callback: Option<EfiAsyncUsbTransferCallback>,
context: *mut c_void,
) -> efi::Status,
/// Manages a USB device with a synchronous interrupt transfer pipe. Unlike
/// `usb_async_interrupt_transfer`, this executes only once. Once it returns,
/// regardless of status, the interrupt request is deleted.
pub usb_sync_interrupt_transfer: unsafe extern "efiapi" fn(
this: *const EfiUsbIoProtocol,
device_endpoint: u8,
data: *mut c_void,
data_length: *mut usize,
timeout: usize,
status: *mut u32,
) -> efi::Status,
/// Manages a USB device with an isochronous transfer pipe. Isochronous
/// transfers are typically used to transfer streaming data.
pub usb_isochronous_transfer: unsafe extern "efiapi" fn(
this: *const EfiUsbIoProtocol,
device_endpoint: u8,
data: *mut c_void,
data_length: usize,
status: *mut u32,
) -> efi::Status,
/// Manages a USB device with an asynchronous isochronous transfer pipe.
/// A nonblocking USB isochronous transfer.
pub usb_async_isochronous_transfer: unsafe extern "efiapi" fn(
this: *const EfiUsbIoProtocol,
device_endpoint: u8,
data: *mut c_void,
data_length: usize,
isochronous_callback: EfiAsyncUsbTransferCallback,
context: *mut c_void,
) -> efi::Status,
/// Retrieves the USB Device Descriptor.
pub usb_get_device_descriptor: unsafe extern "efiapi" fn(
this: *const EfiUsbIoProtocol,
device_descriptor: *mut EfiUsbDeviceDescriptor,
) -> efi::Status,
/// Retrieves the USB Device Configuration Descriptor.
pub usb_get_config_descriptor: unsafe extern "efiapi" fn(
this: *const EfiUsbIoProtocol,
config_descriptor: *mut EfiUsbConfigDescriptor,
) -> efi::Status,
/// Retrieves the Interface Descriptor for a USB Device Controller.
pub usb_get_interface_descriptor: unsafe extern "efiapi" fn(
this: *const EfiUsbIoProtocol,
interface_descriptor: *mut EfiUsbInterfaceDescriptor,
) -> efi::Status,
/// Retrieves an Endpoint Descriptor within a USB Controller.
pub usb_get_endpoint_descriptor: unsafe extern "efiapi" fn(
this: *const EfiUsbIoProtocol,
endpoint_index: u8,
endpoint_descriptor: *mut EfiUsbEndpointDescriptor,
) -> efi::Status,
/// Retrieves a Unicode string stored in a USB Device.
pub usb_get_string_descriptor: unsafe extern "efiapi" fn(
this: *const EfiUsbIoProtocol,
lang_id: u16,
string_id: u8,
string: *mut *mut u16,
) -> efi::Status,
/// Retrieves all the language ID codes that the USB device supports.
pub usb_get_supported_languages: unsafe extern "efiapi" fn(
this: *const EfiUsbIoProtocol,
lang_id_table: *mut *mut u16,
table_size: *mut u16,
) -> efi::Status,

/// Resets and reconfigures the USB controller.
pub usb_port_reset: unsafe extern "efiapi" fn(this: *const EfiUsbIoProtocol) -> efi::Status,
}

// SAFETY: EfiUsbIoProtocol is a C-compatible struct whose layout matches the USB IO GUID interface.
unsafe impl crate::uefi_protocol::ProtocolInterface for EfiUsbIoProtocol {
const PROTOCOL_GUID: crate::BinaryGuid = USB_IO_PROTOCOL_GUID;
}
177 changes: 177 additions & 0 deletions sdk/patina/src/uefi_protocol/usb_io/types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
//! USB descriptor structures, types, and constants.
//!
//! ## License
//!
//! Copyright (c) Microsoft Corporation.
//!
//! SPDX-License-Identifier: Apache-2.0
//!

/// Direction of USB data transfer.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(C)]
pub enum EfiUsbDataDirection {
/// Data flows from device to host.
DataIn = 0,
/// Data flows from host to device.
DataOut = 1,
/// No data phase.
NoData = 2,
}

/// USB device request structure (SETUP packet).
#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct EfiUsbDeviceRequest {
/// Identifies the characteristics of the specific request.
pub request_type: u8,
/// Specifies the particular request.
pub request: u8,
/// Request-specific parameter.
pub value: u16,
/// Request-specific index.
pub index: u16,
/// Number of bytes to transfer if there is a data stage.
pub length: u16,
}

/// USB descriptor type for configuration.
pub const USB_DESC_TYPE_CONFIG: u8 = 2;
/// USB descriptor type for interface.
pub const USB_DESC_TYPE_INTERFACE: u8 = 4;

/// USB endpoint transfer type mask.
pub const USB_ENDPOINT_XFER_TYPE_MASK: u8 = 0x03;
/// USB endpoint type: interrupt.
pub const USB_ENDPOINT_INTERRUPT: u8 = 0x03;
/// USB endpoint direction: IN.
pub const USB_ENDPOINT_DIR_IN: u8 = 0x80;

/// No USB error.
pub const EFI_USB_NOERROR: u32 = 0x00;
/// USB transfer was not executed.
pub const EFI_USB_ERR_NOTEXECUTE: u32 = 0x01;
/// USB stall error (endpoint halted).
pub const EFI_USB_ERR_STALL: u32 = 0x02;
/// USB data buffer error.
pub const EFI_USB_ERR_BUFFER: u32 = 0x04;
/// USB babble error (device sent more data than expected).
pub const EFI_USB_ERR_BABBLE: u32 = 0x08;
/// USB NAK error (device not ready).
pub const EFI_USB_ERR_NAK: u32 = 0x10;
/// USB CRC error.
pub const EFI_USB_ERR_CRC: u32 = 0x20;
/// USB timeout error.
pub const EFI_USB_ERR_TIMEOUT: u32 = 0x40;
/// USB bit stuffing error.
pub const EFI_USB_ERR_BITSTUFF: u32 = 0x80;
/// USB system error.
pub const EFI_USB_ERR_SYSTEM: u32 = 0x100;

/// USB device descriptor.
#[derive(Debug, Clone, Copy, Default)]
#[repr(C)]
pub struct EfiUsbDeviceDescriptor {
/// Size of this descriptor in bytes.
pub length: u8,
/// Descriptor type (USB_DESC_TYPE_DEVICE).
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

USB_DESC_TYPE_DEVICE is not defined with the other USB descriptor types above (CONFIG and INTERFACE are currrently there). Since it's referenced here, it might be worth including for clarity. Were the other types (1, 3, 5) excluded intentionally?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

initially excluded because they weren't needed for the implementation; but agree they should be added to fully define the protocol

pub descriptor_type: u8,
/// USB specification release number in BCD.
pub bcd_usb: u16,
/// Class code (assigned by the USB-IF).
pub device_class: u8,
/// Subclass code (assigned by the USB-IF).
pub device_sub_class: u8,
/// Protocol code (assigned by the USB-IF).
pub device_protocol: u8,
/// Maximum packet size for endpoint zero.
pub max_packet_size0: u8,
/// Vendor ID (assigned by the USB-IF).
pub id_vendor: u16,
/// Product ID (assigned by the manufacturer).
pub id_product: u16,
/// Device release number in BCD.
pub bcd_device: u16,
/// Index of string descriptor describing manufacturer.
pub str_manufacturer: u8,
/// Index of string descriptor describing product.
pub str_product: u8,
/// Index of string descriptor describing device serial number.
pub str_serial_number: u8,
/// Number of possible configurations.
pub num_configurations: u8,
}

/// USB interface descriptor.
#[derive(Debug, Clone, Copy, Default)]
#[repr(C)]
pub struct EfiUsbInterfaceDescriptor {
/// Size of this descriptor in bytes.
pub length: u8,
/// Descriptor type (interface).
pub descriptor_type: u8,
/// Number of this interface.
pub interface_number: u8,
/// Value used to select this alternate setting.
pub alternate_setting: u8,
/// Number of endpoints used by this interface.
pub num_endpoints: u8,
/// Class code assigned by the USB-IF.
pub interface_class: u8,
/// Sub-class code assigned by the USB-IF.
pub interface_sub_class: u8,
/// Protocol code assigned by the USB-IF.
pub interface_protocol: u8,
/// Index of string descriptor describing this interface.
pub interface: u8,
}

/// USB endpoint descriptor.
#[derive(Debug, Clone, Copy, Default)]
#[repr(C)]
pub struct EfiUsbEndpointDescriptor {
/// Size of this descriptor in bytes.
pub length: u8,
/// Descriptor type (endpoint).
pub descriptor_type: u8,
/// The address of the endpoint on the USB device.
pub endpoint_address: u8,
/// Endpoint attributes (transfer type, sync type, usage type).
pub attributes: u8,
/// Maximum packet size this endpoint is capable of sending or receiving.
pub max_packet_size: u16,
/// Interval for polling endpoint for data transfers.
pub interval: u8,
}

/// USB configuration descriptor.
#[derive(Debug, Clone, Copy, Default)]
#[repr(C)]
pub struct EfiUsbConfigDescriptor {
/// Size of this descriptor in bytes.
pub length: u8,
/// Descriptor type (configuration).
pub descriptor_type: u8,
/// Total length of data returned for this configuration.
pub total_length: u16,
/// Number of interfaces supported by this configuration.
pub num_interfaces: u8,
/// Value to use as an argument to SetConfiguration to select this configuration.
pub configuration_value: u8,
/// Index of string descriptor describing this configuration.
pub configuration: u8,
/// Configuration attributes (self-powered, remote wakeup, etc.).
pub attributes: u8,
/// Maximum power consumption in 2mA units.
pub max_power: u8,
}

/// Common header for USB descriptors.
#[derive(Debug, Clone, Copy)]
#[repr(C, packed)]
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this structure (UsbDescHead) packed while the others are not?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

copilot added it and it was an oversight on my part not to make it consistent with the others. In this case, it should not be needed as packed will result in the same as natural alignment for this struct.

pub struct UsbDescHead {
/// Size of this descriptor in bytes.
pub len: u8,
/// Descriptor type identifier.
pub desc_type: u8,
}
13 changes: 13 additions & 0 deletions sdk/patina/src/vendor_protocols.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//! Vendor-specific UEFI protocol definitions.
//!
//! Protocols in this module are not part of the UEFI or PI specifications but
//! follow the UEFI protocol model. They are defined by platform vendors.
//!
//! ## License
//!
//! Copyright (c) Microsoft Corporation.
//!
//! SPDX-License-Identifier: Apache-2.0
//!

pub mod hid_io;
Loading
Loading