diff --git a/cspell.yml b/cspell.yml index 227f81fa9..576cf5398 100644 --- a/cspell.yml +++ b/cspell.yml @@ -121,6 +121,7 @@ words: - mtrrs - nestedfv - nocapture + - noerror - nologo - nomem - noopt diff --git a/sdk/patina/src/lib.rs b/sdk/patina/src/lib.rs index 7b2a53554..f854e4743 100644 --- a/sdk/patina/src/lib.rs +++ b/sdk/patina/src/lib.rs @@ -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. /// diff --git a/sdk/patina/src/uefi_protocol.rs b/sdk/patina/src/uefi_protocol.rs index 702e56dcc..e4e54d486 100644 --- a/sdk/patina/src/uefi_protocol.rs +++ b/sdk/patina/src/uefi_protocol.rs @@ -10,6 +10,7 @@ pub mod decompress; pub mod performance_measurement; pub mod status_code; +pub mod usb_io; use crate::BinaryGuid; diff --git a/sdk/patina/src/uefi_protocol/usb_io.rs b/sdk/patina/src/uefi_protocol/usb_io.rs new file mode 100644 index 000000000..640f730f6 --- /dev/null +++ b/sdk/patina/src/uefi_protocol/usb_io.rs @@ -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, + 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, + 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; +} diff --git a/sdk/patina/src/uefi_protocol/usb_io/types.rs b/sdk/patina/src/uefi_protocol/usb_io/types.rs new file mode 100644 index 000000000..383de6cda --- /dev/null +++ b/sdk/patina/src/uefi_protocol/usb_io/types.rs @@ -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). + 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)] +pub struct UsbDescHead { + /// Size of this descriptor in bytes. + pub len: u8, + /// Descriptor type identifier. + pub desc_type: u8, +} diff --git a/sdk/patina/src/vendor_protocols.rs b/sdk/patina/src/vendor_protocols.rs new file mode 100644 index 000000000..96a2d4e8f --- /dev/null +++ b/sdk/patina/src/vendor_protocols.rs @@ -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; diff --git a/sdk/patina/src/vendor_protocols/hid_io.rs b/sdk/patina/src/vendor_protocols/hid_io.rs new file mode 100644 index 000000000..4e786fd09 --- /dev/null +++ b/sdk/patina/src/vendor_protocols/hid_io.rs @@ -0,0 +1,76 @@ +//! HidIo protocol FFI definitions. +//! +//! Defines the HidIo protocol interface struct, types, and GUID shared between +//! producers (e.g. `usb_hid`) and consumers (e.g. `uefi_hid`) of the protocol. +//! +//! ## License +//! +//! Copyright (c) Microsoft Corporation. +//! +//! SPDX-License-Identifier: Apache-2.0 +//! + +use core::ffi::c_void; + +use r_efi::efi; + +/// HidIo interface GUID: 3EA93936-6BF4-49D6-AA50-D9F5B9AD8CFF +pub const HID_IO_PROTOCOL_GUID: crate::BinaryGuid = + crate::BinaryGuid::from_string("3EA93936-6BF4-49D6-AA50-D9F5B9AD8CFF"); + +/// HID report types per the HID specification. +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[repr(C)] +pub enum HidReportType { + /// Input report (device to host). + InputReport = 1, + /// Output report (host to device). + OutputReport = 2, + /// Feature report (bidirectional). + Feature = 3, +} + +/// Callback type for receiving asynchronous input reports. +pub type HidIoReportCallback = + unsafe extern "efiapi" fn(report_buffer_size: u16, report_buffer: *mut c_void, context: *mut c_void); + +/// The HID_IO protocol FFI interface. +#[repr(C)] +pub struct HidIoProtocol { + /// Retrieves the HID report descriptor from the device. + pub get_report_descriptor: unsafe extern "efiapi" fn( + this: *const HidIoProtocol, + report_descriptor_size: *mut usize, + report_descriptor_buffer: *mut c_void, + ) -> efi::Status, + /// Retrieves a HID report of the specified type from the device. + pub get_report: unsafe extern "efiapi" fn( + this: *const HidIoProtocol, + report_id: u8, + report_type: HidReportType, + report_buffer_size: usize, + report_buffer: *mut c_void, + ) -> efi::Status, + /// Sends a HID report of the specified type to the device. + pub set_report: unsafe extern "efiapi" fn( + this: *const HidIoProtocol, + report_id: u8, + report_type: HidReportType, + report_buffer_size: usize, + report_buffer: *mut c_void, + ) -> efi::Status, + /// Registers a callback for asynchronous input report notifications. + pub register_report_callback: unsafe extern "efiapi" fn( + this: *const HidIoProtocol, + callback: HidIoReportCallback, + context: *mut c_void, + ) -> efi::Status, + /// Unregisters a previously registered input report callback. + pub unregister_report_callback: + unsafe extern "efiapi" fn(this: *const HidIoProtocol, callback: HidIoReportCallback) -> efi::Status, +} + +// SAFETY: HidIoProtocol is a C-compatible struct whose layout matches the HidIo GUID interface. +unsafe impl crate::uefi_protocol::ProtocolInterface for HidIoProtocol { + const PROTOCOL_GUID: crate::BinaryGuid = HID_IO_PROTOCOL_GUID; +}