diff --git a/Cargo.lock b/Cargo.lock index 2ad89404..3cea630d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -210,7 +210,7 @@ dependencies = [ [[package]] name = "cyme" -version = "1.1.3" +version = "1.2.0" dependencies = [ "assert-json-diff", "clap", diff --git a/Cargo.toml b/Cargo.toml index bacb8d4f..e2410e69 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ description = "List system USB buses and devices; a modern and compatible `lsusb repository = "https://github.com/tuna-f1sh/cyme" readme = "README.md" license = "GPL-3.0-or-later" -version = "1.1.3" +version = "1.2.0" edition = "2021" keywords = ["usb", "lsusb", "system_profiler", "macos", "libusb"] categories = ["command-line-utilities"] diff --git a/README.md b/README.md index 7877615e..3e6aaf1d 100644 --- a/README.md +++ b/README.md @@ -25,13 +25,14 @@ The name comes from the technical term for the type of blossom on a Apple tree: # Features * Compatible with `lsusb` using `--lsusb` argument. Supports all arguments including `--verbose` output using libusb. Output is identical for use with no args (list), almost matching for tree (driver port number not included) and near match for verbose. -* Filters like `lsusb` but that also work when printing `--tree`. Adds `--filter_name`, `--filter_serial` and option to hide empty `--hide-buses`/`--hide-hubs`. +* Filters like `lsusb` but that also work when printing `--tree`. Adds `--filter_name`, `--filter_serial`, `filter_class` and option to hide empty `--hide-buses`/`--hide-hubs`. * Improved `--tree` mode; shows device, configurations, interfaces and endpoints as tree depending on level of `--verbose`. * Controllable block data like `lsd --blocks` for device, bus, configurations, interfaces and endpoints. Use `--more` to see more by default. * Modern terminal features with coloured output, utf-8 characters and icon look-up based device data. Can be turned off and customised. * Can be used as a library too with `system_profiler` parsing module, `lsusb` module using libusb and `display` module for printing amongst others. * `--json` output that honours filters and `--tree`. * `--headers` to show meta data only when asked and not take space otherwise. +* `--mask_serials` to either '\*' or randomise serial string for sharing dumps with sensitive serial numbers. * Targets for Linux, macOS, perhaps Windows... ## Demo @@ -87,7 +88,7 @@ For usage as a library for profiling system USB devices, the crate is 100% docum * macOS: "$HOME/Library/Application Support" * Windows: "{FOLDERID_RoamingAppData}" -One can also be supplied with `--config`. Copy or refer to './doc/cyme_example_config.json' for configurables. Supplied args will override these. Use `--debug` to see where it is looking or if it's not loading. +One can also be supplied with `--config`. Copy or refer to './doc/cyme_example_config.json' for configurables. Tthe file is essentially the default args; supplied args will override these. Use `--debug` to see where it is looking or if it's not loading. ### Custom Icons and Colours diff --git a/doc/_cyme b/doc/_cyme index 56c91df5..e867cc1d 100644 --- a/doc/_cyme +++ b/doc/_cyme @@ -23,6 +23,29 @@ _cyme() { '--device=[Selects which device lsusb will examine - supplied as Linux /dev/bus/usb/BBB/DDD style path]:DEVICE: ' \ '--filter-name=[Filter on string contained in name]:FILTER_NAME: ' \ '--filter-serial=[Filter on string contained in serial]:FILTER_SERIAL: ' \ +'--filter-class=[Filter on USB class code]:FILTER_CLASS:((use-interface-descriptor\:"Device class is unspecified, interface descriptors are used to determine needed drivers" +audio\:"Speaker, microphone, sound card, MIDI" +cdc-communications\:"The modern serial interface; appears as a UART/RS232 port on most systems" +hid\:"Human Interface Device; game controllers, keyboards, mice etc. Also commonly used as a device data interface rather then creating something from scratch" +physical\:"Force feedback joystick" +image\:"Scanners, cameras" +printer\:"Laser printer, inkjet printer, CNC machine" +mass-storage\:"Mass storage devices (MSD): USB flash drive, memory card reader, digital audio player, digital camera, external drive" +hub\:"High speed USB hub" +cdc-data\:"Used together with class 02h (Communications and CDC Control) above" +smart-cart\:"USB smart card reader" +content-security\:"Fingerprint reader" +video\:"Webcam" +personal-healthcare\:"Pulse monitor (watch)" +audio-video\:"Webcam, TV" +billboard\:"Describes USB-C alternate modes supported by device" +usb-type-c-bridge\:"An interface to expose and configure the USB Type-C capabilities of Connectors on USB Hubs or Alternate Mode Adapters" +i3c-device\:"An interface to expose and configure I3C function within a USB device to allow interaction between host software and the I3C device, to drive transaction on the I3C bus to/from target devices" +diagnostic\:"Trace and debugging equipment" +wireless-controller\:"Wireless controllers: Bluetooth adaptors, Microsoft RNDIS" +miscellaneous\:"This base class is defined for miscellaneous device definitions. Some matching SubClass and Protocols are defined on the USB-IF website" +application-specific-interface\:"This base class is defined for devices that conform to several class specifications found on the USB-IF website" +vendor-specific-class\:"This base class is defined for vendors to use as they please"))' \ '*-b+[Specify the blocks which will be displayed for each device and in what order]:BLOCKS:((bus-number\:"Number of bus device is attached" device-number\:"Bus issued device number" branch-position\:"Position of device in parent branch" diff --git a/doc/_cyme.ps1 b/doc/_cyme.ps1 index 6dc0728a..fdb41426 100644 --- a/doc/_cyme.ps1 +++ b/doc/_cyme.ps1 @@ -29,6 +29,7 @@ Register-ArgumentCompleter -Native -CommandName 'cyme' -ScriptBlock { [CompletionResult]::new('--device', 'device', [CompletionResultType]::ParameterName, 'Selects which device lsusb will examine - supplied as Linux /dev/bus/usb/BBB/DDD style path') [CompletionResult]::new('--filter-name', 'filter-name', [CompletionResultType]::ParameterName, 'Filter on string contained in name') [CompletionResult]::new('--filter-serial', 'filter-serial', [CompletionResultType]::ParameterName, 'Filter on string contained in serial') + [CompletionResult]::new('--filter-class', 'filter-class', [CompletionResultType]::ParameterName, 'Filter on USB class code') [CompletionResult]::new('-b', 'b', [CompletionResultType]::ParameterName, 'Specify the blocks which will be displayed for each device and in what order') [CompletionResult]::new('--blocks', 'blocks', [CompletionResultType]::ParameterName, 'Specify the blocks which will be displayed for each device and in what order') [CompletionResult]::new('--bus-blocks', 'bus-blocks', [CompletionResultType]::ParameterName, 'Specify the blocks which will be displayed for each bus and in what order') diff --git a/doc/cyme.1 b/doc/cyme.1 index ad0c685f..8fdad5c7 100644 --- a/doc/cyme.1 +++ b/doc/cyme.1 @@ -1,10 +1,10 @@ .ie \n(.g .ds Aq \(aq .el .ds Aq ' -.TH cyme 1 "cyme 1.1.3" +.TH cyme 1 "cyme 1.2.0" .SH NAME cyme \- List system USB buses and devices; a modern and compatible `lsusb` .SH SYNOPSIS -\fBcyme\fR [\fB\-l\fR|\fB\-\-lsusb\fR] [\fB\-t\fR|\fB\-\-tree\fR] [\fB\-d\fR|\fB\-\-vidpid\fR] [\fB\-s\fR|\fB\-\-show\fR] [\fB\-D\fR|\fB\-\-device\fR] [\fB\-\-filter\-name\fR] [\fB\-\-filter\-serial\fR] [\fB\-v\fR|\fB\-\-verbose\fR]... [\fB\-b\fR|\fB\-\-blocks\fR] [\fB\-\-bus\-blocks\fR] [\fB\-\-config\-blocks\fR] [\fB\-\-interface\-blocks\fR] [\fB\-\-endpoint\-blocks\fR] [\fB\-m\fR|\fB\-\-more\fR] [\fB\-\-sort\-devices\fR] [\fB\-\-sort\-buses\fR] [\fB\-\-group\-devices\fR] [\fB\-\-hide\-buses\fR] [\fB\-\-hide\-hubs\fR] [\fB\-\-decimal\fR] [\fB\-\-no\-padding\fR] [\fB\-\-no\-colour\fR] [\fB\-\-ascii\fR] [\fB\-\-headings\fR] [\fB\-\-json\fR] [\fB\-\-from\-json\fR] [\fB\-F\fR|\fB\-\-force\-libusb\fR] [\fB\-c\fR|\fB\-\-config\fR] [\fB\-z\fR|\fB\-\-debug\fR]... [\fB\-\-mask\-serials\fR] [\fB\-h\fR|\fB\-\-help\fR] [\fB\-V\fR|\fB\-\-version\fR] +\fBcyme\fR [\fB\-l\fR|\fB\-\-lsusb\fR] [\fB\-t\fR|\fB\-\-tree\fR] [\fB\-d\fR|\fB\-\-vidpid\fR] [\fB\-s\fR|\fB\-\-show\fR] [\fB\-D\fR|\fB\-\-device\fR] [\fB\-\-filter\-name\fR] [\fB\-\-filter\-serial\fR] [\fB\-\-filter\-class\fR] [\fB\-v\fR|\fB\-\-verbose\fR]... [\fB\-b\fR|\fB\-\-blocks\fR] [\fB\-\-bus\-blocks\fR] [\fB\-\-config\-blocks\fR] [\fB\-\-interface\-blocks\fR] [\fB\-\-endpoint\-blocks\fR] [\fB\-m\fR|\fB\-\-more\fR] [\fB\-\-sort\-devices\fR] [\fB\-\-sort\-buses\fR] [\fB\-\-group\-devices\fR] [\fB\-\-hide\-buses\fR] [\fB\-\-hide\-hubs\fR] [\fB\-\-decimal\fR] [\fB\-\-no\-padding\fR] [\fB\-\-no\-colour\fR] [\fB\-\-ascii\fR] [\fB\-\-headings\fR] [\fB\-\-json\fR] [\fB\-\-from\-json\fR] [\fB\-F\fR|\fB\-\-force\-libusb\fR] [\fB\-c\fR|\fB\-\-config\fR] [\fB\-z\fR|\fB\-\-debug\fR]... [\fB\-\-mask\-serials\fR] [\fB\-h\fR|\fB\-\-help\fR] [\fB\-V\fR|\fB\-\-version\fR] .SH DESCRIPTION List system USB buses and devices; a modern and compatible `lsusb` .SH OPTIONS @@ -30,6 +30,61 @@ Filter on string contained in name \fB\-\-filter\-serial\fR=\fIFILTER_SERIAL\fR Filter on string contained in serial .TP +\fB\-\-filter\-class\fR=\fIFILTER_CLASS\fR +Filter on USB class code +.br + +.br +\fIPossible values:\fR +.RS 14 +.IP \(bu 2 +use\-interface\-descriptor: Device class is unspecified, interface descriptors are used to determine needed drivers +.IP \(bu 2 +audio: Speaker, microphone, sound card, MIDI +.IP \(bu 2 +cdc\-communications: The modern serial interface; appears as a UART/RS232 port on most systems +.IP \(bu 2 +hid: Human Interface Device; game controllers, keyboards, mice etc. Also commonly used as a device data interface rather then creating something from scratch +.IP \(bu 2 +physical: Force feedback joystick +.IP \(bu 2 +image: Scanners, cameras +.IP \(bu 2 +printer: Laser printer, inkjet printer, CNC machine +.IP \(bu 2 +mass\-storage: Mass storage devices (MSD): USB flash drive, memory card reader, digital audio player, digital camera, external drive +.IP \(bu 2 +hub: High speed USB hub +.IP \(bu 2 +cdc\-data: Used together with class 02h (Communications and CDC Control) above +.IP \(bu 2 +smart\-cart: USB smart card reader +.IP \(bu 2 +content\-security: Fingerprint reader +.IP \(bu 2 +video: Webcam +.IP \(bu 2 +personal\-healthcare: Pulse monitor (watch) +.IP \(bu 2 +audio\-video: Webcam, TV +.IP \(bu 2 +billboard: Describes USB\-C alternate modes supported by device +.IP \(bu 2 +usb\-type\-c\-bridge: An interface to expose and configure the USB Type\-C capabilities of Connectors on USB Hubs or Alternate Mode Adapters +.IP \(bu 2 +i3c\-device: An interface to expose and configure I3C function within a USB device to allow interaction between host software and the I3C device, to drive transaction on the I3C bus to/from target devices +.IP \(bu 2 +diagnostic: Trace and debugging equipment +.IP \(bu 2 +wireless\-controller: Wireless controllers: Bluetooth adaptors, Microsoft RNDIS +.IP \(bu 2 +miscellaneous: This base class is defined for miscellaneous device definitions. Some matching SubClass and Protocols are defined on the USB\-IF website +.IP \(bu 2 +application\-specific\-interface: This base class is defined for devices that conform to several class specifications found on the USB\-IF website +.IP \(bu 2 +vendor\-specific\-class: This base class is defined for vendors to use as they please +.RE +.TP \fB\-v\fR, \fB\-\-verbose\fR=\fIVERBOSE\fR Verbosity level: 1 prints device configurations; 2 prints interfaces; 3 prints interface endpoints; 4 prints everything and all blocks .TP @@ -281,6 +336,6 @@ Print help information (use `\-h` for a summary) \fB\-V\fR, \fB\-\-version\fR Print version information .SH VERSION -v1.1.3 +v1.2.0 .SH AUTHORS John Whittington diff --git a/doc/cyme.bash b/doc/cyme.bash index d76c68b7..e406bc37 100644 --- a/doc/cyme.bash +++ b/doc/cyme.bash @@ -19,7 +19,7 @@ _cyme() { case "${cmd}" in cyme) - opts="-l -t -d -s -D -v -b -m -F -c -z -h -V --lsusb --tree --vidpid --show --device --filter-name --filter-serial --verbose --blocks --bus-blocks --config-blocks --interface-blocks --endpoint-blocks --more --sort-devices --sort-buses --group-devices --hide-buses --hide-hubs --decimal --no-padding --no-colour --ascii --headings --json --from-json --force-libusb --config --debug --mask-serials --gen --help --version" + opts="-l -t -d -s -D -v -b -m -F -c -z -h -V --lsusb --tree --vidpid --show --device --filter-name --filter-serial --filter-class --verbose --blocks --bus-blocks --config-blocks --interface-blocks --endpoint-blocks --more --sort-devices --sort-buses --group-devices --hide-buses --hide-hubs --decimal --no-padding --no-colour --ascii --headings --json --from-json --force-libusb --config --debug --mask-serials --gen --help --version" if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -57,6 +57,10 @@ _cyme() { COMPREPLY=($(compgen -f "${cur}")) return 0 ;; + --filter-class) + COMPREPLY=($(compgen -W "use-interface-descriptor audio cdc-communications hid physical image printer mass-storage hub cdc-data smart-cart content-security video personal-healthcare audio-video billboard usb-type-c-bridge i3c-device diagnostic wireless-controller miscellaneous application-specific-interface vendor-specific-class" -- "${cur}")) + return 0 + ;; --blocks) COMPREPLY=($(compgen -W "bus-number device-number branch-position port-path sys-path driver icon vendor-id product-id name manufacturer product-name vendor-name serial speed tree-positions bus-power bus-power-used extra-current-used bcd-device bcd-usb class-code sub-class protocol" -- "${cur}")) return 0 diff --git a/doc/cyme.fish b/doc/cyme.fish index c810c39f..ff3fccfb 100644 --- a/doc/cyme.fish +++ b/doc/cyme.fish @@ -3,6 +3,7 @@ complete -c cyme -s s -l show -d 'Show only devices with specified device and/or complete -c cyme -s D -l device -d 'Selects which device lsusb will examine - supplied as Linux /dev/bus/usb/BBB/DDD style path' -r complete -c cyme -l filter-name -d 'Filter on string contained in name' -r complete -c cyme -l filter-serial -d 'Filter on string contained in serial' -r +complete -c cyme -l filter-class -d 'Filter on USB class code' -r -f -a "{use-interface-descriptor Device class is unspecified\, interface descriptors are used to determine needed drivers,audio Speaker\, microphone\, sound card\, MIDI,cdc-communications The modern serial interface; appears as a UART/RS232 port on most systems,hid Human Interface Device; game controllers\, keyboards\, mice etc. Also commonly used as a device data interface rather then creating something from scratch,physical Force feedback joystick,image Scanners\, cameras,printer Laser printer\, inkjet printer\, CNC machine,mass-storage Mass storage devices (MSD): USB flash drive\, memory card reader\, digital audio player\, digital camera\, external drive,hub High speed USB hub,cdc-data Used together with class 02h (Communications and CDC Control) above,smart-cart USB smart card reader,content-security Fingerprint reader,video Webcam,personal-healthcare Pulse monitor (watch),audio-video Webcam\, TV,billboard Describes USB-C alternate modes supported by device,usb-type-c-bridge An interface to expose and configure the USB Type-C capabilities of Connectors on USB Hubs or Alternate Mode Adapters,i3c-device An interface to expose and configure I3C function within a USB device to allow interaction between host software and the I3C device\, to drive transaction on the I3C bus to/from target devices,diagnostic Trace and debugging equipment,wireless-controller Wireless controllers: Bluetooth adaptors\, Microsoft RNDIS,miscellaneous This base class is defined for miscellaneous device definitions. Some matching SubClass and Protocols are defined on the USB-IF website,application-specific-interface This base class is defined for devices that conform to several class specifications found on the USB-IF website,vendor-specific-class This base class is defined for vendors to use as they please}" complete -c cyme -s b -l blocks -d 'Specify the blocks which will be displayed for each device and in what order' -r -f -a "{bus-number Number of bus device is attached,device-number Bus issued device number,branch-position Position of device in parent branch,port-path Linux style port path,sys-path Linux udev reported syspath,driver Linux udev reported driver loaded for device,icon Icon based on VID/PID,vendor-id Unique vendor identifier - purchased from USB IF,product-id Vendor unique product identifier,name The device name as reported in descriptor or using usb_ids if None,manufacturer The device manufacturer as provided in descriptor or using usb_ids if None,product-name The device product name as reported by usb_ids vidpid lookup,vendor-name The device vendor name as reported by usb_ids vid lookup,serial Device serial string as reported by descriptor,speed Advertised device capable speed,tree-positions Position along all branches back to trunk device,bus-power macOS system_profiler only - actually bus current in mA not power!,bus-power-used macOS system_profiler only - actually bus current used in mA not power!,extra-current-used macOS system_profiler only - actually bus current used in mA not power!,bcd-device The device version,bcd-usb The supported USB version,class-code Class of interface provided by USB IF - only available when using libusb,sub-class Sub-class of interface provided by USB IF - only available when using libusb,protocol Prototol code for interface provided by USB IF - only available when using libusb}" complete -c cyme -l bus-blocks -d 'Specify the blocks which will be displayed for each bus and in what order' -r -f -a "{bus-number System bus number identifier,icon Icon based on VID/PID,name Bus name from descriptor or usb_ids,host-controller Host Controller on macOS\, vendor put here when using libusb,pci-vendor Understood to be vendor ID - it is when using libusb,pci-device Understood to be product ID - it is when using libusb,pci-revision Revsision of hardware,port-path syspath style port path to bus\, applicable to Linux only}" complete -c cyme -l config-blocks -d 'Specify the blocks which will be displayed for each configuration and in what order' -r -f -a "{name Name from string descriptor,number Number of config\, bConfigurationValue; value to set to enable to configuration,num-interfaces Interfaces available for this configuruation,attributes Attributes of configuration\, bmAttributes,icon-attributes Icon representation of bmAttributes,max-power Maximum current consumption in mA}" diff --git a/doc/cyme_example_config.json b/doc/cyme_example_config.json index 16deca06..4437259a 100644 --- a/doc/cyme_example_config.json +++ b/doc/cyme_example_config.json @@ -1,26 +1,26 @@ { "icons": { "user": { - "vid#2e8a": "", "classifier-sub-protocol#15:01:01": "", - "vid-pid-msb#0483:37": "", + "unknown-vendor": "", + "vid-pid#1d50:6018": "", "classifier#02": "", "undefined-classifier": "☶", - "vid-pid#1d50:6018": "", - "unknown-vendor": "", + "vid-pid-msb#0483:37": "", + "vid#2e8a": "", "vid#05ac": "" }, "tree": { - "endpoint_in": "→", - "tree-line": "│ ", - "tree-interface-terminator": "◦", - "tree-edge": "├──", - "tree-device-terminator": "○", "tree-corner": "└──", + "tree-configuration-terminator": "•", "endpoint_out": "←", - "tree-bus-start": "●", + "tree-device-terminator": "○", + "tree-interface-terminator": "◦", + "tree-edge": "├──", + "endpoint_in": "→", "tree-blank": " ", - "tree-configuration-terminator": "•" + "tree-line": "│ ", + "tree-bus-start": "●" } }, "colours": { @@ -86,5 +86,16 @@ "usage-type", "max-packet-size" ], - "mask-serials": null + "mask-serials": null, + "lsusb": false, + "tree": false, + "verbose": 0, + "more": false, + "hide-buses": false, + "hide-hubs": false, + "decimal": false, + "no-padding": false, + "ascii": false, + "headings": false, + "force-libusb": false } \ No newline at end of file diff --git a/src/config.rs b/src/config.rs index 3eeee9c3..8cfd43ad 100644 --- a/src/config.rs +++ b/src/config.rs @@ -35,6 +35,29 @@ pub struct Config { pub endpoint_blocks: Option>, /// Wether to hide device serial numbers by default pub mask_serials: Option, + // non-Options copied from Args + /// Attempt to maintain compatibility with lsusb output + pub lsusb: bool, + /// Dump USB device hierarchy as a tree + pub tree: bool, + /// Verbosity level: 1 prints device configurations; 2 prints interfaces; 3 prints interface endpoints; 4 prints everything and all blocks + pub verbose: u8, + /// Print more blocks by default at each verbosity + pub more: bool, + /// Hide empty buses when printing tree; those with no devices. When listing will hide Linux root_hubs + pub hide_buses: bool, + /// Hide empty hubs when printing tree; those with no devices. When listing will hide hubs regardless of whether empty of not + pub hide_hubs: bool, + /// Show base16 values as base10 decimal instead + pub decimal: bool, + /// Disable padding to align blocks + pub no_padding: bool, + /// Disables icons and utf-8 charactors + pub ascii: bool, + /// Show block headings + pub headings: bool, + /// Force libusb profiler on macOS rather than using/combining system_profiler output + pub force_libusb: bool, } impl Config { diff --git a/src/display.rs b/src/display.rs index a0ce93b3..8cc890f5 100644 --- a/src/display.rs +++ b/src/display.rs @@ -1204,7 +1204,7 @@ impl Block for EndpointBlocks { } /// Value to sort [`USBDevice`] -#[derive(Default, Debug, ValueEnum, Clone, Serialize, Deserialize)] +#[derive(Default, PartialEq, Eq, Debug, ValueEnum, Clone, Serialize, Deserialize)] pub enum Sort { #[default] /// Sort by position in parent branch diff --git a/src/main.rs b/src/main.rs index 7e683028..27017552 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,6 +10,7 @@ use cyme::config::Config; use cyme::display; use cyme::lsusb; use cyme::system_profiler; +use cyme::usb::ClassCode; #[derive(Parser, Debug, Default, Serialize, Deserialize)] #[skip_serializing_none] @@ -43,6 +44,10 @@ struct Args { #[arg(long)] filter_serial: Option, + /// Filter on USB class code + #[arg(long)] + filter_class: Option, + /// Verbosity level: 1 prints device configurations; 2 prints interfaces; 3 prints interface endpoints; 4 prints everything and all blocks #[arg(short = 'v', long, default_value_t = 0, action = clap::ArgAction::Count)] verbose: u8, @@ -161,6 +166,23 @@ macro_rules! wprintln { }; } +/// Merges non-Option Config with passed `Args` +fn merge_config(c: &Config, a: &mut Args) { + a.lsusb |= c.lsusb; + a.tree |= c.tree; + a.more |= c.more; + a.hide_buses |= c.hide_buses; + a.hide_hubs |= c.hide_hubs; + a.decimal |= c.decimal; + a.no_padding |= c.no_padding; + a.ascii |= c.ascii; + a.headings |= c.headings; + a.force_libusb |= c.force_libusb; + if a.verbose == 0 { + a.verbose = c.verbose; + } +} + /// Parse the vidpid filter lsusb format: vid:Option fn parse_vidpid(s: &str) -> Result<(Option, Option), Error> { if s.contains(":") { @@ -358,7 +380,7 @@ fn print_man() -> Result<(), Error> { } fn main() { - let args = Args::parse(); + let mut args = Args::parse(); #[cfg(feature = "cli_generate")] if args.gen { @@ -389,6 +411,7 @@ fn main() { Config::sys() }; + merge_config(&config, &mut args); let colours = if args.no_colour { // set env to be sure too env::set_var("NO_COLOR", "1"); @@ -409,6 +432,7 @@ fn main() { } else if cfg!(target_os = "macos") && !args.force_libusb && args.device.is_none() // device path requires extra + && args.filter_class.is_none() // class filter requires extra && !((args.tree && args.lsusb) || args.verbose > 0 || args.more) { system_profiler::get_spusb().unwrap_or_else(|e| { @@ -437,6 +461,7 @@ fn main() { || args.device.is_some() || args.filter_name.is_some() || args.filter_serial.is_some() + || args.filter_class.is_some() { let mut f = system_profiler::USBFilter::new(); @@ -478,6 +503,7 @@ fn main() { // no need to unwrap as these are Option f.name = args.filter_name; f.serial = args.filter_serial; + f.class = args.filter_class; f.exclude_empty_hub = args.hide_hubs; // exclude root hubs unless dumping a list or json f.no_exclude_root_hub = diff --git a/src/system_profiler.rs b/src/system_profiler.rs index 0bec2143..feda661b 100644 --- a/src/system_profiler.rs +++ b/src/system_profiler.rs @@ -162,10 +162,10 @@ impl USBBus { } } - /// Whether the bus has any empty hubs + /// Whether the bus has just empty hubs pub fn has_empty_hubs(&self) -> bool { match &self.devices { - Some(d) => d.iter().any(|dd| dd.is_hub() && !dd.has_devices()), + Some(d) => d.iter().all(|dd| dd.is_hub() && !dd.has_devices()), None => false, } } @@ -686,11 +686,22 @@ impl USBDevice { /// Does the device have child devices; `devices` is Some and > 0 pub fn has_devices(&self) -> bool { match &self.devices { - Some(d) => d.is_empty(), + Some(d) => d.len() > 0, None => false, } } + /// Does the device have an interface with `class` + pub fn has_interface_class(&self, c: &ClassCode) -> bool { + if let Some(extra) = self.extra.as_ref() { + extra.configurations.iter().any(|conf| { + conf.interfaces.iter().any(|i| i.class == *c) + }) + } else { + false + } + } + /// Gets root_hub [`USBDevice`] if it is one /// /// root_hub returns `Some(Self)` @@ -1152,8 +1163,10 @@ pub struct USBFilter { pub number: Option, /// Retain only devices with name.contains(name) pub name: Option, - /// Retain only devices with serial.contains(serial) + /// retain only devices with serial.contains(serial) pub serial: Option, + /// retain only device of ClassCode class + pub class: Option, /// Exlcude empty hubs in the tree pub exclude_empty_hub: bool, /// Don't exclude Linux root_hub devices - this is inverse because they are pseudo [`USBBus`]'s in the tree @@ -1216,6 +1229,23 @@ pub struct USBFilter { /// assert_eq!(flattened.first().unwrap().name, "Black Magic Probe v1.8.2"); /// ``` /// +/// Filter devices with class +/// +/// ``` +/// use cyme::system_profiler::*; +/// +/// # let mut spusb = read_json_dump(&"./tests/data/cyme_libusb_merge_macos_tree.json").unwrap(); +/// let filter = USBFilter { +/// class: Some(cyme::usb::ClassCode::CDCCommunications), +/// ..Default::default() +/// }; +/// let mut flattened = spusb.flatten_devices(); +/// filter.retain_flattened_devices_ref(&mut flattened); +/// // black magic probe has CDCCommunications serial +/// let device = spusb.get_node(&"20-3.3"); +/// assert_eq!(device.unwrap().name, "Black Magic Probe v1.8.2"); +/// ``` +/// impl USBFilter { /// Creates a new filter with defaults pub fn new() -> Self { @@ -1238,6 +1268,12 @@ impl USBFilter { .as_ref() .map_or(false, |s| s.contains(n.as_str())) })) + && (self.class.as_ref().map_or(true, |fc| { + device + .class + .as_ref() + .map_or(false, |c| c == fc) || device.has_interface_class(fc) + })) && !(self.exclude_empty_hub && device.is_hub() && !device.has_devices()) && (!device.is_root_hub() || self.no_exclude_root_hub) } diff --git a/src/usb.rs b/src/usb.rs index b82b84e8..c12ecf77 100644 --- a/src/usb.rs +++ b/src/usb.rs @@ -10,6 +10,7 @@ use std::fmt; use std::str::FromStr; use std::io; use std::convert::TryFrom; +use clap::ValueEnum; use crate::types::NumericalUnit; @@ -166,7 +167,7 @@ pub enum DescriptorUsage { } /// USB class code defines [ref](https://www.usb.org/defined-class-codes) -#[derive(Debug, Default, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, ValueEnum, Default, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "kebab-case")] pub enum ClassCode { #[default]