Skip to content

Commit

Permalink
Merge pull request #18 from budde25/udev-refactor
Browse files Browse the repository at this point in the history
Udev refactor
  • Loading branch information
budde25 authored Jul 15, 2024
2 parents 2b8aab4 + c7e97f6 commit 8816a4c
Show file tree
Hide file tree
Showing 34 changed files with 855 additions and 664 deletions.
22 changes: 10 additions & 12 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ path = "src/main.rs"
members = ["crates/tegra-rcm"]

[workspace.dependencies]
clap = { version = "4.5.1", features = ["derive"] }
log = "0.4.21"
clap = { version = "4.5.9", features = ["derive"] }
log = "0.4.22"

# Config for 'cargo dist'
[workspace.metadata.dist]
Expand Down Expand Up @@ -52,21 +52,18 @@ clap = { workspace = true }
clap-verbosity-flag = "2.2"
console = "0.15"
dirs = "5.0"
eframe = { version = "0.26", optional = true }
egui-notify = { version = "0.13", optional = true }
egui_extras = { version = "0.26", features = ["svg"], optional = true }
image = { version = "0.24", optional = true }
eframe = { version = "0.28", optional = true }
egui-notify = { version = "0.15", optional = true }
egui_extras = { version = "0.28", features = ["svg"], optional = true }
image = { version = "0.25", optional = true }
indicatif = "0.17"
log = { workspace = true }
once_cell = "1.19"
rfd = { version = "0.14", optional = true, default-features = false, features = [
"xdg-portal",
"tokio",
] }
rfd = { version = "0.14", optional = true }
tegra-rcm = { version = "0.6", path = "crates/tegra-rcm" }
thiserror = "1.0"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
trash = { version = "3.3", optional = true }
trash = { version = "5.0", optional = true }

[build-dependencies]
clap = { workspace = true }
Expand All @@ -76,7 +73,6 @@ clap_mangen = "0.2"
camino = "1.1"

[features]
default = ["gui"]
gui = [
"dep:egui_extras",
"dep:eframe",
Expand All @@ -85,6 +81,8 @@ gui = [
"dep:egui-notify",
"dep:trash",
]
# Enables the alternative notify hotplug implementation for udev restrcited environments (such a flatpak)
notify = ["tegra-rcm/notify"]

# The profile that 'cargo dist' will build with
[profile.dist]
Expand Down
24 changes: 14 additions & 10 deletions crates/tegra-rcm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,32 @@ readme = "README.md"
categories = ["os", "hardware-support"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[features]
notify = ["dep:notify"]

[dependencies]
cfg-if = "1.0.0"
log = { workspace = true }
thiserror = "1.0.57"
thiserror = "1.0.62"

# Linux and Mac specific
[target.'cfg(any(target_os = "linux", target_os = "macos"))'.dependencies]
rusb = "0.9.3"
rusb = "0.9.4"

# Linux specific
[target.'cfg(target_os = "linux")'.dependencies]
glob = "0.3.1"
nix = { version = "0.28.0", features = ["ioctl"] }
nix = { version = "0.29.0", features = ["ioctl"] }
notify = { version = "6.1.1", optional = true }

# Windows specific
[target.'cfg(target_os = "windows")'.dependencies]
libusbk = { version = "0.1.2", features = ["vendored"] }
libusbk = { version = "0.2.0", features = ["vendored"] }
winapi = { version = "0.3.9", features = ["ioapiset", "errhandlingapi"] }

[dev-dependencies]
criterion = "0.5.1"

[[bench]]
name = "my_benchmark"
harness = false
[lints.rust]
dead_code = "allow"
missing_docs = "deny"
missing_debug_implementations = "deny"
trivial_casts = "deny"
trivial_numeric_casts = "deny"
17 changes: 0 additions & 17 deletions crates/tegra-rcm/benches/my_benchmark.rs

This file was deleted.

13 changes: 9 additions & 4 deletions crates/tegra-rcm/src/device/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,25 @@ cfg_if! {
if #[cfg(any(target_os = "macos", target_os = "linux"))] {
mod unix;
pub use unix::SwitchDevice;
pub use unix::SwitchHandle;
} else if #[cfg(target_os = "windows")] {
mod windows;
pub use windows::SwitchDevice;
pub use windows::SwitchHandle;
} else {
compile_error!("Unsupported OS");
}
}

