1pub mod args;
6mod descriptors;
7
8use crate::args::{Args, UsbDevice};
9use crate::descriptors::*;
10use anyhow::{Context, Result, format_err};
11use fidl_fuchsia_io as fio;
12use fuchsia_async::TimeoutExt;
13use fuchsia_sync::Mutex;
14use futures::TryStreamExt;
15use futures::future::{BoxFuture, FutureExt};
16use zx_status as zx;
17
18#[allow(unused_imports)]
20use zerocopy::{IntoBytes, Ref};
21
22pub async fn lsusb(usb_device_dir: fio::DirectoryProxy, args: Args) -> Result<()> {
23 if args.tree {
24 list_tree(&usb_device_dir, &args).await
25 } else {
26 list_devices(&usb_device_dir, &args).await
27 }
28}
29
30async fn list_devices(usb_device_dir: &fio::DirectoryProxy, args: &Args) -> Result<()> {
31 let mut stream = device_watcher::watch_for_files(usb_device_dir).await?;
32
33 println!("ID VID:PID SPEED MANUFACTURER PRODUCT");
34
35 while let Some(filename) = stream
36 .try_next()
37 .on_timeout(std::time::Duration::from_millis(200), || Ok(None))
39 .await
40 .context("FIDL call to get next device returned an error")?
41 {
42 let filename =
43 filename.to_str().ok_or_else(|| format_err!("to_str for filename failed"))?;
44
45 let (device, server_end) =
46 fidl::endpoints::create_proxy::<fidl_fuchsia_hardware_usb_device::DeviceMarker>();
47 usb_device_dir.open(
48 &filename,
49 fio::Flags::PROTOCOL_SERVICE,
50 &Default::default(),
51 server_end.into_channel(),
52 )?;
53
54 match list_device(&device, filename, 0, 0, &args).await {
55 Ok(()) => {}
56 Err(e) => eprintln!("Error: {:?}", e),
57 }
58 }
59 Ok(())
60}
61async fn list_device(
62 device: &fidl_fuchsia_hardware_usb_device::DeviceProxy,
63 filename: &str,
64 depth: usize,
65 max_depth: usize,
66 args: &Args,
67) -> Result<()> {
68 let devname = &format!("/dev/class/usb-device/{:03}", filename);
69
70 let device_desc_buf = device
71 .get_device_descriptor()
72 .on_timeout(std::time::Duration::from_millis(200), || {
73 Err(fidl::Error::ClientRead(zx::Status::TIMED_OUT.into()))
74 })
75 .await
76 .context(format!("DeviceGetDeviceDescriptor failed for {}", devname))?;
77
78 let device_desc = Ref::<_, DeviceDescriptor>::from_bytes(device_desc_buf.as_ref()).unwrap();
79
80 if let Some(UsbDevice { vendor_id, product_id }) = args.device {
81 if { device_desc.id_vendor } != vendor_id {
83 return Ok(());
84 }
85 if product_id.is_some() && { device_desc.id_product } != product_id.unwrap() {
86 return Ok(());
87 }
88 }
89
90 let speed = device
91 .get_device_speed()
92 .on_timeout(std::time::Duration::from_millis(200), || {
93 Err(fidl::Error::ClientRead(zx::Status::TIMED_OUT.into()))
94 })
95 .await
96 .context(format!("DeviceGetDeviceSpeed failed for {}", devname))?;
97
98 let string_manu_desc = get_string_descriptor(device, device_desc.i_manufacturer)
99 .on_timeout(std::time::Duration::from_millis(200), || Err(format_err!("Timeout")))
100 .await
101 .context(format!("DeviceGetStringDescriptor failed for {}", devname))?;
102
103 let string_prod_desc = get_string_descriptor(device, device_desc.i_product)
104 .on_timeout(std::time::Duration::from_millis(200), || Err(format_err!("Timeout")))
105 .await
106 .context(format!("DeviceGetStringDescriptor failed for {}", devname))?;
107
108 let left_pad = depth * 4;
109 let right_pad = (max_depth - depth) * 4;
110
111 println!(
112 "{0:left_pad$}{1:03} {0:right_pad$}{2:04X}:{3:04X} {4:<5} {5} {6}",
113 "",
114 filename,
115 { device_desc.id_vendor },
116 { device_desc.id_product },
117 UsbSpeed(speed),
118 string_manu_desc,
119 string_prod_desc,
120 left_pad = left_pad,
121 right_pad = right_pad,
122 );
123
124 if args.verbose {
125 println!("Device Descriptor:");
126 println!(" {:<33}{}", "bLength", device_desc.b_length);
127 println!(" {:<33}{}", "bDescriptorType", device_desc.b_descriptor_type);
128 println!(" {:<33}{}.{}", "bcdUSB", device_desc.bcd_usb >> 8, device_desc.bcd_usb & 0xFF);
129 println!(" {:<33}{}", "bDeviceClass", device_desc.b_device_class);
130 println!(" {:<33}{}", "bDeviceSubClass", device_desc.b_device_sub_class);
131 println!(" {:<33}{}", "bDeviceProtocol", device_desc.b_device_protocol);
132 println!(" {:<33}{}", "bMaxPacketSize0", device_desc.b_max_packet_size0);
133 println!(" {:<33}{:#06X}", "idVendor", { device_desc.id_vendor });
134 println!(" {:<33}{:#06X}", "idProduct", { device_desc.id_product });
135 println!(
136 " {:<33}{}.{}",
137 "bcdDevice",
138 device_desc.bcd_device >> 8,
139 device_desc.bcd_device & 0xFF
140 );
141 println!(" {:<33}{} {}", "iManufacturer", device_desc.i_manufacturer, string_manu_desc);
142 println!(" {:<33}{} {}", "iProduct", device_desc.i_product, string_prod_desc);
143
144 let serial_number = get_string_descriptor(device, device_desc.i_serial_number)
145 .on_timeout(std::time::Duration::from_millis(200), || Err(format_err!("Timeout")))
146 .await
147 .context(format!("DeviceGetStringDescriptor failed for {}", devname))?;
148
149 println!(" {:<33}{} {}", "iSerialNumber", device_desc.i_serial_number, serial_number);
150 println!(" {:<33}{}", "bNumConfigurations", device_desc.b_num_configurations);
151
152 let mut config = args.configuration;
153 if config.is_none() {
154 config = Some(
155 device
156 .get_configuration()
157 .on_timeout(std::time::Duration::from_millis(200), || {
158 Err(fidl::Error::ClientRead(zx::Status::TIMED_OUT.into()))
159 })
160 .await
161 .context(format!("DeviceGetConfiguration failed for {}", devname))?,
162 );
163 }
164
165 let (status, config_desc_data) = device
166 .get_configuration_descriptor(config.unwrap())
167 .on_timeout(std::time::Duration::from_millis(200), || {
168 Err(fidl::Error::ClientRead(zx::Status::TIMED_OUT.into()))
169 })
170 .await
171 .context(format!("DeviceGetConfigurationDescriptor failed for {}", devname))?;
172
173 zx::Status::ok(status)
174 .map_err(|e| return anyhow::anyhow!("Failed to get configuration descriptor: {}", e))?;
175
176 for descriptor in DescriptorIterator::new(&config_desc_data) {
177 match descriptor {
178 Descriptor::Config(config_desc) => {
179 println!("{:>2}Configuration Descriptor:", "");
180 println!("{:>4}{:<31}{}", "", "bLength", config_desc.b_length);
181 println!("{:>4}{:<31}{}", "", "bDescriptorType", config_desc.b_descriptor_type);
182 println!("{:>4}{:<31}{}", "", "wTotalLength", { config_desc.w_total_length });
183 println!("{:>4}{:<31}{}", "", "bNumInterfaces", config_desc.b_num_interfaces);
184 println!(
185 "{:>4}{:<31}{}",
186 "", "bConfigurationValue", config_desc.b_configuration_value
187 );
188 let config_str = get_string_descriptor(device, config_desc.i_configuration)
189 .on_timeout(std::time::Duration::from_millis(200), || {
190 Err(format_err!("Timeout"))
191 })
192 .await
193 .context(format!("DeviceGetStringDescriptor failed for {}", devname))?;
194 println!(
195 "{:>4}{:<31}{} {}",
196 "", "iConfiguration", config_desc.i_configuration, config_str
197 );
198 println!("{:>4}{:<31}{:#04X}", "", "bmAttributes", config_desc.bm_attributes);
199 println!("{:>4}{:<31}{}", "", "bMaxPower", config_desc.b_max_power);
200 }
201 Descriptor::Interface(info) => {
202 println!("{:>4}Interface Descriptor:", "");
203 println!("{:>6}{:<29}{}", "", "bLength", info.b_length);
204 println!("{:>6}{:<29}{}", "", "bDescriptorType", info.b_descriptor_type);
205 println!("{:>6}{:<29}{}", "", "bInterfaceNumber", info.b_interface_number);
206 println!("{:>6}{:<29}{}", "", "bAlternateSetting", info.b_alternate_setting);
207 println!("{:>6}{:<29}{}", "", "bNumEndpoints", info.b_num_endpoints);
208 println!("{:>6}{:<29}{}", "", "bInterfaceClass", info.b_interface_class);
209 println!("{:>6}{:<29}{}", "", "bInterfaceSubClass", info.b_interface_sub_class);
210 println!("{:>6}{:<29}{}", "", "bInterfaceProtocol", info.b_interface_protocol);
211
212 let interface_str = get_string_descriptor(device, info.i_interface)
213 .on_timeout(std::time::Duration::from_millis(200), || {
214 Err(format_err!("Timeout"))
215 })
216 .await
217 .context(format!("DeviceGetStringDescriptor failed for {}", devname))?;
218 println!("{:>6}{:<29}{} {}", "", "iInterface", info.i_interface, interface_str);
219 }
220 Descriptor::Endpoint(info) => {
221 println!("{:>6}Endpoint Descriptor:", "");
222 println!("{:>8}{:<27}{}", "", "bLength", info.b_length);
223 println!("{:>8}{:<27}{}", "", "bDescriptorType", info.b_descriptor_type);
224 println!("{:>8}{:<27}{:#04X}", "", "bEndpointAddress", info.b_endpoint_address);
225 println!("{:>8}{:<27}{:#04X}", "", "bmAttributes", info.bm_attributes);
226 println!("{:>8}{:<27}{}", "", "wMaxPacketSize", { info.w_max_packet_size });
227 println!("{:>8}{:<27}{}", "", "bInterval", info.b_interval);
228 }
229 Descriptor::Hid(descriptor) => {
230 let info = descriptor.get();
231 println!("{:>6}HID Descriptor:", "");
232 println!("{:>8}{:<27}{}", "", "bLength", info.b_length);
233 println!("{:>8}{:<27}{}", "", "bDescriptorType", info.b_descriptor_type);
234 println!(
235 "{:>8}{:<27}{}{}",
236 "",
237 "bcdHID",
238 info.bcd_hid >> 8,
239 info.bcd_hid & 0xFF
240 );
241 println!("{:>8}{:<27}{}", "", "bCountryCode", info.b_country_code);
242 println!("{:>8}{:<27}{}", "", "bNumDescriptors", info.b_num_descriptors);
243 for entry in descriptor {
244 println!("{:>10}{:<25}{}", "", "bDescriptorType", entry.b_descriptor_type);
245 println!("{:>10}{:<25}{}", "", "wDescriptorLength", {
246 entry.w_descriptor_length
247 });
248 }
249 }
250 Descriptor::SsEpCompanion(info) => {
251 println!("{:>8}SuperSpeed Endpoint Companion Descriptor:", "");
252 println!("{:>10}{:<25}{}", "", "bLength", info.b_length);
253 println!("{:>10}{:<25}{}", "", "bDescriptorType", info.b_descriptor_type);
254 println!("{:>10}{:<25}{:#04X}", "", "bMaxBurst", info.b_max_burst);
255 println!("{:>10}{:<25}{:#04X}", "", "bmAttributes", info.bm_attributes);
256 println!("{:>10}{:<25}{}", "", "wBytesPerInterval", info.w_bytes_per_interval);
257 }
258 Descriptor::SsIsochEpCompanion(info) => {
259 println!("{:>10}SuperSpeed Isochronous Endpoint Companion Descriptor:", "");
260 println!("{:>12}{:<23}{}", "", "bLength", info.b_length);
261 println!("{:>12}{:<23}{}", "", "bDescriptorType", info.b_descriptor_type);
262 println!("{:>12}{:<23}{}", "", "wReserved", { info.w_reserved });
263 println!("{:>12}{:<23}{}", "", "dwBytesPerInterval", {
264 info.dw_bytes_per_interval
265 });
266 }
267 Descriptor::InterfaceAssociation(info) => {
268 println!("{:>12}Interface Association Descriptor:", "");
269 println!("{:>14}{:<21}{}", "", "bLength", info.b_length);
270 println!("{:>14}{:<21}{}", "", "bDescriptorType", info.b_descriptor_type);
271 println!("{:>14}{:<21}{}", "", "bFirstInterface", info.b_first_interface);
272 println!("{:>14}{:<21}{}", "", "bInterfaceCount", info.b_interface_count);
273 println!("{:>14}{:<21}{}", "", "bFunctionClass", info.b_function_class);
274 println!("{:>14}{:<21}{}", "", "bFunctionSubClass", info.b_function_sub_class);
275 println!("{:>14}{:<21}{}", "", "bFunctionProtocol", info.b_function_protocol);
276 println!("{:>14}{:<21}{}", "", "iFunction", info.i_function);
277 }
278 Descriptor::Unknown(buffer) => {
279 println!("Unknown Descriptor:");
280 println!(" {:<33}{}", "bLength", buffer[0]);
281 println!(" {:<33}{}", "bDescriptorType", buffer[1]);
282 println!(" {:X?}", buffer);
283 }
284 }
285 }
286 }
287 return Ok(());
288}
289
290struct DeviceNode {
291 pub device: fidl_fuchsia_hardware_usb_device::DeviceProxy,
292 pub filename: String,
293 pub device_id: u32,
294 pub hub_id: u32,
295 pub depth: Mutex<Option<usize>>,
298}
299
300impl DeviceNode {
301 fn get_depth(&self, devices: &[DeviceNode]) -> Result<usize> {
302 if let Some(depth) = self.depth.lock().clone() {
303 return Ok(depth);
304 }
305 if self.hub_id == 0 {
306 return Ok(0);
307 }
308 for device in devices.iter() {
309 if self.hub_id == device.device_id {
310 return device.get_depth(devices).map(|depth| depth + 1);
311 }
312 }
313 Err(format_err!("Hub not found for device"))
314 }
315}
316
317async fn list_tree(usb_device_dir: &fio::DirectoryProxy, args: &Args) -> Result<()> {
318 let mut stream = device_watcher::watch_for_files(usb_device_dir).await?;
319 let mut devices = Vec::new();
320
321 while let Some(filename) = stream
322 .try_next()
323 .on_timeout(std::time::Duration::from_millis(200), || Ok(None))
325 .await
326 .context("FIDL call to get next device returned an error")?
327 {
328 let filename =
329 filename.to_str().ok_or_else(|| format_err!("to_str for filename failed"))?;
330
331 let (device, server_end) =
332 fidl::endpoints::create_proxy::<fidl_fuchsia_hardware_usb_device::DeviceMarker>();
333 usb_device_dir.open(
334 &filename,
335 fio::Flags::PROTOCOL_SERVICE,
336 &Default::default(),
337 server_end.into_channel(),
338 )?;
339
340 devices.push(get_device_info(device, filename).await?);
341 }
342
343 for device in devices.iter() {
344 let depth = device.get_depth(&devices)?;
345 *device.depth.lock() = Some(depth);
346 }
347
348 let max_depth = devices
349 .iter()
350 .filter_map(|device| device.depth.lock().clone())
351 .fold(0, std::cmp::max::<usize>);
352
353 print!("ID ");
354 for _ in 0..max_depth {
355 print!(" ");
356 }
357 println!(" VID:PID SPEED MANUFACTURER PRODUCT");
358
359 do_list_tree(&devices, 0, max_depth, args).await
360}
361
362fn do_list_tree<'a>(
363 devices: &'a [DeviceNode],
364 hub_id: u32,
365 max_depth: usize,
366 args: &'a Args,
367) -> BoxFuture<'a, Result<()>> {
368 async move {
369 for device in devices.iter() {
370 if device.hub_id == hub_id {
371 let depth = device.depth.lock().unwrap().clone();
372 match list_device(&device.device, &device.filename, depth, max_depth, args).await {
373 Ok(()) => {}
374 Err(e) => eprintln!("Error: {:?}", e),
375 }
376 do_list_tree(devices, device.device_id, max_depth, args).await?;
377 }
378 }
379 Ok(())
380 }
381 .boxed()
382}
383
384async fn get_device_info(
385 device: fidl_fuchsia_hardware_usb_device::DeviceProxy,
386 filename: &str,
387) -> Result<DeviceNode> {
388 let devname = &format!("/dev/class/usb-device/{:03}", filename);
389
390 let device_id =
391 device.get_device_id().await.context(format!("GetDeviceId failed for {}", devname))?;
392
393 let hub_id =
394 device.get_hub_device_id().await.context(format!("GeHubId failed for {}", devname))?;
395
396 let filename = filename.to_string();
397 Ok(DeviceNode { device, filename, device_id, hub_id, depth: Mutex::new(None) })
398}
399
400async fn get_string_descriptor(
401 device: &fidl_fuchsia_hardware_usb_device::DeviceProxy,
402 desc_id: u8,
403) -> Result<String, anyhow::Error> {
404 match desc_id {
405 0 => return Ok(String::from("UNKNOWN")),
406 _ => {
407 return device.get_string_descriptor(desc_id, EN_US).await.map(
408 |(status, value, _)| {
409 if zx::Status::ok(status).is_ok() {
410 Ok(value)
411 } else {
412 Ok(String::from("UNKNOWN"))
413 }
414 },
415 )?;
416 }
417 };
418}
419
420#[cfg(test)]
421mod test {
422
423 use super::*;
424 use futures::prelude::*;
425
426 async fn run_usb_server(
427 stream: fidl_fuchsia_hardware_usb_device::DeviceRequestStream,
428 ) -> Result<(), anyhow::Error> {
429 stream
430 .map(|result| result.context("failed request"))
431 .try_for_each(|request| async {
432 match request {
433 fidl_fuchsia_hardware_usb_device::DeviceRequest::GetDeviceSpeed {responder } => {
434 responder.send(1)?;
435 }
436 fidl_fuchsia_hardware_usb_device::DeviceRequest::GetDeviceDescriptor {
437 responder} => {
438 let descriptor = DeviceDescriptor {
439 b_length: std::mem::size_of::<DeviceDescriptor>() as u8,
440 b_descriptor_type: 1,
441 bcd_usb: 2,
442 b_device_class: 3,
443 b_device_sub_class: 4,
444 b_device_protocol: 5,
445 b_max_packet_size0: 6,
446 id_vendor: 7,
447 id_product: 8,
448 bcd_device: 9,
449 i_manufacturer: 10,
450 i_product: 11,
451 i_serial_number: 12,
452 b_num_configurations: 2,
453 };
454 let mut array = [0; 18];
455 array.copy_from_slice(descriptor.as_bytes());
456 responder.send(&array)?;
457 }
458 fidl_fuchsia_hardware_usb_device::DeviceRequest::GetConfigurationDescriptor {
459 config: _, responder} => {
460 let total_length =
461 std::mem::size_of::<ConfigurationDescriptor>() +
462 std::mem::size_of::<InterfaceInfoDescriptor>() * 2;
463
464 let config_descriptor = ConfigurationDescriptor {
465 b_length: std::mem::size_of::<ConfigurationDescriptor>() as u8,
466 b_descriptor_type: 0,
467 w_total_length: total_length as u16,
468 b_num_interfaces: 2,
469 b_configuration_value: 0,
470 i_configuration: 0,
471 bm_attributes: 0,
472 b_max_power: 0,
473 };
474
475 let interface_one = InterfaceInfoDescriptor {
476 b_length: std::mem::size_of::<InterfaceInfoDescriptor>() as u8,
477 b_descriptor_type: 4,
478 b_interface_number: 1,
479 b_alternate_setting: 0,
480 b_num_endpoints: 0,
481 b_interface_class: 1,
482 b_interface_sub_class: 2,
483 b_interface_protocol: 3,
484 i_interface: 1,
485 };
486
487 let interface_two = InterfaceInfoDescriptor {
488 b_length: std::mem::size_of::<InterfaceInfoDescriptor>() as u8,
489 b_descriptor_type: 4,
490 b_interface_number: 2,
491 b_alternate_setting: 0,
492 b_num_endpoints: 0,
493 b_interface_class: 3,
494 b_interface_sub_class: 4,
495 b_interface_protocol: 5,
496 i_interface: 2,
497 };
498
499 let mut vec = std::vec::Vec::new();
500 vec.extend_from_slice(config_descriptor.as_bytes());
501 vec.extend_from_slice(interface_one.as_bytes());
502 vec.extend_from_slice(interface_two.as_bytes());
503
504 responder.send(0, &vec)?;
505 }
506 fidl_fuchsia_hardware_usb_device::DeviceRequest::GetStringDescriptor {
507 desc_id: _, lang_id: _, responder} => {
508 responder.send(0, "<unknown>", 0)?;
509 }
510 fidl_fuchsia_hardware_usb_device::DeviceRequest::GetConfiguration {responder} => {
511 responder.send(0)?;
512 }
513 _ => {
514 return Err(anyhow::anyhow!("Unsupported function"));
515 }
516 }
517 Ok(())
518 })
519 .await?;
520 Ok(())
521 }
522
523 #[fuchsia::test]
524 async fn smoke_test() {
525 let (device, stream) = fidl::endpoints::create_proxy_and_stream::<
526 fidl_fuchsia_hardware_usb_device::DeviceMarker,
527 >();
528
529 let server_task = run_usb_server(stream).fuse();
530 let test_task = async move {
531 let args = Args { tree: false, verbose: true, configuration: None, device: None };
532 println!("ID VID:PID SPEED MANUFACTURER PRODUCT");
533 list_device(&device, "", 0, 0, &args).await.unwrap();
534 }
535 .fuse();
536 futures::pin_mut!(server_task, test_task);
537 futures::select! {
538 result = server_task => {
539 panic!("Server task finished: {:?}", result);
540 },
541 () = test_task => {},
542 }
543 }
544}