lsusb/
lib.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5pub mod args;
6mod descriptors;
7
8use crate::args::{Args, UsbDevice};
9use crate::descriptors::*;
10use anyhow::{format_err, Context, Result};
11use fuchsia_async::TimeoutExt;
12use futures::future::{BoxFuture, FutureExt};
13use futures::TryStreamExt;
14use std::sync::Mutex;
15use {fidl_fuchsia_io as fio, zx_status as zx};
16
17// This isn't actually unused, but rustc can't seem to tell otherwise.
18#[allow(unused_imports)]
19use zerocopy::{IntoBytes, Ref};
20
21pub async fn lsusb(usb_device_dir: fio::DirectoryProxy, args: Args) -> Result<()> {
22    if args.tree {
23        list_tree(&usb_device_dir, &args).await
24    } else {
25        list_devices(&usb_device_dir, &args).await
26    }
27}
28
29async fn list_devices(usb_device_dir: &fio::DirectoryProxy, args: &Args) -> Result<()> {
30    let mut stream = device_watcher::watch_for_files(usb_device_dir).await?;
31
32    println!("ID    VID:PID   SPEED  MANUFACTURER PRODUCT");
33
34    while let Some(filename) = stream
35        .try_next()
36        // This will wait forever, so if there are no more devices, lets stop waiting.
37        .on_timeout(std::time::Duration::from_millis(200), || Ok(None))
38        .await
39        .context("FIDL call to get next device returned an error")?
40    {
41        let filename =
42            filename.to_str().ok_or_else(|| format_err!("to_str for filename failed"))?;
43
44        let (device, server_end) =
45            fidl::endpoints::create_proxy::<fidl_fuchsia_hardware_usb_device::DeviceMarker>();
46        usb_device_dir.open(
47            &filename,
48            fio::Flags::PROTOCOL_SERVICE,
49            &Default::default(),
50            server_end.into_channel(),
51        )?;
52
53        match list_device(&device, filename, 0, 0, &args).await {
54            Ok(()) => {}
55            Err(e) => eprintln!("Error: {:?}", e),
56        }
57    }
58    Ok(())
59}
60async fn list_device(
61    device: &fidl_fuchsia_hardware_usb_device::DeviceProxy,
62    filename: &str,
63    depth: usize,
64    max_depth: usize,
65    args: &Args,
66) -> Result<()> {
67    let devname = &format!("/dev/class/usb-device/{:03}", filename);
68
69    let device_desc_buf = device
70        .get_device_descriptor()
71        .on_timeout(std::time::Duration::from_millis(200), || {
72            Err(fidl::Error::ClientRead(zx::Status::TIMED_OUT.into()))
73        })
74        .await
75        .context(format!("DeviceGetDeviceDescriptor failed for {}", devname))?;
76
77    let device_desc = Ref::<_, DeviceDescriptor>::from_bytes(device_desc_buf.as_ref()).unwrap();
78
79    if let Some(UsbDevice { vendor_id, product_id }) = args.device {
80        // Return early if this isn't the device that was asked about.
81        if { device_desc.id_vendor } != vendor_id {
82            return Ok(());
83        }
84        if product_id.is_some() && { device_desc.id_product } != product_id.unwrap() {
85            return Ok(());
86        }
87    }
88
89    let speed = device
90        .get_device_speed()
91        .on_timeout(std::time::Duration::from_millis(200), || {
92            Err(fidl::Error::ClientRead(zx::Status::TIMED_OUT.into()))
93        })
94        .await
95        .context(format!("DeviceGetDeviceSpeed failed for {}", devname))?;
96
97    let string_manu_desc = get_string_descriptor(device, device_desc.i_manufacturer)
98        .on_timeout(std::time::Duration::from_millis(200), || Err(format_err!("Timeout")))
99        .await
100        .context(format!("DeviceGetStringDescriptor failed for {}", devname))?;
101
102    let string_prod_desc = get_string_descriptor(device, device_desc.i_product)
103        .on_timeout(std::time::Duration::from_millis(200), || Err(format_err!("Timeout")))
104        .await
105        .context(format!("DeviceGetStringDescriptor failed for {}", devname))?;
106
107    let left_pad = depth * 4;
108    let right_pad = (max_depth - depth) * 4;
109
110    println!(
111        "{0:left_pad$}{1:03}  {0:right_pad$}{2:04X}:{3:04X}  {4:<5}  {5} {6}",
112        "",
113        filename,
114        { device_desc.id_vendor },
115        { device_desc.id_product },
116        UsbSpeed(speed),
117        string_manu_desc,
118        string_prod_desc,
119        left_pad = left_pad,
120        right_pad = right_pad,
121    );
122
123    if args.verbose {
124        println!("Device Descriptor:");
125        println!("  {:<33}{}", "bLength", device_desc.b_length);
126        println!("  {:<33}{}", "bDescriptorType", device_desc.b_descriptor_type);
127        println!("  {:<33}{}.{}", "bcdUSB", device_desc.bcd_usb >> 8, device_desc.bcd_usb & 0xFF);
128        println!("  {:<33}{}", "bDeviceClass", device_desc.b_device_class);
129        println!("  {:<33}{}", "bDeviceSubClass", device_desc.b_device_sub_class);
130        println!("  {:<33}{}", "bDeviceProtocol", device_desc.b_device_protocol);
131        println!("  {:<33}{}", "bMaxPacketSize0", device_desc.b_max_packet_size0);
132        println!("  {:<33}{:#06X}", "idVendor", { device_desc.id_vendor });
133        println!("  {:<33}{:#06X}", "idProduct", { device_desc.id_product });
134        println!(
135            "  {:<33}{}.{}",
136            "bcdDevice",
137            device_desc.bcd_device >> 8,
138            device_desc.bcd_device & 0xFF
139        );
140        println!("  {:<33}{} {}", "iManufacturer", device_desc.i_manufacturer, string_manu_desc);
141        println!("  {:<33}{} {}", "iProduct", device_desc.i_product, string_prod_desc);
142
143        let serial_number = get_string_descriptor(device, device_desc.i_serial_number)
144            .on_timeout(std::time::Duration::from_millis(200), || Err(format_err!("Timeout")))
145            .await
146            .context(format!("DeviceGetStringDescriptor failed for {}", devname))?;
147
148        println!("  {:<33}{} {}", "iSerialNumber", device_desc.i_serial_number, serial_number);
149        println!("  {:<33}{}", "bNumConfigurations", device_desc.b_num_configurations);
150
151        let mut config = args.configuration;
152        if config.is_none() {
153            config = Some(
154                device
155                    .get_configuration()
156                    .on_timeout(std::time::Duration::from_millis(200), || {
157                        Err(fidl::Error::ClientRead(zx::Status::TIMED_OUT.into()))
158                    })
159                    .await
160                    .context(format!("DeviceGetConfiguration failed for {}", devname))?,
161            );
162        }
163
164        let (status, config_desc_data) = device
165            .get_configuration_descriptor(config.unwrap())
166            .on_timeout(std::time::Duration::from_millis(200), || {
167                Err(fidl::Error::ClientRead(zx::Status::TIMED_OUT.into()))
168            })
169            .await
170            .context(format!("DeviceGetConfigurationDescriptor failed for {}", devname))?;
171
172        zx::Status::ok(status)
173            .map_err(|e| return anyhow::anyhow!("Failed to get configuration descriptor: {}", e))?;
174
175        for descriptor in DescriptorIterator::new(&config_desc_data) {
176            match descriptor {
177                Descriptor::Config(config_desc) => {
178                    println!("{:>2}Configuration Descriptor:", "");
179                    println!("{:>4}{:<31}{}", "", "bLength", config_desc.b_length);
180                    println!("{:>4}{:<31}{}", "", "bDescriptorType", config_desc.b_descriptor_type);
181                    println!("{:>4}{:<31}{}", "", "wTotalLength", { config_desc.w_total_length });
182                    println!("{:>4}{:<31}{}", "", "bNumInterfaces", config_desc.b_num_interfaces);
183                    println!(
184                        "{:>4}{:<31}{}",
185                        "", "bConfigurationValue", config_desc.b_configuration_value
186                    );
187                    let config_str = get_string_descriptor(device, config_desc.i_configuration)
188                        .on_timeout(std::time::Duration::from_millis(200), || {
189                            Err(format_err!("Timeout"))
190                        })
191                        .await
192                        .context(format!("DeviceGetStringDescriptor failed for {}", devname))?;
193                    println!(
194                        "{:>4}{:<31}{} {}",
195                        "", "iConfiguration", config_desc.i_configuration, config_str
196                    );
197                    println!("{:>4}{:<31}{:#04X}", "", "bmAttributes", config_desc.bm_attributes);
198                    println!("{:>4}{:<31}{}", "", "bMaxPower", config_desc.b_max_power);
199                }
200                Descriptor::Interface(info) => {
201                    println!("{:>4}Interface Descriptor:", "");
202                    println!("{:>6}{:<29}{}", "", "bLength", info.b_length);
203                    println!("{:>6}{:<29}{}", "", "bDescriptorType", info.b_descriptor_type);
204                    println!("{:>6}{:<29}{}", "", "bInterfaceNumber", info.b_interface_number);
205                    println!("{:>6}{:<29}{}", "", "bAlternateSetting", info.b_alternate_setting);
206                    println!("{:>6}{:<29}{}", "", "bNumEndpoints", info.b_num_endpoints);
207                    println!("{:>6}{:<29}{}", "", "bInterfaceClass", info.b_interface_class);
208                    println!("{:>6}{:<29}{}", "", "bInterfaceSubClass", info.b_interface_sub_class);
209                    println!("{:>6}{:<29}{}", "", "bInterfaceProtocol", info.b_interface_protocol);
210
211                    let interface_str = get_string_descriptor(device, info.i_interface)
212                        .on_timeout(std::time::Duration::from_millis(200), || {
213                            Err(format_err!("Timeout"))
214                        })
215                        .await
216                        .context(format!("DeviceGetStringDescriptor failed for {}", devname))?;
217                    println!("{:>6}{:<29}{} {}", "", "iInterface", info.i_interface, interface_str);
218                }
219                Descriptor::Endpoint(info) => {
220                    println!("{:>6}Endpoint Descriptor:", "");
221                    println!("{:>8}{:<27}{}", "", "bLength", info.b_length);
222                    println!("{:>8}{:<27}{}", "", "bDescriptorType", info.b_descriptor_type);
223                    println!("{:>8}{:<27}{:#04X}", "", "bEndpointAddress", info.b_endpoint_address);
224                    println!("{:>8}{:<27}{:#04X}", "", "bmAttributes", info.bm_attributes);
225                    println!("{:>8}{:<27}{}", "", "wMaxPacketSize", { info.w_max_packet_size });
226                    println!("{:>8}{:<27}{}", "", "bInterval", info.b_interval);
227                }
228                Descriptor::Hid(descriptor) => {
229                    let info = descriptor.get();
230                    println!("{:>6}HID Descriptor:", "");
231                    println!("{:>8}{:<27}{}", "", "bLength", info.b_length);
232                    println!("{:>8}{:<27}{}", "", "bDescriptorType", info.b_descriptor_type);
233                    println!(
234                        "{:>8}{:<27}{}{}",
235                        "",
236                        "bcdHID",
237                        info.bcd_hid >> 8,
238                        info.bcd_hid & 0xFF
239                    );
240                    println!("{:>8}{:<27}{}", "", "bCountryCode", info.b_country_code);
241                    println!("{:>8}{:<27}{}", "", "bNumDescriptors", info.b_num_descriptors);
242                    for entry in descriptor {
243                        println!("{:>10}{:<25}{}", "", "bDescriptorType", entry.b_descriptor_type);
244                        println!("{:>10}{:<25}{}", "", "wDescriptorLength", {
245                            entry.w_descriptor_length
246                        });
247                    }
248                }
249                Descriptor::SsEpCompanion(info) => {
250                    println!("{:>8}SuperSpeed Endpoint Companion Descriptor:", "");
251                    println!("{:>10}{:<25}{}", "", "bLength", info.b_length);
252                    println!("{:>10}{:<25}{}", "", "bDescriptorType", info.b_descriptor_type);
253                    println!("{:>10}{:<25}{:#04X}", "", "bMaxBurst", info.b_max_burst);
254                    println!("{:>10}{:<25}{:#04X}", "", "bmAttributes", info.bm_attributes);
255                    println!("{:>10}{:<25}{}", "", "wBytesPerInterval", info.w_bytes_per_interval);
256                }
257                Descriptor::SsIsochEpCompanion(info) => {
258                    println!("{:>10}SuperSpeed Isochronous Endpoint Companion Descriptor:", "");
259                    println!("{:>12}{:<23}{}", "", "bLength", info.b_length);
260                    println!("{:>12}{:<23}{}", "", "bDescriptorType", info.b_descriptor_type);
261                    println!("{:>12}{:<23}{}", "", "wReserved", { info.w_reserved });
262                    println!("{:>12}{:<23}{}", "", "dwBytesPerInterval", {
263                        info.dw_bytes_per_interval
264                    });
265                }
266                Descriptor::InterfaceAssociation(info) => {
267                    println!("{:>12}Interface Association Descriptor:", "");
268                    println!("{:>14}{:<21}{}", "", "bLength", info.b_length);
269                    println!("{:>14}{:<21}{}", "", "bDescriptorType", info.b_descriptor_type);
270                    println!("{:>14}{:<21}{}", "", "bFirstInterface", info.b_first_interface);
271                    println!("{:>14}{:<21}{}", "", "bInterfaceCount", info.b_interface_count);
272                    println!("{:>14}{:<21}{}", "", "bFunctionClass", info.b_function_class);
273                    println!("{:>14}{:<21}{}", "", "bFunctionSubClass", info.b_function_sub_class);
274                    println!("{:>14}{:<21}{}", "", "bFunctionProtocol", info.b_function_protocol);
275                    println!("{:>14}{:<21}{}", "", "iFunction", info.i_function);
276                }
277                Descriptor::Unknown(buffer) => {
278                    println!("Unknown Descriptor:");
279                    println!("  {:<33}{}", "bLength", buffer[0]);
280                    println!("  {:<33}{}", "bDescriptorType", buffer[1]);
281                    println!("  {:X?}", buffer);
282                }
283            }
284        }
285    }
286    return Ok(());
287}
288
289struct DeviceNode {
290    pub device: fidl_fuchsia_hardware_usb_device::DeviceProxy,
291    pub filename: String,
292    pub device_id: u32,
293    pub hub_id: u32,
294    // Depth in tree, None if not computed yet.
295    // Mutex is used for interior mutability.
296    pub depth: Mutex<Option<usize>>,
297}
298
299impl DeviceNode {
300    fn get_depth(&self, devices: &[DeviceNode]) -> Result<usize> {
301        if let Some(depth) = self.depth.lock().unwrap().clone() {
302            return Ok(depth);
303        }
304        if self.hub_id == 0 {
305            return Ok(0);
306        }
307        for device in devices.iter() {
308            if self.hub_id == device.device_id {
309                return device.get_depth(devices).map(|depth| depth + 1);
310            }
311        }
312        Err(format_err!("Hub not found for device"))
313    }
314}
315
316async fn list_tree(usb_device_dir: &fio::DirectoryProxy, args: &Args) -> Result<()> {
317    let mut stream = device_watcher::watch_for_files(usb_device_dir).await?;
318    let mut devices = Vec::new();
319
320    while let Some(filename) = stream
321        .try_next()
322        // This will wait forever, so if there are no more devices, lets stop waiting.
323        .on_timeout(std::time::Duration::from_millis(200), || Ok(None))
324        .await
325        .context("FIDL call to get next device returned an error")?
326    {
327        let filename =
328            filename.to_str().ok_or_else(|| format_err!("to_str for filename failed"))?;
329
330        let (device, server_end) =
331            fidl::endpoints::create_proxy::<fidl_fuchsia_hardware_usb_device::DeviceMarker>();
332        usb_device_dir.open(
333            &filename,
334            fio::Flags::PROTOCOL_SERVICE,
335            &Default::default(),
336            server_end.into_channel(),
337        )?;
338
339        devices.push(get_device_info(device, filename).await?);
340    }
341
342    for device in devices.iter() {
343        let depth = device.get_depth(&devices)?;
344        *device.depth.lock().unwrap() = Some(depth);
345    }
346
347    let max_depth = devices
348        .iter()
349        .filter_map(|device| device.depth.lock().unwrap().clone())
350        .fold(0, std::cmp::max::<usize>);
351
352    print!("ID   ");
353    for _ in 0..max_depth {
354        print!("    ");
355    }
356    println!(" VID:PID   SPEED  MANUFACTURER PRODUCT");
357
358    do_list_tree(&devices, 0, max_depth, args).await
359}
360
361fn do_list_tree<'a>(
362    devices: &'a [DeviceNode],
363    hub_id: u32,
364    max_depth: usize,
365    args: &'a Args,
366) -> BoxFuture<'a, Result<()>> {
367    async move {
368        for device in devices.iter() {
369            if device.hub_id == hub_id {
370                let depth = device.depth.lock().unwrap().unwrap().clone();
371                match list_device(&device.device, &device.filename, depth, max_depth, args).await {
372                    Ok(()) => {}
373                    Err(e) => eprintln!("Error: {:?}", e),
374                }
375                do_list_tree(devices, device.device_id, max_depth, args).await?;
376            }
377        }
378        Ok(())
379    }
380    .boxed()
381}
382
383async fn get_device_info(
384    device: fidl_fuchsia_hardware_usb_device::DeviceProxy,
385    filename: &str,
386) -> Result<DeviceNode> {
387    let devname = &format!("/dev/class/usb-device/{:03}", filename);
388
389    let device_id =
390        device.get_device_id().await.context(format!("GetDeviceId failed for {}", devname))?;
391
392    let hub_id =
393        device.get_hub_device_id().await.context(format!("GeHubId failed for {}", devname))?;
394
395    let filename = filename.to_string();
396    Ok(DeviceNode { device, filename, device_id, hub_id, depth: Mutex::new(None) })
397}
398
399async fn get_string_descriptor(
400    device: &fidl_fuchsia_hardware_usb_device::DeviceProxy,
401    desc_id: u8,
402) -> Result<String, anyhow::Error> {
403    match desc_id {
404        0 => return Ok(String::from("UNKNOWN")),
405        _ => {
406            return device.get_string_descriptor(desc_id, EN_US).await.map(
407                |(status, value, _)| {
408                    if zx::Status::ok(status).is_ok() {
409                        Ok(value)
410                    } else {
411                        Ok(String::from("UNKNOWN"))
412                    }
413                },
414            )?
415        }
416    };
417}
418
419#[cfg(test)]
420mod test {
421
422    use super::*;
423    use fuchsia_async as fasync;
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    #[fasync::run_singlethreaded(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}