From df54e6ea6dd0da1b17fd7a8193d1d7f47a132273 Mon Sep 17 00:00:00 2001 From: uchouT Date: Wed, 9 Sep 2026 15:48:14 +0000 Subject: [PATCH] refactor(server): define the USB device announce type Backends received ironrdp-rdpeusb's DeviceAnnounce, so implementing UsbRedirDevice meant naming RDPEUSB wire types. UsbDeviceAnnounce reports the bus speed as ironrdp-usb's UsbSpeed and drops the Windows USBDI capability fields, leaving the facade free of RDPEUSB vocabulary. Signed-off-by: uchouT --- crates/ironrdp-rdpeusb/src/pdu/sink.rs | 11 ++++++ crates/ironrdp-server/src/lib.rs | 6 ++-- crates/ironrdp-server/src/urbdrc.rs | 47 ++++++++++++++++++++++++-- 3 files changed, 58 insertions(+), 6 deletions(-) diff --git a/crates/ironrdp-rdpeusb/src/pdu/sink.rs b/crates/ironrdp-rdpeusb/src/pdu/sink.rs index 49609bdfdf..0a97b8551a 100644 --- a/crates/ironrdp-rdpeusb/src/pdu/sink.rs +++ b/crates/ironrdp-rdpeusb/src/pdu/sink.rs @@ -15,6 +15,7 @@ use ironrdp_dvc::DvcEncode; use ironrdp_pdu::utils::strict_sum; use ironrdp_str::multi_sz::MultiSzString; use ironrdp_str::prefixed::Cch32String; +use ironrdp_usb::UsbSpeed; use crate::pdu::header::{FunctionId, InterfaceId, Mask, MessageId, SharedMsgHeader}; @@ -410,6 +411,16 @@ impl DeviceSpeed { } } +impl From for UsbSpeed { + fn from(value: DeviceSpeed) -> Self { + if value == DeviceSpeed::FULL_SPEED { + Self::Full + } else { + Self::High + } + } +} + #[repr(transparent)] #[derive(Debug, PartialEq, Clone, Copy)] pub struct NoAckIsochWriteJitterBufSizeInMs(u32); diff --git a/crates/ironrdp-server/src/lib.rs b/crates/ironrdp-server/src/lib.rs index 4dffa3f4b4..f7197a9bb6 100644 --- a/crates/ironrdp-server/src/lib.rs +++ b/crates/ironrdp-server/src/lib.rs @@ -41,7 +41,7 @@ pub use ironrdp_acceptor::Acceptor; pub use ironrdp_pdu::rdp::server_error_info::ErrorInfo; pub use ironrdp_pdu::rdp::session_info::ServerAutoReconnect; #[cfg(feature = "usb")] -pub use ironrdp_rdpeusb::io::{CompletionData, DeviceAnnounce, DeviceText, InternalIoControlPacket}; +pub use ironrdp_rdpeusb::io::{CompletionData, DeviceText, InternalIoControlPacket}; pub use rdpdr::{NoopRdpdrServerBackend, RdpdrServerBackend, RdpdrServerFactory, RdpdrServerMessage}; pub use rdpei::{ CsReadyFlags, CsReadyPdu, DismissHoveringTouchContactPdu, PenContact, PenContactDataFlags, PenContactFields, @@ -58,8 +58,8 @@ pub use server::{ pub use sound::{RdpsndServerHandler, RdpsndServerMessage, SoundServerFactory}; #[cfg(feature = "usb")] pub use urbdrc::{ - CompletionFut, DeviceFactory, PendingHandle, PendingRequest, RdpUsbDeviceAnnounceInfo, UsbDeviceHandle, - UsbRedirDevice, UsbRequestCompletion, + CompletionFut, DeviceFactory, PendingHandle, PendingRequest, RdpUsbDeviceAnnounceInfo, UsbDeviceAnnounce, + UsbDeviceHandle, UsbRedirDevice, UsbRequestCompletion, }; #[cfg(feature = "__bench")] pub mod bench { diff --git a/crates/ironrdp-server/src/urbdrc.rs b/crates/ironrdp-server/src/urbdrc.rs index d16c295083..216f6b13b0 100644 --- a/crates/ironrdp-server/src/urbdrc.rs +++ b/crates/ironrdp-server/src/urbdrc.rs @@ -23,7 +23,7 @@ use ironrdp_rdpeusb::{ server::{UrbdrcControlServerBackend, UrbdrcDeviceServerBackend}, }; use ironrdp_usb::{ - InterfaceSelection, TransferType, + InterfaceSelection, TransferType, UsbSpeed, control::GetDescriptorRequest, descriptor::{ConfigurationDescriptorSet, InterfaceDescriptor, ValidConfigurationDescriptorSet}, endpoint::EndpointAddress, @@ -1425,6 +1425,8 @@ pub(crate) struct UsbRedirServer { pub trait UsbRedirDevice: Send { /// Called when the client announces the device with `ADD_DEVICE`. + /// + /// This is the first callback, and it is invoked exactly once. fn device_added(&mut self, info: RdpUsbDeviceAnnounceInfo); fn device_text(&mut self, device_text: DeviceText); @@ -1440,9 +1442,48 @@ pub trait UsbRedirDevice: Send { fn close(&mut self) {} } +/// A redirected USB device as the client announced it. +#[derive(Debug, Clone)] +#[non_exhaustive] +pub struct UsbDeviceAnnounce { + /// Identity the client assigned to the redirected device. + /// + /// It is opaque to the server and stable for the lifetime of the device + /// channel, which makes it usable as a key for per-device state. + pub device_instance_id: String, + /// Device identifiers reported by the client, most specific first. + pub hw_ids: Vec, + /// Identifiers of the device classes the client reports the device is + /// compatible with, most specific first. + pub compat_ids: Vec, + /// Identifier shared by every function of the same physical device, which + /// groups the announcements of a composite device. + pub container_id: String, + /// Bus speed the client reports the device is operating at. + /// + /// RDPEUSB reports only whether the device is operating at high speed, so + /// this is always either [`UsbSpeed::High`] or [`UsbSpeed::Full`]. A + /// low-speed device is announced as full speed, because the protocol has no + /// encoding to distinguish the two. + pub speed: UsbSpeed, +} + +impl From for UsbDeviceAnnounce { + fn from(value: DeviceAnnounce) -> Self { + Self { + device_instance_id: value.device_instance_id, + hw_ids: value.hw_ids, + compat_ids: value.compat_ids, + container_id: value.container_id, + speed: value.usb_device_caps.device_speed.into(), + } + } +} + +/// A device announcement together with the handle which drives that device. #[derive(Debug)] pub struct RdpUsbDeviceAnnounceInfo { - pub announce: DeviceAnnounce, + pub announce: UsbDeviceAnnounce, pub usb_handle: UsbDeviceHandle, } @@ -1508,7 +1549,7 @@ impl UsbRedirServer { impl UrbdrcDeviceServerBackend for UsbRedirServer { fn add_device(&mut self, device: DeviceAnnounce) -> PduResult<()> { self.device.device_added(RdpUsbDeviceAnnounceInfo { - announce: device, + announce: device.into(), usb_handle: self.handle.clone(), }); Ok(())