pub(crate) const SWITCH_VID: u16 = 0x0955;
pub(crate) const SWITCH_PID: u16 = 0x7321;
pub(crate) const RCM_VID: u16 = 0x0955;
pub(crate) const RCM_PID: u16 = 0x7321;

pub(crate) trait Device {
fn find_device() -> Result<Option<SwitchDevice>>;
fn init(&mut self) -> Result<()>;
fn find_device() -> Result<SwitchDevice>;
fn init(&mut self) -> Result<SwitchHandle>;
}

pub(crate) trait DeviceHandle {
fn read(&mut self, buf: &mut [u8]) -> Result<usize>;
fn write(&mut self, buf: &[u8]) -> Result<usize>;
}
58 changes: 34 additions & 24 deletions crates/tegra-rcm/src/device/unix.rs
Original file line number Diff line number Diff line change
@@ -1,38 +1,58 @@
use super::{Device, SWITCH_PID, SWITCH_VID};
use super::{RCM_PID, RCM_VID};

use rusb::{Context, DeviceHandle, UsbContext};
use rusb::{Context, Device, DeviceHandle, UsbContext};
use std::time::Duration;

use crate::vulnerability::Vulnerability;
use crate::Result;

/// A connected and init switch device connection
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct SwitchDevice {
handle: DeviceHandle<Context>,
device: Device<Context>,
}

