-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from sammypanda/dev
lUSB-cli 1.0.0
- Loading branch information
Showing
8 changed files
with
249 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,3 +8,8 @@ Cargo.lock | |
|
||
# These are backup files generated by rustfmt | ||
**/*.rs.bk | ||
|
||
|
||
# Added by cargo | ||
|
||
/target |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +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" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,15 @@ | ||
# lUSB-cli | ||
linUxSwitchBoard (command-line interface): Virtual switchboard for your USB devices | ||
<h1 align="center">lUSB-cli</h1> | ||
<p align="center">linUxSwitchBoard (command-line interface): Virtual switchboard for your USB devices</p> | ||
<br> | ||
<h2>Usage</h2> | ||
|
||
```shell | ||
lUSB-cli [verb] [device ids] | ||
``` | ||
``` | ||
verbs: | ||
- list | ||
- disable | ||
- enable | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
with (import <nixpkgs> {}); | ||
|
||
mkShell { | ||
buildInputs = [ | ||
pkg-config | ||
libusb | ||
]; | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
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<u8, Error> { | ||
Ok(self.index) | ||
} | ||
|
||
pub fn get_device_handle(&self) -> Result<DeviceHandle<Context>, 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 <Option> | ||
Some(device) => device, // pass on the variable | ||
None => { // error case | ||
return Err(Error::new( | ||
ErrorKind::NotFound, | ||
format!("USB with the '{}' identifier was not found", index) | ||
)); | ||
} | ||
}; | ||
|
||
let device_handle = match device.open() { // handle <Result> | ||
Ok(device_handle) => device_handle, // pass on the variable | ||
Err(_) => { // error case | ||
return Err(Error::new( | ||
ErrorKind::Unsupported, | ||
format!("USB device not openable: identifier '{}'", index) | ||
)); | ||
} | ||
}; | ||
|
||
// return device_handle as Result | ||
Ok(device_handle) | ||
} | ||
} | ||
|
||
pub fn list() { | ||
cli_devices_list::demo(); | ||
} | ||
|
||
pub fn handle_verb(verb: &str, device: &Device) { | ||
match device.get_device_handle() { | ||
Ok(mut device_handle) => { | ||
println!("Device: {:?}", device.get_index().unwrap()); | ||
println!( | ||
"device_handle: {}", | ||
device_handle | ||
.device() | ||
.device_descriptor() | ||
.unwrap() | ||
.product_id() | ||
); | ||
|
||
match verb { | ||
"enable" => { | ||
interface_loop(&mut device_handle, true) | ||
} | ||
"disable" => { | ||
interface_loop(&mut device_handle, false) | ||
} | ||
_ => { | ||
panic!("Invalid verb, use disable or enable"); | ||
} | ||
} | ||
}, | ||
Err(error) => { | ||
eprintln!("{error}"); | ||
} | ||
}; | ||
} | ||
|
||
fn interface_loop<T: UsbContext>(device_handle: &mut DeviceHandle<T>, enable: bool) { | ||
device_handle.device().active_config_descriptor().unwrap().interfaces().enumerate().for_each(|(index, interface)| { | ||
let index = index as u8; | ||
|
||
if enable { | ||
device_handle.attach_kernel_driver(index).unwrap_or_else(|error| println!("{error}")); | ||
} else { | ||
device_handle.detach_kernel_driver(index).unwrap_or_else(|error| println!("{error}")); | ||
} | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
extern crate rusb; | ||
use std::time::Duration; | ||
|
||
const ID_ENGLISH: u16 = 1033; // the language code for US English | ||
|
||
// | ||
// translate devices from <Result> and iterate over | ||
// | ||
pub fn demo() { | ||
let mut index = 0; | ||
|
||
for device in rusb::devices().unwrap().iter() { | ||
let device_desc = device.device_descriptor().unwrap(); // translate the device description from <Result> | ||
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!("\u{274C} - Bus {}, Device {}: {}", // 2A2F is a ⨯ (cross product) unicode symbol | ||
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<String>` | ||
}) | ||
.unwrap_or_else(|| { // unwrapping `Option<String>` but with an `else` for... | ||
// handling the case when the desired language is not found | ||
String::from("Language not found") | ||
}); | ||
|
||
println!("{} - Bus {} Device {} ID {}:{} ({})", | ||
index, | ||
device.bus_number(), | ||
device.address(), | ||
device_desc.vendor_id(), | ||
device_desc.product_id(), | ||
device_string | ||
); | ||
|
||
index += 1; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
use clap::{Command, Arg}; | ||
|
||
mod cli; | ||
use cli::cli_devices; // should basically be the proxy for every other *_devices_* module | ||
|
||
// responsible for parsing command inputs and delegating tasks | ||
fn main() { | ||
let identifiers_arg: Arg = Arg::new("identifiers") | ||
.value_name("IDENTIFIERS") | ||
.required(true) | ||
.value_delimiter(',') | ||
.value_parser(clap::value_parser!(u8)) | ||
.help("Comma-separated list of identifiers"); | ||
|
||
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")) | ||
.subcommand( | ||
Command::new("disable") | ||
.about("Disable the specified USB devices") | ||
.arg( | ||
&identifiers_arg | ||
) | ||
) | ||
.subcommand( | ||
Command::new("enable") | ||
.about("Enable the specified USB devices") | ||
.arg( | ||
&identifiers_arg | ||
) | ||
) | ||
.get_matches(); | ||
|
||
match cmd.subcommand() { | ||
Some(("list", _)) => { | ||
cli_devices::list(); | ||
}, | ||
Some((verb, sub_m)) => { | ||
let identifiers = sub_m.get_many::<u8>("identifiers") // we can be sure it exists since `clap` handles parsing | ||
.unwrap_or_else(|| panic!("Comma-separted identifiers not found")); // ..but just in case | ||
|
||
for device in identifiers { | ||
cli_devices::handle_verb(verb, &cli::cli_devices::Device::new(*device)) | ||
}; | ||
}, | ||
_ => {} // required by 'match' | ||
}; | ||
} |