From 82fed5e3a74a96027a9847afe0bc9f33cad42356 Mon Sep 17 00:00:00 2001 From: sammypanda <38725335+sammypanda@users.noreply.github.com> Date: Fri, 26 May 2023 13:32:02 +1000 Subject: [PATCH 01/29] added cargo defaults to .gitignore --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index 088ba6b..22d3516 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,8 @@ Cargo.lock # These are backup files generated by rustfmt **/*.rs.bk + + +# Added by cargo + +/target From 97b62a95cab462e1a33dee81a6c0883081f1183d Mon Sep 17 00:00:00 2001 From: sammypanda <38725335+sammypanda@users.noreply.github.com> Date: Fri, 26 May 2023 13:34:38 +1000 Subject: [PATCH 02/29] added Cargo.toml w/ libusb dependency --- Cargo.toml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 Cargo.toml diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..f9f6c4a --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "l_usb_cli" +version = "0.1.0" +edition = "2021" + +[dependencies] +libusb = "0.3.0" From d1051b73448c4b5767e9150a67140f188fcd7628 Mon Sep 17 00:00:00 2001 From: sammypanda <38725335+sammypanda@users.noreply.github.com> Date: Fri, 26 May 2023 13:35:53 +1000 Subject: [PATCH 03/29] added simple logging of visible usb devices getting a feel for the libusb library - descriptive strings print unfinished --- src/main.rs | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 src/main.rs diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..19d1518 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,45 @@ +extern crate libusb; +use std::time::Duration; + +fn main() { + let context = libusb::Context::new().unwrap(); // instantiate a libusb context + + // + // translate devices from and iterate over + // + for device in context.devices().unwrap().iter() { + let device_desc = device.device_descriptor().unwrap(); // translate the device description from + let device_handle; // allows us to access extra deets + + // open device for handle with consideration that it might be empty + match device.open() { + Ok(result) => device_handle = result, + Err(_error) => { + println!("Bus {}, Device {} - unused: no further work :)", + device.bus_number(), + device.address() + ); + continue; + } + } + + let device_languages = device_handle.read_languages( // dependency for reading string descriptors + Duration::new(30, 0) // timeout after 30 seconds, 0 nanoseconds + ).unwrap(); + + for each in &device_languages { + println!("{}", each.lang_id()); + } + + println!("Bus {} Device {} ID {}:{} (n/a)", + device.bus_number(), + device.address(), + device_desc.vendor_id(), + device_desc.product_id(), + // device_handle.read_product_string( + // lang_id, + // device_desc + // ) + ); + } +} \ No newline at end of file From 2c04446a5a45c5594e882ee2a20777906cf03bfb Mon Sep 17 00:00:00 2001 From: sammypanda <38725335+sammypanda@users.noreply.github.com> Date: Fri, 26 May 2023 13:47:06 +1000 Subject: [PATCH 04/29] modified README to represent goal for v1 --- README.md | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7158842..832f6e0 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,15 @@ -# lUSB-cli -linUxSwitchBoard (command-line interface): Virtual switchboard for your USB devices +

lUSB-cli

+

linUxSwitchBoard (command-line interface): Virtual switchboard for your USB devices

+
+

Usage

+ +```shell +lUSB-cli [verb] [device ids] +``` +``` +verbs: + +- list +- disable +- enable +``` \ No newline at end of file From f303a7f107e34e289d5cf5bff93ff9170311cafb Mon Sep 17 00:00:00 2001 From: sammypanda <38725335+sammypanda@users.noreply.github.com> Date: Sat, 27 May 2023 08:47:06 +1000 Subject: [PATCH 05/29] usb open failure: 'unused' static error -> libusb error --- src/main.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index 19d1518..1e3ef80 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,10 +14,11 @@ fn main() { // open device for handle with consideration that it might be empty match device.open() { Ok(result) => device_handle = result, - Err(_error) => { - println!("Bus {}, Device {} - unused: no further work :)", + Err(error) => { + println!("Bus {}, Device {} - {}", device.bus_number(), - device.address() + device.address(), + error ); continue; } From a4f069a9d893b1d48ed9b78fe5de069e7a8534bb Mon Sep 17 00:00:00 2001 From: sammypanda <38725335+sammypanda@users.noreply.github.com> Date: Sat, 27 May 2023 09:51:16 +1000 Subject: [PATCH 06/29] logging visible usb devices: product strings support finished 'descriptive strings print' mentioned in d1051b73448c4b5767e9150a67140f188fcd7628 --- src/main.rs | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/main.rs b/src/main.rs index 1e3ef80..4122669 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,8 @@ extern crate libusb; use std::time::Duration; +const ID_ENGLISH: u16 = 1033; // the language code for US English + fn main() { let context = libusb::Context::new().unwrap(); // instantiate a libusb context @@ -10,6 +12,7 @@ fn main() { for device in context.devices().unwrap().iter() { let device_desc = device.device_descriptor().unwrap(); // translate the device description from let device_handle; // allows us to access extra deets + let device_string; // open device for handle with consideration that it might be empty match device.open() { @@ -26,21 +29,26 @@ fn main() { let device_languages = device_handle.read_languages( // dependency for reading string descriptors Duration::new(30, 0) // timeout after 30 seconds, 0 nanoseconds - ).unwrap(); - - for each in &device_languages { - println!("{}", each.lang_id()); - } + ).unwrap(); - println!("Bus {} Device {} ID {}:{} (n/a)", + device_string = device_languages.iter() // convert Vec to Iter to check if our language exists in + .find(|language| language.lang_id() == ID_ENGLISH) // pass `language` parameter to represent `Some` value and get the `Language` id + .and_then(|language| { // operation to be performed on `Some` value returned by `find()` + device_handle.read_product_string(language.clone(), &device_desc, Duration::new(30, 0)) // get the product string + .map_err(|error| error.to_string()) // if there is an error + .ok() // convert `Result` into `Option` + }) + .unwrap_or_else(|| { // unwrapping `Option` but with an `else` for... + // handling the case when the desired language is not found + String::from("Language not found") + }); + + println!("Bus {} Device {} ID {}:{} ({})", device.bus_number(), device.address(), device_desc.vendor_id(), device_desc.product_id(), - // device_handle.read_product_string( - // lang_id, - // device_desc - // ) + device_string ); } } \ No newline at end of file From 51964e65562bb2ff66c095a45c237d2c36ae2473 Mon Sep 17 00:00:00 2001 From: sammypanda <38725335+sammypanda@users.noreply.github.com> Date: Sat, 27 May 2023 15:08:29 +1000 Subject: [PATCH 07/29] name of binary: 'l_usb_cli' -> 'lusb-cli' --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index f9f6c4a..565b820 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "l_usb_cli" +name = "lusb-cli" version = "0.1.0" edition = "2021" From 25137810c5aa533ee8ea496bc987769c008d6eee Mon Sep 17 00:00:00 2001 From: sammypanda <38725335+sammypanda@users.noreply.github.com> Date: Mon, 26 Jun 2023 16:45:16 +1000 Subject: [PATCH 08/29] swap to rusb crate --- Cargo.toml | 2 +- src/main.rs | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 565b820..721092a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,4 +4,4 @@ version = "0.1.0" edition = "2021" [dependencies] -libusb = "0.3.0" +rusb = "0.9" diff --git a/src/main.rs b/src/main.rs index 4122669..083292f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,15 +1,14 @@ -extern crate libusb; +extern crate rusb; use std::time::Duration; const ID_ENGLISH: u16 = 1033; // the language code for US English fn main() { - let context = libusb::Context::new().unwrap(); // instantiate a libusb context // // translate devices from and iterate over // - for device in context.devices().unwrap().iter() { + for device in rusb::devices().unwrap().iter() { let device_desc = device.device_descriptor().unwrap(); // translate the device description from let device_handle; // allows us to access extra deets let device_string; @@ -51,4 +50,4 @@ fn main() { device_string ); } -} \ No newline at end of file +} From a12123bd57a07547cd67be51360e7dd3ef7391e4 Mon Sep 17 00:00:00 2001 From: sammypanda <38725335+sammypanda@users.noreply.github.com> Date: Mon, 26 Jun 2023 16:56:58 +1000 Subject: [PATCH 09/29] added a basic nix-shell config used with rustup (rootful) --- shell.nix | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 shell.nix diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..f8c0fb2 --- /dev/null +++ b/shell.nix @@ -0,0 +1,9 @@ +with (import {}); + +mkShell { + buildInputs = [ + pkg-config + libusb + ]; +} + From c8eb90d33007dc788e5987efb29960b5e22633c2 Mon Sep 17 00:00:00 2001 From: sammypanda <38725335+sammypanda@users.noreply.github.com> Date: Wed, 5 Jul 2023 10:52:00 +1000 Subject: [PATCH 10/29] refactor: improved hierarchy --- src/cli.rs | 1 + src/cli/cli_devices_list.rs | 52 +++++++++++++++++++++++++++++++++++ src/main.rs | 54 +++---------------------------------- 3 files changed, 56 insertions(+), 51 deletions(-) create mode 100644 src/cli.rs create mode 100644 src/cli/cli_devices_list.rs diff --git a/src/cli.rs b/src/cli.rs new file mode 100644 index 0000000..73e6528 --- /dev/null +++ b/src/cli.rs @@ -0,0 +1 @@ +pub mod cli_devices_list; \ No newline at end of file diff --git a/src/cli/cli_devices_list.rs b/src/cli/cli_devices_list.rs new file mode 100644 index 0000000..fc3ed46 --- /dev/null +++ b/src/cli/cli_devices_list.rs @@ -0,0 +1,52 @@ +extern crate rusb; +use std::time::Duration; + +const ID_ENGLISH: u16 = 1033; // the language code for US English + +// +// translate devices from and iterate over +// +pub fn demo() { + for device in rusb::devices().unwrap().iter() { + let device_desc = device.device_descriptor().unwrap(); // translate the device description from + let device_handle; // allows us to access extra deets + let device_string; + + // open device for handle with consideration that it might be empty + match device.open() { + Ok(result) => device_handle = result, + Err(error) => { + println!("Bus {}, Device {} - {}", + device.bus_number(), + device.address(), + error + ); + continue; + } + } + + let device_languages = device_handle.read_languages( // dependency for reading string descriptors + Duration::new(30, 0) // timeout after 30 seconds, 0 nanoseconds + ).unwrap(); + + device_string = device_languages.iter() // convert Vec to Iter to check if our language exists in + .find(|language| language.lang_id() == ID_ENGLISH) // pass `language` parameter to represent `Some` value and get the `Language` id + .and_then(|language| { // operation to be performed on `Some` value returned by `find()` + device_handle.read_product_string(language.clone(), &device_desc, Duration::new(30, 0)) // get the product string + .map_err(|error| error.to_string()) // if there is an error + .ok() // convert `Result` into `Option` + }) + .unwrap_or_else(|| { // unwrapping `Option` but with an `else` for... + // handling the case when the desired language is not found + String::from("Language not found") + }); + + println!("Bus {} Device {} ID {}:{} ({})", + device.bus_number(), + device.address(), + device_desc.vendor_id(), + device_desc.product_id(), + device_string + ); + } +} diff --git a/src/main.rs b/src/main.rs index 083292f..a049fae 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,53 +1,5 @@ -extern crate rusb; -use std::time::Duration; - -const ID_ENGLISH: u16 = 1033; // the language code for US English +mod cli; fn main() { - - // - // translate devices from and iterate over - // - for device in rusb::devices().unwrap().iter() { - let device_desc = device.device_descriptor().unwrap(); // translate the device description from - let device_handle; // allows us to access extra deets - let device_string; - - // open device for handle with consideration that it might be empty - match device.open() { - Ok(result) => device_handle = result, - Err(error) => { - println!("Bus {}, Device {} - {}", - device.bus_number(), - device.address(), - error - ); - continue; - } - } - - let device_languages = device_handle.read_languages( // dependency for reading string descriptors - Duration::new(30, 0) // timeout after 30 seconds, 0 nanoseconds - ).unwrap(); - - device_string = device_languages.iter() // convert Vec to Iter to check if our language exists in - .find(|language| language.lang_id() == ID_ENGLISH) // pass `language` parameter to represent `Some` value and get the `Language` id - .and_then(|language| { // operation to be performed on `Some` value returned by `find()` - device_handle.read_product_string(language.clone(), &device_desc, Duration::new(30, 0)) // get the product string - .map_err(|error| error.to_string()) // if there is an error - .ok() // convert `Result` into `Option` - }) - .unwrap_or_else(|| { // unwrapping `Option` but with an `else` for... - // handling the case when the desired language is not found - String::from("Language not found") - }); - - println!("Bus {} Device {} ID {}:{} ({})", - device.bus_number(), - device.address(), - device_desc.vendor_id(), - device_desc.product_id(), - device_string - ); - } -} + cli::cli_devices_list::demo() // lists all visible USB devices +} \ No newline at end of file From 380b6c6f732d98c8779f7143301c95bfa6635be8 Mon Sep 17 00:00:00 2001 From: sammypanda <38725335+sammypanda@users.noreply.github.com> Date: Wed, 5 Jul 2023 15:25:06 +1000 Subject: [PATCH 11/29] added cli parsing --- Cargo.toml | 3 +++ src/main.rs | 21 ++++++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 721092a..99abf89 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,10 @@ [package] name = "lusb-cli" +description = "linUxSwitchBoard (command-line interface): Virtual switchboard for your USB devices" +authors = ["sammypanda"] version = "0.1.0" edition = "2021" [dependencies] +clap = "4.3.10" rusb = "0.9" diff --git a/src/main.rs b/src/main.rs index a049fae..1b7d4a5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,24 @@ +use clap::{Command}; + mod cli; fn main() { - cli::cli_devices_list::demo() // lists all visible USB devices + let mut cmd = Command::new(env!("CARGO_PKG_NAME")) + .arg_required_else_help(true) + .version(env!("CARGO_PKG_VERSION", "Version not set")) + .author(env!("CARGO_PKG_AUTHORS")) + .about(env!("CARGO_PKG_DESCRIPTION")) + .subcommand( + Command::new("list") + .about("List all recognised USB devices") + ); + + let matches = cmd.get_matches_mut(); + + match matches.subcommand_name() { + Some("list") => { + cli::cli_devices_list::demo(); + }, + _ => {} // required by 'match' + } } \ No newline at end of file From 922f012812ecce77e9be3b10932a046b74e7f6b9 Mon Sep 17 00:00:00 2001 From: sammypanda <38725335+sammypanda@users.noreply.github.com> Date: Sat, 8 Jul 2023 16:34:59 +1000 Subject: [PATCH 12/29] optimisations + disable verb test mod.rs for /cli instead of cli.rs, super keyword for modules inside cli/, using cli_devices.rs as a proxy to talk to more specific modules in main.rs, functional approach to handling cli parsing --- src/cli.rs | 1 - src/cli/cli_devices.rs | 5 +++++ src/cli/mod.rs | 4 ++++ src/main.rs | 29 ++++++++++++++++++++--------- 4 files changed, 29 insertions(+), 10 deletions(-) delete mode 100644 src/cli.rs create mode 100644 src/cli/cli_devices.rs create mode 100644 src/cli/mod.rs diff --git a/src/cli.rs b/src/cli.rs deleted file mode 100644 index 73e6528..0000000 --- a/src/cli.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod cli_devices_list; \ No newline at end of file diff --git a/src/cli/cli_devices.rs b/src/cli/cli_devices.rs new file mode 100644 index 0000000..6092e2d --- /dev/null +++ b/src/cli/cli_devices.rs @@ -0,0 +1,5 @@ +use super::cli_devices_list; + +pub fn list() { + cli_devices_list::demo(); +} \ No newline at end of file diff --git a/src/cli/mod.rs b/src/cli/mod.rs new file mode 100644 index 0000000..f3b4b77 --- /dev/null +++ b/src/cli/mod.rs @@ -0,0 +1,4 @@ +pub mod cli_devices; +pub mod cli_devices_list; + +// super keyword is used in this codebase to refer up a layer \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 1b7d4a5..4b930dc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,24 +1,35 @@ -use clap::{Command}; +use clap::{Command, Arg}; mod cli; +use cli::cli_devices; // should basically be the proxy for every other *_devices_* module fn main() { - let mut cmd = Command::new(env!("CARGO_PKG_NAME")) + let cmd = Command::new(env!("CARGO_PKG_NAME")) .arg_required_else_help(true) .version(env!("CARGO_PKG_VERSION", "Version not set")) .author(env!("CARGO_PKG_AUTHORS")) .about(env!("CARGO_PKG_DESCRIPTION")) .subcommand( Command::new("list") - .about("List all recognised USB devices") - ); - - let matches = cmd.get_matches_mut(); + .about("List all recognised USB devices")) + .subcommand( + Command::new("disable") + .about("Disable the specified USB devices") + .arg( + Arg::new("identifiers") + .value_name("IDENTIFIERS") + .required(true) + .help("Comma-separated list of identifiers") + ) + ).get_matches(); - match matches.subcommand_name() { - Some("list") => { - cli::cli_devices_list::demo(); + match cmd.subcommand() { + Some(("list", _)) => { + cli_devices::list(); }, + Some(("disable", sub_m)) => { + println!("Devices listed: {}", sub_m.get_one::("identifiers").unwrap()); + } _ => {} // required by 'match' } } \ No newline at end of file From 9ce431dbd2b3086ebc73ee4c212659cfb8a321be Mon Sep 17 00:00:00 2001 From: sammypanda <38725335+sammypanda@users.noreply.github.com> Date: Sun, 9 Jul 2023 19:51:03 +1000 Subject: [PATCH 13/29] created developer 'disable verb' identifier parser would be a good idea to move as much of it as possible out of the main.rs into a dedicated identifier parser, maybe in cli_devices.rs --- src/cli/cli_devices.rs | 48 ++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 19 ++++++++++++++++- 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/src/cli/cli_devices.rs b/src/cli/cli_devices.rs index 6092e2d..d784be2 100644 --- a/src/cli/cli_devices.rs +++ b/src/cli/cli_devices.rs @@ -1,4 +1,52 @@ +extern crate rusb; use super::cli_devices_list; +use std::io::{Error, ErrorKind}; +use rusb::{Context, UsbContext, DeviceHandle}; + +pub struct Device { + index: u8 +} + +impl Device { + pub fn new(index: u8) -> Self { + Self { + index + } + } + + pub fn get_index(&self) -> Result { + Ok(self.index) + } + + pub fn get_device_handle(&self) -> Result, Error> { + let index = self.index; + let context = Context::new().unwrap(); + let devices = context.devices().unwrap(); + + let device = match devices.iter().nth(index as usize) { // handle