Skip to content

Commit eac466e

Browse files
authored
Merge pull request #130 from rust-embedded-community/feature/language-refactor
Refactoring language management for simplicity
2 parents f154b66 + 18399f9 commit eac466e

File tree

8 files changed

+151
-226
lines changed

8 files changed

+151
-226
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 & 79 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,
@@ -548,97 +545,66 @@ impl<B: UsbBus> UsbDevice<'_, B> {
548545
descriptor_type::STRING => match index {
549546
// first STRING Request
550547
0 => {
551-
if let Some(extra_lang_ids) = config.extra_lang_ids {
552-
let mut lang_id_bytes = [0u8; 32];
553-
554-
lang_id_bytes
555-
.chunks_exact_mut(2)
556-
.zip([LangID::EN_US].iter().chain(extra_lang_ids.iter()))
557-
.for_each(|(buffer, lang_id)| {
558-
buffer.copy_from_slice(&u16::from(lang_id).to_le_bytes());
559-
});
560-
561-
accept_writer(xfer, |w| {
562-
w.write(
563-
descriptor_type::STRING,
564-
&lang_id_bytes[0..(1 + extra_lang_ids.len()) * 2],
565-
)
566-
})
567-
} else {
568-
accept_writer(xfer, |w| {
569-
w.write(
570-
descriptor_type::STRING,
571-
&u16::from(LangID::EN_US).to_le_bytes(),
572-
)
573-
})
548+
let mut lang_id_bytes = [0u8; 32];
549+
for (lang, buf) in config
550+
.string_descriptors
551+
.iter()
552+
.zip(lang_id_bytes.chunks_exact_mut(2))
553+
{
554+
buf.copy_from_slice(&u16::from(lang.id).to_le_bytes());
574555
}
556+
accept_writer(xfer, |w| {
557+
w.write(
558+
descriptor_type::STRING,
559+
&lang_id_bytes[..config.string_descriptors.len() * 2],
560+
)
561+
})
575562
}
576563

577564
// rest STRING Requests
578565
_ => {
579-
let s = match LangID::try_from(req.index) {
566+
let lang_id = match LangID::try_from(req.index) {
580567
Err(_err) => {
581568
#[cfg(feature = "defmt")]
582569
defmt::warn!(
583570
"Receive unknown LANGID {:#06X}, reject the request",
584571
_err.number
585572
);
586-
None
573+
xfer.reject().ok();
574+
return;
587575
}
588576

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

640-
if let Some(s) = s {
641-
accept_writer(xfer, |w| w.string(s));
606+
if let Some(string_descriptor) = string {
607+
accept_writer(xfer, |w| w.string(string_descriptor));
642608
} else {
643609
let _ = xfer.reject();
644610
}

0 commit comments

Comments
 (0)