Skip to content

Commit 3598a6f

Browse files
committed
Refactoring language management for simplicity
1 parent d6eaf93 commit 3598a6f

File tree

8 files changed

+151
-230
lines changed

8 files changed

+151
-230
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
2121
### Breaking
2222
* Acess numeric form of `EndpointType` variants now require a `.to_bm_attributes()`. ([#60](https://github.com/rust-embedded-community/usb-device/pull/60))
2323
* `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))
24-
* `.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))
24+
* `.manufacturer()`, `.product()` and `.serial_number()` of `UsbDeviceBuilder` are now replaced with the `strings()` function that accepts a `StringDescriptor` list to allow multilanguage support ([#122](https://github.com/rust-embedded-community/usb-device/pull/122))
25+
* Various methods of the `UsbDeviceBuilder` now return `Result<>` types instead of internally panicking.
2526

2627
### Changed
2728
* `EndpointType` enum now has fields for isochronous synchronization and usage ([#60](https://github.com/rust-embedded-community/usb-device/pull/60)).

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ repository = "https://github.com/mvirkkunen/usb-device"
1313
defmt = { version = "0.3", optional = true }
1414
portable-atomic = { version = "1.2.0", default-features = false }
1515
num_enum = { version = "0.6.1", default-features = false }
16+
heapless = "0.7"
1617

1718
[dev-dependencies]
1819
rusb = "0.9.1"

src/descriptor.rs

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -116,11 +116,29 @@ impl DescriptorWriter<'_> {
116116
config.product_id as u8,
117117
(config.product_id >> 8) as u8, // idProduct
118118
config.device_release as u8,
119-
(config.device_release >> 8) as u8, // bcdDevice
120-
config.manufacturer.map_or(0, |_| 1), // iManufacturer
121-
config.product.map_or(0, |_| 2), // iProduct
122-
config.serial_number.map_or(0, |_| 3), // iSerialNumber
123-
1, // bNumConfigurations
119+
(config.device_release >> 8) as u8, // bcdDevice
120+
config.string_descriptors.first().map_or(0, |lang| {
121+
if lang.manufacturer.is_some() {
122+
1
123+
} else {
124+
0
125+
}
126+
}),
127+
config.string_descriptors.first().map_or(0, |lang| {
128+
if lang.product.is_some() {
129+
2
130+
} else {
131+
0
132+
}
133+
}),
134+
config.string_descriptors.first().map_or(0, |lang| {
135+
if lang.serial.is_some() {
136+
3
137+
} else {
138+
0
139+
}
140+
}),
141+
1, // bNumConfigurations
124142
],
125143
)
126144
}

src/descriptor/lang_id.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ impl From<LangID> for u16 {
1515
}
1616

1717
#[allow(missing_docs)]
18-
#[derive(Clone, Copy, PartialEq, TryFromPrimitive)]
18+
#[derive(Clone, Copy, PartialEq, Debug, TryFromPrimitive)]
1919
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
2020
#[repr(u16)]
2121
pub enum LangID {

src/device.rs

Lines changed: 45 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::class::{ControlIn, ControlOut, UsbClass};
33
use crate::control;
44
use crate::control_pipe::ControlPipe;
55
use crate::descriptor::{descriptor_type, lang_id::LangID, BosWriter, DescriptorWriter};
6-
pub use crate::device_builder::{UsbDeviceBuilder, UsbVidPid};
6+
pub use crate::device_builder::{StringDescriptors, UsbDeviceBuilder, UsbVidPid};
77
use crate::endpoint::{EndpointAddress, EndpointType};
88
use crate::{Result, UsbDirection};
99
use core::convert::TryFrom;
@@ -64,10 +64,7 @@ pub(crate) struct Config<'a> {
6464
pub product_id: u16,
6565
pub usb_rev: UsbRev,
6666
pub device_release: u16,
67-
pub extra_lang_ids: Option<&'a [LangID]>,
68-
pub manufacturer: Option<&'a [&'a str]>,
69-
pub product: Option<&'a [&'a str]>,
70-
pub serial_number: Option<&'a [&'a str]>,
67+
pub string_descriptors: heapless::Vec<StringDescriptors<'a>, 16>,
7168
pub self_powered: bool,
7269
pub supports_remote_wakeup: bool,
7370
pub composite_with_iads: bool,
@@ -550,101 +547,66 @@ impl<B: UsbBus> UsbDevice<'_, B> {
550547
descriptor_type::STRING => match index {
551548
// first STRING Request
552549
0 => {
553-
if let Some(extra_lang_ids) = config.extra_lang_ids {
554-
let mut lang_id_bytes = [0u8; 32];
555-
556-
lang_id_bytes
557-
.chunks_exact_mut(2)
558-
.zip([LangID::EN_US].iter().chain(extra_lang_ids.iter()))
559-
.for_each(|(buffer, lang_id)| {
560-
buffer.copy_from_slice(&u16::from(lang_id).to_le_bytes());
561-
});
562-
563-
accept_writer(xfer, |w| {
564-
w.write(
565-
descriptor_type::STRING,
566-
&lang_id_bytes[0..(1 + extra_lang_ids.len()) * 2],
567-
)
568-
})
569-
} else {
570-
accept_writer(xfer, |w| {
571-
w.write(
572-
descriptor_type::STRING,
573-
&u16::from(LangID::EN_US).to_le_bytes(),
574-
)
575-
})
550+
let mut lang_id_bytes = [0u8; 32];
551+
for (lang, buf) in config
552+
.string_descriptors
553+
.iter()
554+
.zip(lang_id_bytes.chunks_exact_mut(2))
555+
{
556+
buf.copy_from_slice(&u16::from(lang.id).to_le_bytes());
576557
}
558+
accept_writer(xfer, |w| {
559+
w.write(
560+
descriptor_type::STRING,
561+
&lang_id_bytes[..config.string_descriptors.len() * 2],
562+
)
563+
})
577564
}
578565

579566
// rest STRING Requests
580567
_ => {
581-
let s = match LangID::try_from(req.index) {
568+
let lang_id = match LangID::try_from(req.index) {
582569
Err(_err) => {
583570
#[cfg(feature = "defmt")]
584571
defmt::warn!(
585572
"Receive unknown LANGID {:#06X}, reject the request",
586573
_err.number
587574
);
588-
None
575+
xfer.reject().ok();
576+
return;
589577
}
590578

591-
Ok(req_lang_id) => {
592-
if index <= 3 {
593-
// for Manufacture, Product and Serial
594-
595-
// construct the list of lang_ids full supported by device
596-
let mut lang_id_list: [Option<LangID>; 16] = [None; 16];
597-
match config.extra_lang_ids {
598-
None => lang_id_list[0] = Some(LangID::EN_US),
599-
Some(extra_lang_ids) => {
600-
lang_id_list
601-
.iter_mut()
602-
.zip(
603-
[LangID::EN_US].iter().chain(extra_lang_ids.iter()),
604-
)
605-
.for_each(|(item, lang_id)| *item = Some(*lang_id));
606-
}
607-
};
608-
609-
lang_id_list
610-
.iter()
611-
.fuse()
612-
.position(|list_lang_id| match *list_lang_id {
613-
Some(list_lang_id) if req_lang_id == list_lang_id => true,
614-
_ => false,
615-
})
616-
.or_else(|| {
617-
// Since we construct the list of full supported lang_ids previously,
618-
// we can safely reject requests which ask for other lang_id.
619-
#[cfg(feature = "defmt")]
620-
defmt::warn!(
621-
"Receive unknown LANGID {:#06X}, reject the request",
622-
req_lang_id
623-
);
624-
None
625-
})
626-
.and_then(|lang_id_list_index| {
627-
match index {
628-
1 => config.manufacturer,
629-
2 => config.product,
630-
3 => config.serial_number,
631-
_ => unreachable!(),
632-
}
633-
.map(|str_list| str_list[lang_id_list_index])
634-
})
635-
} else {
636-
// for other custom STRINGs
637-
638-
let index = StringIndex::new(index);
639-
classes
640-
.iter()
641-
.find_map(|cls| cls.get_string(index, req_lang_id))
579+
Ok(req_lang_id) => req_lang_id,
580+
};
581+
let string = match index {
582+
// Manufacturer, product, and serial are handled directly here.
583+
1..=3 => {
584+
let Some(lang) = config
585+
.string_descriptors
586+
.iter()
587+
.find(|lang| lang.id == lang_id)
588+
else {
589+
xfer.reject().ok();
590+
return;
591+
};
592+
593+
match index {
594+
1 => lang.manufacturer,
595+
2 => lang.product,
596+
3 => lang.serial,
597+
_ => unreachable!(),
642598
}
643599
}
600+
_ => {
601+
let index = StringIndex::new(index);
602+
classes
603+
.iter()
604+
.find_map(|cls| cls.get_string(index, lang_id))
605+
}
644606
};
645607

646-
if let Some(s) = s {
647-
accept_writer(xfer, |w| w.string(s));
608+
if let Some(string_descriptor) = string {
609+
accept_writer(xfer, |w| w.string(string_descriptor));
648610
} else {
649611
xfer.reject().ok();
650612
}

0 commit comments

Comments
 (0)