Skip to content

Commit

Permalink
Merge pull request #15 from kevinmehall/get-descriptor
Browse files Browse the repository at this point in the history
Blocking control transfers and `Device::get_descriptor`, `Device::get_string_descriptor`
  • Loading branch information
kevinmehall authored Dec 18, 2023
2 parents 4ce7f55 + bf0db93 commit 2c5526b
Show file tree
Hide file tree
Showing 16 changed files with 811 additions and 54 deletions.
83 changes: 83 additions & 0 deletions examples/blocking.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
use std::time::Duration;

use nusb::transfer::{Control, ControlType, Recipient};

fn main() {
env_logger::init();
let di = nusb::list_devices()
.unwrap()
.find(|d| d.vendor_id() == 0x59e3 && d.product_id() == 0x0a23)
.expect("device should be connected");

println!("Device info: {di:?}");

let device = di.open().unwrap();

// Linux can make control transfers without claiming an interface
#[cfg(any(target_os = "linux", target_os = "macos"))]
{
let result = device.control_out_blocking(
Control {
control_type: ControlType::Vendor,
recipient: Recipient::Device,
request: 0x81,
value: 0x9999,
index: 0x9999,
},
&[1, 2, 3, 4],
Duration::from_secs(1),
);
println!("{result:?}");

let mut buf = [0; 64];

let len = device
.control_in_blocking(
Control {
control_type: ControlType::Vendor,
recipient: Recipient::Device,
request: 0x81,
value: 0x9999,
index: 0x9999,
},
&mut buf,
Duration::from_secs(1),
)
.unwrap();

println!("{result:?}, {data:?}", data = &buf[..len]);
}

// but we also provide an API on the `Interface` to support Windows
let interface = device.claim_interface(0).unwrap();

let result = interface.control_out_blocking(
Control {
control_type: ControlType::Vendor,
recipient: Recipient::Device,
request: 0x81,
value: 0x9999,
index: 0x9999,
},
&[1, 2, 3, 4, 5],
Duration::from_secs(1),
);
println!("{result:?}");

let mut buf = [0; 64];

let len = interface
.control_in_blocking(
Control {
control_type: ControlType::Vendor,
recipient: Recipient::Device,
request: 0x81,
value: 0x9999,
index: 0x9999,
},
&mut buf,
Duration::from_secs(1),
)
.unwrap();
println!("{data:?}", data = &buf[..len]);
}
68 changes: 68 additions & 0 deletions examples/string_descriptors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
use std::time::Duration;

use nusb::{descriptors::language_id::US_ENGLISH, DeviceInfo};

fn main() {
env_logger::init();
for dev in nusb::list_devices().unwrap() {
inspect_device(dev);
}
}

fn inspect_device(dev: DeviceInfo) {
println!(
"Device {:03}.{:03} ({:04x}:{:04x}) {} {}",
dev.bus_number(),
dev.device_address(),
dev.vendor_id(),
dev.product_id(),
dev.manufacturer_string().unwrap_or(""),
dev.product_string().unwrap_or("")
);
let dev = match dev.open() {
Ok(dev) => dev,
Err(e) => {
println!("Failed to open device: {}", e);
return;
}
};

let timeout = Duration::from_millis(100);

let dev_descriptor = dev.get_descriptor(0x01, 0, 0, timeout).unwrap();
if dev_descriptor.len() < 18
|| dev_descriptor[0] as usize > dev_descriptor.len()
|| dev_descriptor[1] != 0x01
{
println!(" Invalid device descriptor: {dev_descriptor:?}");
return;
}

let languages: Vec<u16> = dev
.get_string_descriptor_supported_languages(timeout)
.map(|i| i.collect())
.unwrap_or_default();
println!(" Languages: {languages:02x?}");

let language = languages.first().copied().unwrap_or(US_ENGLISH);

let i_manufacturer = dev_descriptor[14];
if i_manufacturer != 0 {
let s = dev.get_string_descriptor(i_manufacturer, language, timeout);
println!(" Manufacturer({i_manufacturer}): {s:?}");
}

let i_product = dev_descriptor[15];
if i_product != 0 {
let s = dev.get_string_descriptor(i_product, language, timeout);
println!(" Product({i_product}): {s:?}");
}

let i_serial = dev_descriptor[16];
if i_serial != 0 {
let s = dev.get_string_descriptor(i_serial, language, timeout);
println!(" Serial({i_serial}): {s:?}");
}

println!("");
}
30 changes: 27 additions & 3 deletions src/descriptors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@
//!
//! Descriptors are blocks of data that describe the functionality of a USB device.
use std::{collections::BTreeMap, fmt::{Display, Debug}, io::ErrorKind, iter, ops::Deref};
use std::{
collections::BTreeMap,
fmt::{Debug, Display},
io::ErrorKind,
iter,
ops::Deref,
};

use log::warn;

Expand All @@ -22,6 +28,16 @@ pub(crate) const DESCRIPTOR_LEN_INTERFACE: u8 = 9;
pub(crate) const DESCRIPTOR_TYPE_ENDPOINT: u8 = 0x05;
pub(crate) const DESCRIPTOR_LEN_ENDPOINT: u8 = 7;

/// USB defined language IDs for string descriptors.
///
/// In practice, different language IDs are not used,
/// and device string descriptors are only provided
/// with [`language_id::US_ENGLISH`].
pub mod language_id {
/// US English
pub const US_ENGLISH: u16 = 0x0409;
}

/// A raw USB descriptor.
///
/// Wraps a byte slice to provide access to the bytes of a descriptor by implementing `Deref` to `[u8]`,
Expand Down Expand Up @@ -287,7 +303,12 @@ impl<'a> Configuration<'a> {

struct DebugEntries<F>(F);

impl<F, I> Debug for DebugEntries<F> where F: Fn() -> I, I: Iterator, I::Item: Debug {
impl<F, I> Debug for DebugEntries<F>
where
F: Fn() -> I,
I: Iterator,
I::Item: Debug,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_list().entries(self.0()).finish()
}
Expand All @@ -301,7 +322,10 @@ impl<'a> Debug for Configuration<'a> {
.field("attributes", &self.attributes())
.field("max_power", &self.max_power())
.field("string_index", &self.string_index())
.field("interface_alt_settings", &DebugEntries(|| self.interface_alt_settings()))
.field(
"interface_alt_settings",
&DebugEntries(|| self.interface_alt_settings()),
)
.finish()
}
}
Expand Down
Loading

0 comments on commit 2c5526b

Please sign in to comment.