diff --git a/CHANGELOG.md b/CHANGELOG.md index 271677e..21d83ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Added * New enums and allocators for Isochronous endpoints ([#60](https://github.com/rust-embedded-community/usb-device/pull/60)). * Ability to select USB revision ([#116](https://github.com/rust-embedded-community/usb-device/pull/116)). +* Added support for alternate settings on interfaces ([#114](https://github.com/rust-embedded-community/usb-device/pull/114)). ### Changed * `EndpointType` enum now has fields for isochronous synchronization and usage ([#60](https://github.com/rust-embedded-community/usb-device/pull/60)). diff --git a/src/bus.rs b/src/bus.rs index dd0f0ba..8c8531d 100644 --- a/src/bus.rs +++ b/src/bus.rs @@ -321,7 +321,7 @@ impl UsbBusAllocator { /// A handle for a USB interface that contains its number. #[derive(Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct InterfaceNumber(u8); +pub struct InterfaceNumber(pub(crate) u8); impl From for u8 { fn from(n: InterfaceNumber) -> u8 { diff --git a/src/class.rs b/src/class.rs index 5337e97..9e94484 100644 --- a/src/class.rs +++ b/src/class.rs @@ -1,4 +1,4 @@ -use crate::bus::{StringIndex, UsbBus}; +use crate::bus::{InterfaceNumber, StringIndex, UsbBus}; use crate::control; use crate::control_pipe::ControlPipe; use crate::descriptor::{BosWriter, DescriptorWriter}; @@ -116,6 +116,24 @@ pub trait UsbClass { fn endpoint_in_complete(&mut self, addr: EndpointAddress) { let _ = addr; } + + /// Called when the interfaces alternate setting state is requested. + /// + /// Note: This method may be called on interfaces, that are not relevant to this class. + /// You should return `None, if `interface` belongs to an interface you don't know. + fn get_alt_setting(&mut self, interface: InterfaceNumber) -> Option { + let _ = interface; + None + } + + /// Called when the interfaces alternate setting state is altered. + /// + /// Note: This method may be called on interfaces, that are not relevant to this class. + /// You should return `false`, if `interface` belongs to an interface you don't know. + fn set_alt_setting(&mut self, interface: InterfaceNumber, alternative: u8) -> bool { + let _ = (interface, alternative); + false + } } /// Handle for a control IN transfer. When implementing a class, use the methods of this object to diff --git a/src/device.rs b/src/device.rs index 09f25d2..56021d7 100644 --- a/src/device.rs +++ b/src/device.rs @@ -1,4 +1,4 @@ -use crate::bus::{PollResult, StringIndex, UsbBus, UsbBusAllocator}; +use crate::bus::{InterfaceNumber, PollResult, StringIndex, UsbBus, UsbBusAllocator}; use crate::class::{ControlIn, ControlOut, UsbClass}; use crate::control; use crate::control_pipe::ControlPipe; @@ -357,7 +357,25 @@ impl UsbDevice<'_, B> { } (Recipient::Interface, Request::GET_INTERFACE) => { - // TODO: change when alternate settings are implemented + // Reject interface numbers bigger than 255 + if req.index > core::u8::MAX.into() { + xfer.reject().ok(); + return; + } + + // Ask class implementations, whether they know the alternate setting + // of the interface in question + for cls in classes { + match cls.get_alt_setting(InterfaceNumber(req.index as u8)) { + Some(setting) => { + xfer.accept_with(&setting.to_le_bytes()).ok(); + return; + } + None => (), + } + } + + // If no class returned an alternate setting, return the default value xfer.accept_with(&DEFAULT_ALTERNATE_SETTING.to_le_bytes()) .ok(); } @@ -374,7 +392,7 @@ impl UsbDevice<'_, B> { fn control_out(&mut self, classes: &mut ClassList<'_, B>, req: control::Request) { use crate::control::{Recipient, Request}; - for cls in classes { + for cls in classes.iter_mut() { cls.control_out(ControlOut::new(&mut self.control, &req)); if !self.control.waiting_for_response() { @@ -447,9 +465,28 @@ impl UsbDevice<'_, B> { } } - (Recipient::Interface, Request::SET_INTERFACE, DEFAULT_ALTERNATE_SETTING_U16) => { - // TODO: do something when alternate settings are implemented - xfer.accept().ok(); + (Recipient::Interface, Request::SET_INTERFACE, alt_setting) => { + // Reject interface numbers and alt settings bigger than 255 + if req.index > core::u8::MAX.into() || alt_setting > core::u8::MAX.into() { + xfer.reject().ok(); + return; + } + + // Ask class implementations, whether they accept the alternate interface setting. + for cls in classes { + if cls.set_alt_setting(InterfaceNumber(req.index as u8), alt_setting as u8) + { + xfer.accept().ok(); + return; + } + } + + // Default behaviour, if no class implementation accepted the alternate setting. + if alt_setting == DEFAULT_ALTERNATE_SETTING_U16 { + xfer.accept().ok(); + } else { + xfer.reject().ok(); + } } _ => {