Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

multi-language STRING Desc support #122

Merged
merged 10 commits into from
Jul 27, 2023
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,16 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
* 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)).
* Added support for architectures without atomics ([#115](https://github.com/rust-embedded-community/usb-device/pull/115)).
* Added support for multi-language STRING desc ([#122](https://github.com/rust-embedded-community/usb-device/pull/122)).
* `UsbDeviceBuilder` has a public `.extra_lang_ids()` method to specify LANGIDs besides ENGLISH_US(0x0409)

### Breaking
* `DescriptorWriter::iad()` now requires a `Option<StringIndex>` to optionally specify a string for describing the function ([#121](https://github.com/rust-embedded-community/usb-device/pull/121))
* `.manufacturer()`, `.product()` and `.serial_number()` of `UsbDeviceBuilder` now require `&[&str]` to specify strings match with each LANGIDs supported by device. ([#122](https://github.com/rust-embedded-community/usb-device/pull/122))

### Changed
* `EndpointType` enum now has fields for isochronous synchronization and usage ([#60](https://github.com/rust-embedded-community/usb-device/pull/60)).
* `descriptor_type::STRING` of `fn get_descriptor()` will send the LANGIDs supported by device, and respond STRING Request with specified LANGID. ([#122](https://github.com/rust-embedded-community/usb-device/pull/122))

## [0.2.9] - 2022-08-02

Expand Down
89 changes: 69 additions & 20 deletions src/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,10 @@ pub(crate) struct Config<'a> {
pub product_id: u16,
pub usb_rev: UsbRev,
pub device_release: u16,
pub manufacturer: Option<&'a str>,
pub product: Option<&'a str>,
pub serial_number: Option<&'a str>,
pub extra_lang_ids: Option<&'a [u16]>,
pub manufacturer: Option<&'a [&'a str]>,
pub product: Option<&'a [&'a str]>,
pub serial_number: Option<&'a [&'a str]>,
ryan-summers marked this conversation as resolved.
Show resolved Hide resolved
pub self_powered: bool,
pub supports_remote_wakeup: bool,
pub composite_with_iads: bool,
Expand Down Expand Up @@ -547,25 +548,73 @@ impl<B: UsbBus> UsbDevice<'_, B> {
Ok(())
}),

descriptor_type::STRING => {
if index == 0 {
accept_writer(xfer, |w| {
w.write(descriptor_type::STRING, &lang_id::ENGLISH_US.to_le_bytes())
})
} else {
let s = match index {
1 => config.manufacturer,
2 => config.product,
3 => config.serial_number,
_ => {
let index = StringIndex::new(index);
let lang_id = req.index;
descriptor_type::STRING => match index {
// first STRING Request
0 => {
if let Some(extra_lang_ids) = config.extra_lang_ids {
assert!(
extra_lang_ids.len() < 16,
"Not support more than 15 extra LangIDs"
);
eZioPan marked this conversation as resolved.
Show resolved Hide resolved

let mut lang_id_bytes = [0u8; 32];
lang_id_bytes[0..2].copy_from_slice(&lang_id::ENGLISH_US.to_le_bytes());

extra_lang_ids
.iter()
.enumerate()
.for_each(|(index, lang_id)| {
lang_id_bytes[(index + 1) * 2..(index + 2) * 2]
.copy_from_slice(&lang_id.to_le_bytes());
});
eZioPan marked this conversation as resolved.
Show resolved Hide resolved

accept_writer(xfer, |w| {
w.write(
descriptor_type::STRING,
&lang_id_bytes[0..extra_lang_ids.len() * 2 + 2],
)
})
} else {
accept_writer(xfer, |w| {
w.write(descriptor_type::STRING, &lang_id::ENGLISH_US.to_le_bytes())
})
}
}

classes
// rest STRING Requests
_ => {
let s = if index <= 3 {
// for Manufacture, Product and Serial
let lang_id_list_index = match config.extra_lang_ids {
Some(extra_lang_ids) => extra_lang_ids
.iter()
.filter_map(|cls| cls.get_string(index, lang_id))
.next()
.position(|lang_id| req.index == *lang_id)
.map_or(0, |index| index + 1),
None => 0,
};

match index {
1 => config
.manufacturer
.map(|str_list| str_list[lang_id_list_index]),

2 => config.product.map(|str_list| str_list[lang_id_list_index]),

3 => config
.serial_number
.map(|str_list| str_list[lang_id_list_index]),

_ => unreachable!(),
}
} else {
// for other custom STRINGs
let index = StringIndex::new(index);
let lang_id = req.index;

classes
.iter()
.filter_map(|cls| cls.get_string(index, lang_id))
.next()
};

if let Some(s) = s {
Expand All @@ -574,7 +623,7 @@ impl<B: UsbBus> UsbDevice<'_, B> {
xfer.reject().ok();
}
}
}
},

_ => {
xfer.reject().ok();
Expand Down
46 changes: 40 additions & 6 deletions src/device_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ impl<'a, B: UsbBus> UsbDeviceBuilder<'a, B> {
product_id: vid_pid.1,
usb_rev: UsbRev::Usb210,
device_release: 0x0010,
extra_lang_ids: None,
manufacturer: None,
product: None,
serial_number: None,
Expand Down Expand Up @@ -106,27 +107,60 @@ impl<'a, B: UsbBus> UsbDeviceBuilder<'a, B> {
self
}

/// Sets **extra** Language ID for device.
///
/// Since "en_US"(0x0409) is implicitly embedded, you just need to fill other LangIDs
///
/// Default: (none)
pub fn extra_lang_ids(mut self, extra_lang_ids: &'a [u16]) -> Self {
eZioPan marked this conversation as resolved.
Show resolved Hide resolved
eZioPan marked this conversation as resolved.
Show resolved Hide resolved
assert!(
extra_lang_ids.len() < 16,
"Not support more than 15 extra LangIDs"
);

if extra_lang_ids.len() > 0 {
self.config.extra_lang_ids = Some(extra_lang_ids);
}

self
eZioPan marked this conversation as resolved.
Show resolved Hide resolved
}

/// Sets the manufacturer name string descriptor.
///
/// the first string should always be in English, the language of reset strings
/// should be pair with what inside [.extra_lang_ids()](Self::extra_lang_ids)
///
/// Default: (none)
pub fn manufacturer(mut self, manufacturer: &'a str) -> Self {
self.config.manufacturer = Some(manufacturer);
pub fn manufacturer(mut self, manufacturer_ls: &'a [&'a str]) -> Self {
if manufacturer_ls.len() > 0 {
self.config.manufacturer = Some(manufacturer_ls);
}
eZioPan marked this conversation as resolved.
Show resolved Hide resolved
self
}

/// Sets the product name string descriptor.
///
/// the first string should always be in English, the language of reset strings
/// should be pair with what inside [.extra_lang_ids()](Self::extra_lang_ids)
///
/// Default: (none)
pub fn product(mut self, product: &'a str) -> Self {
self.config.product = Some(product);
pub fn product(mut self, product_ls: &'a [&'a str]) -> Self {
if product_ls.len() > 0 {
eZioPan marked this conversation as resolved.
Show resolved Hide resolved
self.config.product = Some(product_ls);
}
self
}

/// Sets the serial number string descriptor.
///
/// the first string should always be in English, the language of reset strings
/// should be pair with what inside [.extra_lang_ids()](Self::extra_lang_ids)
///
/// Default: (none)
pub fn serial_number(mut self, serial_number: &'a str) -> Self {
self.config.serial_number = Some(serial_number);
pub fn serial_number(mut self, serial_number_ls: &'a [&'a str]) -> Self {
if serial_number_ls.len() > 0 {
eZioPan marked this conversation as resolved.
Show resolved Hide resolved
self.config.serial_number = Some(serial_number_ls);
}
self
}

Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ pub mod endpoint;
/// // product name. If using an existing class, remember to check the class crate documentation
/// // for correct values.
/// let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x5824, 0x27dd))
/// .product("Serial port")
/// .product(&["Serial port"])
/// .device_class(usb_serial::DEVICE_CLASS)
/// .build();
///
Expand Down
6 changes: 3 additions & 3 deletions src/test_class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,9 @@ impl<B: UsbBus> TestClass<'_, B> {
usb_bus: &'b UsbBusAllocator<B>,
) -> UsbDeviceBuilder<'b, B> {
UsbDeviceBuilder::new(usb_bus, UsbVidPid(VID, PID))
.manufacturer(MANUFACTURER)
.product(PRODUCT)
.serial_number(SERIAL_NUMBER)
.manufacturer(&[MANUFACTURER])
.product(&[PRODUCT])
.serial_number(&[SERIAL_NUMBER])
.max_packet_size_0(sizes::CONTROL_ENDPOINT)
}

Expand Down