From: Colin Braun <colinbrauncl@gmail.com>
To: "Miguel Ojeda" <ojeda@kernel.org>,
"Greg Kroah-Hartman" <gregkh@linuxfoundation.org>,
"Boqun Feng" <boqun@kernel.org>, "Gary Guo" <gary@garyguo.net>,
"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
"Benno Lossin" <lossin@kernel.org>,
"Andreas Hindborg" <a.hindborg@kernel.org>,
"Alice Ryhl" <aliceryhl@google.com>,
"Trevor Gross" <tmgross@umich.edu>,
"Danilo Krummrich" <dakr@kernel.org>,
"Daniel Almeida" <daniel.almeida@collabora.com>,
"Tamir Duberstein" <tamird@kernel.org>,
"Alexandre Courbot" <acourbot@nvidia.com>,
"Onur Özkan" <work@onurozkan.dev>,
"Mauro Carvalho Chehab" <mchehab@kernel.org>,
"Alan Stern" <stern@rowland.harvard.edu>,
"Mathias Nyman" <mathias.nyman@intel.com>
Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org,
linux-usb@vger.kernel.org, linux-media@vger.kernel.org,
Colin Braun <colin.braun.cl@gmail.com>
Subject: [RFC PATCH 2/4] rust: usb: add usb host interface and endpoint abstractions
Date: Sun, 12 Jul 2026 16:07:59 -0500 [thread overview]
Message-ID: <20260712-urb-abstraction-v1-v1-2-9fa011634ead@gmail.com> (raw)
In-Reply-To: <20260712-urb-abstraction-v1-v1-0-9fa011634ead@gmail.com>
The goal of this patch is to create safe USB host interface and endpoint
descriptors necessary for USB driver development. Specifically, this
safely wraps the C side `struct usb_host_interface` and
`struct usb_host_endpoint` types.
Additionally, support for querying and configuring USB interface
altsettings is added to the existing Rust `Interface` and `Device`
types.
The `Device` struct is made public for two reasons:
- Sending control URBs in a way that makes it clear that endpoint 0 is a
shared device endpoint, not specific to an interface.
- Allows for the possiblity of a future driver that binds to the USB
device rather than an interface.
Signed-off-by: Colin Braun <colin.braun.cl@gmail.com>
---
rust/kernel/usb.rs | 194 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 191 insertions(+), 3 deletions(-)
diff --git a/rust/kernel/usb.rs b/rust/kernel/usb.rs
index 3ae9c05cd32a..21dddc735bdf 100644
--- a/rust/kernel/usb.rs
+++ b/rust/kernel/usb.rs
@@ -20,6 +20,12 @@
prelude::*,
sync::aref::AlwaysRefCounted,
types::Opaque,
+ usb::ch9::{
+ Direction,
+ EndpointDescriptor,
+ InterfaceClass,
+ InterfaceDescriptor, //
+ },
ThisModule, //
};
use core::{
@@ -29,6 +35,7 @@
MaybeUninit, //
},
ptr::NonNull,
+ slice, //
};
pub mod ch9;
@@ -358,6 +365,173 @@ impl<Ctx: device::DeviceContext> Interface<Ctx> {
fn as_raw(&self) -> *mut bindings::usb_interface {
self.0.get()
}
+
+ fn inner(&self) -> &bindings::usb_interface {
+ // SAFETY: The type invariants guarantee that `self.0` wraps a valid
+ // `struct usb_interface`.
+ unsafe { &*self.as_raw() }
+ }
+
+ /// Returns the current alternate setting for this interface.
+ pub fn cur_altsetting(&self) -> &HostInterface {
+ // SAFETY: `cur_altsetting` is a valid `struct usb_host_interface`
+ // pointer provided by the USB core. `HostInterface` is
+ // `#[repr(transparent)]` over it.
+ unsafe { &*(self.inner().cur_altsetting as *const HostInterface) }
+ }
+
+ /// Returns all alternate settings for this interface.
+ pub fn altsettings(&self) -> &[HostInterface] {
+ // SAFETY: `altsetting` is a valid array of `num_altsetting`
+ // entries provided by the USB core. `HostInterface` is
+ // `#[repr(transparent)]` over `usb_host_interface`.
+ unsafe {
+ slice::from_raw_parts(
+ self.inner().altsetting as *const HostInterface,
+ self.inner().num_altsetting as usize,
+ )
+ }
+ }
+}
+
+impl Interface<device::Bound> {
+ /// Select an alternate setting for this interface.
+ ///
+ /// On success the device switches to the given alternate setting,
+ /// which may change the set of active endpoints. This is a convenience
+ /// wrapper around [`Device<Bound>::set_interface`].
+ pub fn set_interface(&self, altsetting: u8) -> Result {
+ let dev: &Device<device::Bound> = self.as_ref();
+ dev.set_interface(self.cur_altsetting().number(), altsetting)
+ }
+}
+
+/// Abstraction for the USB Host Interface structure, i.e. `struct usb_host_interface`.
+#[repr(transparent)]
+pub struct HostInterface(Opaque<bindings::usb_host_interface>);
+
+impl HostInterface {
+ fn inner(&self) -> &bindings::usb_host_interface {
+ // SAFETY: The type invariants guarantee that `self.0` wraps a valid
+ // `struct usb_host_interface`.
+ unsafe { &*self.0.get() }
+ }
+
+ /// Returns the interface descriptor.
+ fn desc(&self) -> &InterfaceDescriptor {
+ // SAFETY: `desc` is a valid `struct usb_interface_descriptor`
+ // embedded in `usb_host_interface`. `InterfaceDescriptor` is
+ // `#[repr(transparent)]` over it.
+ unsafe { &*((core::ptr::from_ref(&self.inner().desc)).cast()) }
+ }
+
+ /// Returns the list of endpoints in this alternate setting.
+ pub fn endpoints(&self) -> &[HostEndpoint] {
+ // SAFETY: `endpoint` is a valid array of `bNumEndpoints` entries.
+ // `HostEndpoint` is `#[repr(transparent)]` over
+ // `usb_host_endpoint`.
+ unsafe {
+ core::ptr::slice_from_raw_parts(
+ self.inner().endpoint as *const HostEndpoint,
+ self.desc().bNumEndpoints() as usize,
+ )
+ .as_ref()
+ .unwrap_or(&[])
+ }
+ }
+
+ /// Returns the interface number (`bInterfaceNumber`).
+ pub fn number(&self) -> u8 {
+ self.desc().bInterfaceNumber()
+ }
+
+ /// Returns the alternate setting number (`bAlternateSetting`).
+ pub fn alternate_setting(&self) -> u8 {
+ self.desc().bAlternateSetting()
+ }
+
+ /// Returns the interface class (`bInterfaceClass`).
+ pub fn class(&self) -> InterfaceClass {
+ self.desc().bInterfaceClass()
+ }
+}
+
+/// USB endpoint transfer type.
+///
+/// Maps to the `bmAttributes` field of the endpoint descriptor
+/// (`USB_ENDPOINT_XFER_*` constants).
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+#[repr(u8)]
+pub enum EndpointType {
+ /// Control endpoint.
+ Control = bindings::USB_ENDPOINT_XFER_CONTROL as u8,
+ /// Isochronous endpoint.
+ Isoc = bindings::USB_ENDPOINT_XFER_ISOC as u8,
+ /// Bulk endpoint.
+ Bulk = bindings::USB_ENDPOINT_XFER_BULK as u8,
+ /// Interrupt endpoint.
+ Int = bindings::USB_ENDPOINT_XFER_INT as u8,
+}
+
+/// Abstraction for the USB Host Endpoint structure, i.e. [`struct usb_host_endpoint`].
+///
+/// [`struct usb_host_endpoint`]: https://docs.kernel.org/driver-api/usb/usb.html#c.usb_host_endpoint
+#[repr(transparent)]
+pub struct HostEndpoint(Opaque<bindings::usb_host_endpoint>);
+
+impl HostEndpoint {
+ fn inner(&self) -> &bindings::usb_host_endpoint {
+ // SAFETY: The type invariants guarantee that `self.0` wraps a valid
+ // `struct usb_host_endpoint`.
+ unsafe { &*self.0.get() }
+ }
+
+ /// Returns the endpoint descriptor.
+ fn desc(&self) -> &EndpointDescriptor {
+ // SAFETY: `desc` is a valid `struct usb_endpoint_descriptor`
+ // embedded in `usb_host_endpoint`. `EndpointDescriptor` is
+ // `#[repr(transparent)]` over it.
+ unsafe { &*(core::ptr::from_ref(&self.inner().desc).cast()) }
+ }
+
+ /// Returns the direction of this endpoint (IN or OUT).
+ pub fn endpoint_dir(&self) -> Direction {
+ if self.desc().bEndpointAddress() & Direction::In as u8 == 0 {
+ Direction::Out
+ } else {
+ Direction::In
+ }
+ }
+
+ /// Returns the endpoint number (0-15).
+ pub fn endpoint_number(&self) -> u8 {
+ self.desc().bEndpointAddress() & bindings::USB_ENDPOINT_NUMBER_MASK as u8
+ }
+
+ /// Returns the transfer type of this endpoint.
+ pub fn endpoint_type(&self) -> EndpointType {
+ let val = self.desc().bmAttributes() & bindings::USB_ENDPOINT_XFERTYPE_MASK as u8;
+ // SAFETY: `bmAttributes` masked with `USB_ENDPOINT_XFERTYPE_MASK`
+ // is guaranteed to be 0-3, which maps exactly to the four
+ // `EndpointType` variants.
+ unsafe { core::mem::transmute::<u8, EndpointType>(val) }
+ }
+
+ /// Returns the interval for interrupt and isochronous endpoints.
+ pub fn interval(&self) -> u8 {
+ self.desc().bInterval()
+ }
+
+ /// Returns the maximum packet size for this endpoint.
+ pub fn maxp(&self) -> u16 {
+ u16::from_le(self.desc().wMaxPacketSize()) & bindings::USB_ENDPOINT_MAXP_MASK as u16
+ }
+
+ /// Returns the high-speed multiplier for isochronous endpoints.
+ pub fn maxp_mult(&self) -> u16 {
+ (u16::from_le(self.desc().wMaxPacketSize()) & bindings::USB_EP_MAXP_MULT_MASK as u16)
+ >> bindings::USB_EP_MAXP_MULT_SHIFT
+ }
}
// SAFETY: `usb::Interface` is a transparent wrapper of `struct usb_interface`.
@@ -382,8 +556,8 @@ fn as_ref(&self) -> &device::Device<Ctx> {
}
}
-impl<Ctx: device::DeviceContext> AsRef<Device> for Interface<Ctx> {
- fn as_ref(&self) -> &Device {
+impl<Ctx: device::DeviceContext> AsRef<Device<Ctx>> for Interface<Ctx> {
+ fn as_ref(&self) -> &Device<Ctx> {
// SAFETY: `self.as_raw()` is valid by the type invariants.
let usb_dev = unsafe { bindings::interface_to_usbdev(self.as_raw()) };
@@ -428,7 +602,7 @@ unsafe impl Sync for Interface {}
///
/// [`struct usb_device`]: https://www.kernel.org/doc/html/latest/driver-api/usb/usb.html#c.usb_device
#[repr(transparent)]
-struct Device<Ctx: device::DeviceContext = device::Normal>(
+pub struct Device<Ctx: device::DeviceContext = device::Normal>(
Opaque<bindings::usb_device>,
PhantomData<Ctx>,
);
@@ -439,6 +613,20 @@ fn as_raw(&self) -> *mut bindings::usb_device {
}
}
+impl Device<device::Bound> {
+ /// Select an alternate setting for the given interface.
+ ///
+ /// On success the device switches the given interface to the given alternate setting,
+ /// which may change the set of active endpoints.
+ pub fn set_interface(&self, interface: u8, altsetting: u8) -> Result {
+ // SAFETY: `self.as_raw()` is a valid `struct usb_device` pointer by the type
+ // invariants. `usb_set_interface` is safe to call on a bound device.
+ to_result(unsafe {
+ bindings::usb_set_interface(self.as_raw(), i32::from(interface), i32::from(altsetting))
+ })
+ }
+}
+
// SAFETY: `Device` is a transparent wrapper of a type that doesn't depend on `Device`'s generic
// argument.
kernel::impl_device_context_deref!(unsafe { Device });
--
2.54.0
next prev parent reply other threads:[~2026-07-12 21:09 UTC|newest]
Thread overview: 28+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-12 21:07 [RFC PATCH 0/4] rust: usb: add usb request block abstractions and a user Colin Braun
2026-07-12 21:07 ` [RFC PATCH 1/4] rust: usb: add USB ch9 standard descriptors and constants Colin Braun
2026-07-12 21:07 ` Colin Braun [this message]
2026-07-13 13:22 ` [RFC PATCH 2/4] rust: usb: add usb host interface and endpoint abstractions Danilo Krummrich
2026-07-13 20:03 ` Colin Braun
2026-07-13 20:09 ` Danilo Krummrich
2026-07-14 9:26 ` Oliver Neukum
2026-07-14 13:05 ` Danilo Krummrich
2026-07-14 16:26 ` Alan Stern
2026-07-14 17:53 ` Danilo Krummrich
2026-07-14 18:48 ` Oliver Neukum
2026-07-14 18:57 ` Danilo Krummrich
2026-07-14 19:25 ` Alan Stern
2026-07-15 0:27 ` Danilo Krummrich
2026-07-14 17:57 ` Oliver Neukum
2026-07-14 19:03 ` Alan Stern
2026-07-14 18:26 ` Miguel Ojeda
2026-07-12 21:08 ` [RFC PATCH 3/4] rust: usb: add urb abstraction with control and isochronous support Colin Braun
2026-07-12 21:08 ` [RFC PATCH 4/4] media: add gv-usb2 audio capture driver Colin Braun
2026-07-13 14:44 ` Danilo Krummrich
2026-07-13 21:08 ` Colin Braun
2026-07-13 13:22 ` [RFC PATCH 0/4] rust: usb: add usb request block abstractions and a user Danilo Krummrich
2026-07-13 20:10 ` Colin Braun
2026-07-13 13:53 ` Daniel Almeida
2026-07-13 20:32 ` Colin Braun
2026-07-15 2:35 ` Daniel Almeida
2026-07-15 5:43 ` Greg Kroah-Hartman
2026-07-15 19:57 ` Colin Braun
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260712-urb-abstraction-v1-v1-2-9fa011634ead@gmail.com \
--to=colinbrauncl@gmail.com \
--cc=a.hindborg@kernel.org \
--cc=acourbot@nvidia.com \
--cc=aliceryhl@google.com \
--cc=bjorn3_gh@protonmail.com \
--cc=boqun@kernel.org \
--cc=colin.braun.cl@gmail.com \
--cc=dakr@kernel.org \
--cc=daniel.almeida@collabora.com \
--cc=gary@garyguo.net \
--cc=gregkh@linuxfoundation.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-media@vger.kernel.org \
--cc=linux-usb@vger.kernel.org \
--cc=lossin@kernel.org \
--cc=mathias.nyman@intel.com \
--cc=mchehab@kernel.org \
--cc=ojeda@kernel.org \
--cc=rust-for-linux@vger.kernel.org \
--cc=stern@rowland.harvard.edu \
--cc=tamird@kernel.org \
--cc=tmgross@umich.edu \
--cc=work@onurozkan.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox