diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs index 07b6c79d..8cf9c82a 100644 --- a/embassy-usb/src/builder.rs +++ b/embassy-usb/src/builder.rs @@ -1,6 +1,6 @@ use heapless::Vec; -use crate::Interface; +use crate::{Interface, STRING_INDEX_CUSTOM_START}; use super::control::ControlHandler; use super::descriptor::{BosWriter, DescriptorWriter}; @@ -181,7 +181,7 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { config, interfaces: Vec::new(), control_buf, - next_string_index: 4, + next_string_index: STRING_INDEX_CUSTOM_START, device_descriptor, config_descriptor, @@ -212,14 +212,6 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { self.control_buf.len() } - /// Allocates a new string index. - pub fn alloc_string(&mut self) -> StringIndex { - let index = self.next_string_index; - self.next_string_index += 1; - - StringIndex::new(index) - } - /// Add an USB function. /// /// If [`Config::composite_with_iads`] is set, this will add an IAD descriptor @@ -277,6 +269,7 @@ impl<'a, 'd, D: Driver<'d>> FunctionBuilder<'a, 'd, D> { handler: None, current_alt_setting: 0, num_alt_settings: 0, + num_strings: 0, }; if self.builder.interfaces.push(iface).is_err() { @@ -308,6 +301,15 @@ impl<'a, 'd, D: Driver<'d>> InterfaceBuilder<'a, 'd, D> { self.builder.interfaces[self.interface_number.0 as usize].handler = Some(handler); } + /// Allocates a new string index. + pub fn string(&mut self) -> StringIndex { + let index = self.builder.next_string_index; + self.builder.next_string_index += 1; + self.builder.interfaces[self.interface_number.0 as usize].num_strings += 1; + + StringIndex::new(index) + } + /// Add an alternate setting to the interface and write its descriptor. /// /// Alternate setting numbers are guaranteed to be allocated consecutively, starting from 0. diff --git a/embassy-usb/src/control.rs b/embassy-usb/src/control.rs index 0b59451d..ff42f9d7 100644 --- a/embassy-usb/src/control.rs +++ b/embassy-usb/src/control.rs @@ -189,6 +189,20 @@ pub trait ControlHandler { let _ = (req, buf); InResponse::Rejected } + + /// Called when a GET_DESCRIPTOR STRING control request is received. + /// + /// Write the response string somewhere (usually to `buf`, but you may use another buffer + /// owned by yourself, or a static buffer), then return it. + fn get_string<'a>( + &'a mut self, + index: StringIndex, + lang_id: u16, + buf: &'a mut [u8], + ) -> Option<&'a str> { + let _ = (index, lang_id, buf); + None + } } /// Typestate representing a ControlPipe in the DATA IN stage diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs index acb26c60..3bfedc04 100644 --- a/embassy-usb/src/lib.rs +++ b/embassy-usb/src/lib.rs @@ -66,6 +66,11 @@ pub const CONFIGURATION_VALUE: u8 = 1; pub const MAX_INTERFACE_COUNT: usize = 4; +const STRING_INDEX_MANUFACTURER: u8 = 1; +const STRING_INDEX_PRODUCT: u8 = 2; +const STRING_INDEX_SERIAL_NUMBER: u8 = 3; +const STRING_INDEX_CUSTOM_START: u8 = 4; + /// A handler trait for changes in the device state of the [UsbDevice]. pub trait DeviceStateHandler { /// Called when the USB device has been enabled or disabled. @@ -91,6 +96,7 @@ struct Interface<'d> { handler: Option<&'d mut dyn ControlHandler>, current_alt_setting: u8, num_alt_settings: u8, + num_strings: u8, } pub struct UsbDevice<'d, D: Driver<'d>> { @@ -540,14 +546,34 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { .await } else { let s = match index { - 1 => self.config.manufacturer, - 2 => self.config.product, - 3 => self.config.serial_number, + STRING_INDEX_MANUFACTURER => self.config.manufacturer, + STRING_INDEX_PRODUCT => self.config.product, + STRING_INDEX_SERIAL_NUMBER => self.config.serial_number, _ => { - let _index = StringIndex::new(index); - let _lang_id = req.index; - // TODO - None + // Find out which iface owns this string index. + let mut index_left = index - STRING_INDEX_CUSTOM_START; + let mut the_iface = None; + for iface in &mut self.interfaces { + if index_left < iface.num_strings { + the_iface = Some(iface); + break; + } + index_left -= iface.num_strings; + } + + if let Some(iface) = the_iface { + if let Some(handler) = &mut iface.handler { + let index = StringIndex::new(index); + let lang_id = req.index; + handler.get_string(index, lang_id, self.control_buf) + } else { + warn!("String requested to an interface with no handler."); + None + } + } else { + warn!("String requested but didn't match to an interface."); + None + } } };