Skip to content

Commit a3b846a

Browse files
committed
multi-language STRING Desc support
1 parent 98ccb7f commit a3b846a

File tree

4 files changed

+113
-30
lines changed

4 files changed

+113
-30
lines changed

src/device.rs

Lines changed: 69 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,10 @@ pub(crate) struct Config<'a> {
6363
pub product_id: u16,
6464
pub usb_rev: UsbRev,
6565
pub device_release: u16,
66-
pub manufacturer: Option<&'a str>,
67-
pub product: Option<&'a str>,
68-
pub serial_number: Option<&'a str>,
66+
pub extra_lang_ids: Option<&'a [u16]>,
67+
pub manufacturer: Option<&'a [&'a str]>,
68+
pub product: Option<&'a [&'a str]>,
69+
pub serial_number: Option<&'a [&'a str]>,
6970
pub self_powered: bool,
7071
pub supports_remote_wakeup: bool,
7172
pub composite_with_iads: bool,
@@ -547,25 +548,73 @@ impl<B: UsbBus> UsbDevice<'_, B> {
547548
Ok(())
548549
}),
549550

550-
descriptor_type::STRING => {
551-
if index == 0 {
552-
accept_writer(xfer, |w| {
553-
w.write(descriptor_type::STRING, &lang_id::ENGLISH_US.to_le_bytes())
554-
})
555-
} else {
556-
let s = match index {
557-
1 => config.manufacturer,
558-
2 => config.product,
559-
3 => config.serial_number,
560-
_ => {
561-
let index = StringIndex::new(index);
562-
let lang_id = req.index;
551+
descriptor_type::STRING => match index {
552+
// first STRING Request
553+
0 => {
554+
if let Some(extra_lang_ids) = config.extra_lang_ids {
555+
assert!(
556+
extra_lang_ids.len() < 16,
557+
"Not support more than 15 extra LangIDs"
558+
);
559+
560+
let mut lang_id_bytes = [0u8; 32];
561+
lang_id_bytes[0..2].copy_from_slice(&lang_id::ENGLISH_US.to_le_bytes());
562+
563+
extra_lang_ids
564+
.iter()
565+
.enumerate()
566+
.for_each(|(index, lang_id)| {
567+
lang_id_bytes[(index + 1) * 2..(index + 2) * 2]
568+
.copy_from_slice(&lang_id.to_le_bytes());
569+
});
570+
571+
accept_writer(xfer, |w| {
572+
w.write(
573+
descriptor_type::STRING,
574+
&lang_id_bytes[0..extra_lang_ids.len() * 2 + 2],
575+
)
576+
})
577+
} else {
578+
accept_writer(xfer, |w| {
579+
w.write(descriptor_type::STRING, &lang_id::ENGLISH_US.to_le_bytes())
580+
})
581+
}
582+
}
563583

564-
classes
584+
// reset STRING Request
585+
_ => {
586+
let s = if index <= 3 {
587+
// for Manufacture, Product and Serial
588+
let lang_id_list_index = match config.extra_lang_ids {
589+
Some(extra_lang_ids) => extra_lang_ids
565590
.iter()
566-
.filter_map(|cls| cls.get_string(index, lang_id))
567-
.next()
591+
.position(|lang_id| req.index == *lang_id)
592+
.map_or(0, |index| index + 1),
593+
None => 0,
594+
};
595+
596+
match index {
597+
1 => config
598+
.manufacturer
599+
.map(|str_list| str_list[lang_id_list_index]),
600+
601+
2 => config.product.map(|str_list| str_list[lang_id_list_index]),
602+
603+
3 => config
604+
.serial_number
605+
.map(|str_list| str_list[lang_id_list_index]),
606+
607+
_ => unreachable!(),
568608
}
609+
} else {
610+
// for other custom STRINGs
611+
let index = StringIndex::new(index);
612+
let lang_id = req.index;
613+
614+
classes
615+
.iter()
616+
.filter_map(|cls| cls.get_string(index, lang_id))
617+
.next()
569618
};
570619

571620
if let Some(s) = s {
@@ -574,7 +623,7 @@ impl<B: UsbBus> UsbDevice<'_, B> {
574623
xfer.reject().ok();
575624
}
576625
}
577-
}
626+
},
578627

579628
_ => {
580629
xfer.reject().ok();

src/device_builder.rs

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ impl<'a, B: UsbBus> UsbDeviceBuilder<'a, B> {
3636
product_id: vid_pid.1,
3737
usb_rev: UsbRev::Usb210,
3838
device_release: 0x0010,
39+
extra_lang_ids: None,
3940
manufacturer: None,
4041
product: None,
4142
serial_number: None,
@@ -106,27 +107,60 @@ impl<'a, B: UsbBus> UsbDeviceBuilder<'a, B> {
106107
self
107108
}
108109

110+
/// Sets **extra** Language ID for device.
111+
///
112+
/// Since "en_US"(0x0409) is implicitly embedded, you just need to fill other LangIDs
113+
///
114+
/// Default: (none)
115+
pub fn extra_lang_ids(mut self, extra_lang_ids: &'a [u16]) -> Self {
116+
assert!(
117+
extra_lang_ids.len() < 16,
118+
"Not support more than 15 extra LangIDs"
119+
);
120+
121+
if extra_lang_ids.len() > 0 {
122+
self.config.extra_lang_ids = Some(extra_lang_ids);
123+
}
124+
125+
self
126+
}
127+
109128
/// Sets the manufacturer name string descriptor.
110129
///
130+
/// the first string should always be in English, the language of reset strings
131+
/// should be pair with what inside [.extra_lang_ids()](Self::extra_lang_ids)
132+
///
111133
/// Default: (none)
112-
pub fn manufacturer(mut self, manufacturer: &'a str) -> Self {
113-
self.config.manufacturer = Some(manufacturer);
134+
pub fn manufacturer(mut self, manufacturer_ls: &'a [&'a str]) -> Self {
135+
if manufacturer_ls.len() > 0 {
136+
self.config.manufacturer = Some(manufacturer_ls);
137+
}
114138
self
115139
}
116140

117141
/// Sets the product name string descriptor.
118142
///
143+
/// the first string should always be in English, the language of reset strings
144+
/// should be pair with what inside [.extra_lang_ids()](Self::extra_lang_ids)
145+
///
119146
/// Default: (none)
120-
pub fn product(mut self, product: &'a str) -> Self {
121-
self.config.product = Some(product);
147+
pub fn product(mut self, product_ls: &'a [&'a str]) -> Self {
148+
if product_ls.len() > 0 {
149+
self.config.product = Some(product_ls);
150+
}
122151
self
123152
}
124153

125154
/// Sets the serial number string descriptor.
126155
///
156+
/// the first string should always be in English, the language of reset strings
157+
/// should be pair with what inside [.extra_lang_ids()](Self::extra_lang_ids)
158+
///
127159
/// Default: (none)
128-
pub fn serial_number(mut self, serial_number: &'a str) -> Self {
129-
self.config.serial_number = Some(serial_number);
160+
pub fn serial_number(mut self, serial_number_ls: &'a [&'a str]) -> Self {
161+
if serial_number_ls.len() > 0 {
162+
self.config.serial_number = Some(serial_number_ls);
163+
}
130164
self
131165
}
132166

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ pub mod endpoint;
149149
/// // product name. If using an existing class, remember to check the class crate documentation
150150
/// // for correct values.
151151
/// let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x5824, 0x27dd))
152-
/// .product("Serial port")
152+
/// .product(&["Serial port"])
153153
/// .device_class(usb_serial::DEVICE_CLASS)
154154
/// .build();
155155
///

src/test_class.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,9 +113,9 @@ impl<B: UsbBus> TestClass<'_, B> {
113113
usb_bus: &'b UsbBusAllocator<B>,
114114
) -> UsbDeviceBuilder<'b, B> {
115115
UsbDeviceBuilder::new(usb_bus, UsbVidPid(VID, PID))
116-
.manufacturer(MANUFACTURER)
117-
.product(PRODUCT)
118-
.serial_number(SERIAL_NUMBER)
116+
.manufacturer(&[MANUFACTURER])
117+
.product(&[PRODUCT])
118+
.serial_number(&[SERIAL_NUMBER])
119119
.max_packet_size_0(sizes::CONTROL_ENDPOINT)
120120
}
121121

0 commit comments

Comments
 (0)