impl Device for SwitchDevice {
impl super::Device for SwitchDevice {
/// Tries to connect to the device and open and interface
fn find_device() -> Result<Option<Self>> {
fn find_device() -> Result<Self> {
let context = Context::new()?;
for device in context.devices()?.iter() {
let device_desc = device.device_descriptor()?;
let desc = device.device_descriptor()?;

if device_desc.vendor_id() == SWITCH_VID && device_desc.product_id() == SWITCH_PID {
let dev = device.open()?;
return Ok(Some(Self::with_device_handle(dev)));
if desc.vendor_id() == RCM_VID && desc.product_id() == RCM_PID {
return Ok(Self { device });
}
}
// We did not find the device
Ok(None)
Err(crate::SwitchError::SwitchNotFound)
}

/// Init the device
fn init(&mut self) -> Result<()> {
self.handle.claim_interface(0)?;
Ok(())
fn init(&mut self) -> Result<SwitchHandle> {
let handle = self.device.open()?;
handle.claim_interface(0)?;
let switch_handle = SwitchHandle { handle };
switch_handle.validate_environment()?;
Ok(switch_handle)
}
}

impl SwitchDevice {
pub(crate) fn new(device: Device<Context>) -> Self {
let dev = device.device_descriptor().unwrap();
assert_eq!(dev.product_id(), RCM_PID);
assert_eq!(dev.vendor_id(), RCM_VID);
Self { device }
}
}

/// A connected and init switch device connection
#[derive(Debug)]
pub struct SwitchHandle {
pub(crate) handle: DeviceHandle<Context>,
}

impl super::DeviceHandle for SwitchHandle {
/// Read from the device into the buffer
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
let amount = self.handle.read_bulk(0x81, buf, Duration::from_secs(1))?;
Expand All @@ -45,13 +65,3 @@ impl Device for SwitchDevice {
Ok(amount)
}
}

impl SwitchDevice {
pub fn with_device_handle(device: DeviceHandle<Context>) -> Self {
Self { handle: device }
}

pub fn device(&self) -> &DeviceHandle<Context> {
&self.handle
}
}
51 changes: 22 additions & 29 deletions crates/tegra-rcm/src/device/windows.rs
Original file line number Diff line number Diff line change
@@ -1,53 +1,46 @@
use libusbk::{DeviceHandle, DeviceList};
use libusbk::{Device, DeviceHandle, DeviceList};

use super::{Device, SWITCH_PID, SWITCH_VID};
use super::{RCM_PID, RCM_VID};
use crate::Result;

/// A connected and init switch device connection
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct SwitchDevice {
device: DeviceHandle,
pub(crate) device: Device,
}

/// A connected and init switch device connection
#[derive(Debug)]
pub struct SwitchHandle {
pub(crate) handle: DeviceHandle,
}

impl Device for SwitchDevice {
impl super::Device for SwitchDevice {
/// Tries to connect to the device and open and interface
fn find_device() -> Result<Option<Self>> {
fn find_device() -> Result<Self> {
let devices = DeviceList::new()?;
let device = devices.find_with_vid_and_pid(SWITCH_PID as i32, SWITCH_VID as i32);
if let Ok(device) = device {
let handle = device.open()?;
return Ok(Some(Self::with_device_handle(handle)));
}

// We did not find the device
Ok(None)
let device = devices.find_with_vid_and_pid(RCM_PID as i32, RCM_VID as i32)?;
Ok(Self { device })
}

/// Init the device
fn init(&mut self) -> Result<()> {
// stub
Ok(())
fn init(&mut self) -> Result<SwitchHandle> {
Ok(SwitchHandle {
handle: self.device.open()?,
})
}
}

impl super::DeviceHandle for SwitchHandle {
/// Read from the device into the buffer
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
let amount = self.device.read_pipe(0x81, buf)?;
let amount = self.handle.read_pipe(0x81, buf)?;
Ok(amount as usize)
}

/// Write to the device from the buffer
fn write(&mut self, buf: &[u8]) -> Result<usize> {
let amount = self.device.write_pipe(0x01, buf)?;
let amount = self.handle.write_pipe(0x01, buf)?;
Ok(amount as usize)
}
}

impl SwitchDevice {
pub fn with_device_handle(device: DeviceHandle) -> Self {
Self { device }
}

pub fn device(&self) -> &DeviceHandle {
&self.device
}
}
10 changes: 8 additions & 2 deletions crates/tegra-rcm/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,18 @@ pub(crate) type Result<T> = std::result::Result<T, SwitchError>;
#[derive(Error, Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum SwitchError {
/// We expected to get a timeout after smashing the stack but we did not
/// Expected to get a timeout after smashing the stack but we did not
#[error("Expected timeout error after smashing the stack")]
ExpectedError,

/// We cannot find a switch in RCM mode connected
/// Cannot find a switch in RCM mode connected
#[error("Nintendo Switch in RCM mode not found")]
SwitchNotFound,

/// Usb device was not initalized
#[error("Usb device was not initalized")]
NotInit,

/// Unable to claim the Switches interface
#[error("Unable to claim interface: `{0}`")]
UsbBadInterface(u8),
Expand Down Expand Up @@ -96,6 +100,8 @@ impl From<rusb::Error> for SwitchError {
fn from(err: rusb::Error) -> Self {
match err {
rusb::Error::Access => Self::AccessDenied,
rusb::Error::NoDevice => Self::SwitchNotFound,
rusb::Error::NotFound => Self::SwitchNotFound,
_ => Self::Usb(err.to_string()),
}
}
Expand Down
20 changes: 14 additions & 6 deletions crates/tegra-rcm/src/hotplug/mod.rs
Original file line number Diff line number Diff line change
@@ -1,35 +1,43 @@
use std::sync::mpsc::Sender;

use thiserror::Error;

use crate::error::Result;
use crate::Switch;

#[cfg(all(feature = "notify", target_os = "linux"))]
mod notify;

cfg_if::cfg_if! {
if #[cfg(any(target_os = "macos", target_os = "linux"))] {
mod unix;
pub use unix::create_hotplug;
} else if #[cfg(target_os = "windows")] {
#[cfg(target_os = "windows")]
mod windows;
pub use windows::create_hotplug;
} else {
compile_error!("Unsupported OS");
}
}

use crate::Switch;

/// Defines the two actions for when a device is plugged in or removed
pub trait Actions {
/// A switch device has a arrived
fn arrives(&mut self, switch: Switch);
fn arrives(&mut self, switch: Result<Switch>);
/// A switch device has left
fn leaves(&mut self);
}

struct HotplugHandler {
inner: Box<dyn Actions>,
sender: Sender<Result<Switch>>,
callback: Option<Box<dyn Fn() + Send + Sync>>,
}
unsafe impl Send for HotplugHandler {}

#[derive(Debug, Error)]
pub enum HotplugError {
#[error("The hotplug API is not supported on this platform")]
NotSupported,

#[error("A file watcher error occurred")]
Watcher,
}
Loading

0 comments on commit 8816a4c

Please sign in to comment.